MCPcopy Create free account
hub / github.com/authorizerdev/authorizer / registerTool

Method registerTool

internal/mcp/server.go:164–211  ·  view source on GitHub ↗

registerTool wires one ToolBinding into the MCP server. The handler: 1. Constructs a fresh proto.Message of the right type via dynamicpb 2. Unmarshals JSON args into it 3. Invokes the gRPC method via grpc.ClientConn.Invoke (with bearer) 4. Marshals the response back to JSON for the MCP client

(b ToolBinding)

Source from the content-addressed store, hash-verified

162// 3. Invokes the gRPC method via grpc.ClientConn.Invoke (with bearer)
163// 4. Marshals the response back to JSON for the MCP client
164func (s *Server) registerTool(b ToolBinding) {
165 schema := schemaForMessage(b.InputDescriptor)
166 tool := &mcp.Tool{
167 Name: b.Name,
168 Description: b.Description,
169 InputSchema: schema,
170 }
171 if b.Destructive {
172 // MCP clients show a destructive-action confirmation when this is set.
173 tool.Annotations = &mcp.ToolAnnotations{DestructiveHint: ptrTrue()}
174 }
175
176 s.mcpSrv.AddTool(tool, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
177 // Build a dynamic proto.Message for the request, then unmarshal JSON.
178 reqMsg := dynamicpb.NewMessage(b.InputDescriptor)
179 if len(req.Params.Arguments) > 0 && !isJSONNull(req.Params.Arguments) {
180 if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(req.Params.Arguments, reqMsg); err != nil {
181 // Argument decode failures surface as tool errors (not
182 // protocol errors) so the LLM gets actionable text.
183 return errorResult("invalid arguments: " + err.Error()), nil
184 }
185 }
186
187 respMsg := dynamicpb.NewMessage(b.OutputDescriptor)
188 if err := s.gwConn.Invoke(s.stampAuth(ctx), b.FullMethod, reqMsg, respMsg); err != nil {
189 s.log.Debug().Err(err).Str("tool", b.Name).Str("method", b.FullMethod).Msg("MCP tool invocation failed")
190 // gRPC errors (Unimplemented, PermissionDenied, NotFound, ...)
191 // become CallToolResult{IsError: true} with the gRPC status
192 // message as the content. The MCP host shows this to the LLM
193 // in a way that lets it react / try a different tool, rather
194 // than a low-level JSON-RPC failure that would just abort.
195 return errorResult(err.Error()), nil
196 }
197
198 respJSON, err := (protojson.MarshalOptions{UseProtoNames: true, EmitUnpopulated: true}).Marshal(respMsg)
199 if err != nil {
200 return errorResult("encode response: " + err.Error()), nil
201 }
202 // Surface as both Content (text-shaped) and StructuredContent so MCP
203 // clients that prefer either get something they can consume.
204 var structured any
205 _ = json.Unmarshal(respJSON, &structured)
206 return &mcp.CallToolResult{
207 Content: []mcp.Content{&mcp.TextContent{Text: string(respJSON)}},
208 StructuredContent: structured,
209 }, nil
210 })
211}
212
213func ptrTrue() *bool { v := true; return &v }
214

Callers 1

NewFunction · 0.95

Calls 6

stampAuthMethod · 0.95
schemaForMessageFunction · 0.85
ptrTrueFunction · 0.85
isJSONNullFunction · 0.85
errorResultFunction · 0.85
ErrorMethod · 0.80

Tested by

no test coverage detected