Repository: googleapis/genai-toolbox
File: internal/server/mcp.go (Go)

Code before:

	// Set required semantic attributes
	span.SetAttributes(
		attribute.String("mcp.method.name", baseMessage.Method),
		attribute.String("network.transport", networkTransport),
		attribute.String("network.protocol.name", networkProtocolName),
		attribute.String("mcp.transport.type", transportType),
	)

	// Set network protocol version if available
	if networkProtocolVersion != "" {
		span.SetAttributes(attribute.String("network.protocol.version", networkProtocolVersion))
	}

	// Set MCP protocol version if available
	if protocolVersion != "" {
		span.SetAttributes(attribute.String("mcp.protocol.version", protocolVersion))
	}

	// Set request ID
	if baseMessage.Id != nil {
		if idStr, ok := baseMessage.Id.(string); ok {
			span.SetAttributes(attribute.String("jsonrpc.request.id", idStr))
		} else if idNum, ok := baseMessage.Id.(float64); ok {
			span.SetAttributes(attribute.String("jsonrpc.request.id", fmt.Sprintf("%v", idNum)))
		}
	}

	// Set toolset name if available
	if toolsetName != "" {
		span.SetAttributes(attribute.String("toolset.name", toolsetName))
	}

	// Extract tool/prompt name for gen_ai attributes
	switch baseMessage.Method {
	case "tools/call":
		var req struct {
			Params struct {
				Name string `json:"name"`
			} `json:"params"`
		}
		if json.Unmarshal(body, &req) == nil && req.Params.Name != "" {
			span.SetAttributes(
				attribute.String("gen_ai.tool.name", req.Params.Name),
				attribute.String("gen_ai.operation.name", "execute_tool"),
			)
		}
	case "prompts/get":
		var req struct {
			Params struct {
				Name string `json:"name"`

Review comment (suggestion):
```suggestion
		span.SetAttributes(attribute.String("jsonrpc.request.id", fmt.Sprintf("%v", baseMessage.Id)))
```

using `%v` directly should be okay. It should automaticallly work for both types.

Code after:
		if err != nil {
			span.SetStatus(codes.Error, err.Error())
			if rpcErr, ok := result.(jsonrpc.JSONRPCError); ok {
				span.SetAttributes(attribute.String("error.type", rpcErr.Error.String()))
			}
			return "", result, err
		}
		span.SetAttributes(attribute.String("mcp.protocol.version", version))
		return version, result, err
	default:
		toolset, ok := s.ResourceMgr.GetToolset(toolsetName)
		if !ok {
			err := fmt.Errorf("toolset does not exist")
			rpcErr := jsonrpc.NewError(baseMessage.Id, jsonrpc.INVALID_REQUEST, err.Error(), nil)
			span.SetStatus(codes.Error, err.Error())
			span.SetAttributes(attribute.String("error.type", rpcErr.Error.String()))
			return "", rpcErr, err
		}
		promptset, ok := s.ResourceMgr.GetPromptset(promptsetName)
		if !ok {
			err := fmt.Errorf("promptset does not exist")
			rpcErr := jsonrpc.NewError(baseMessage.Id, jsonrpc.INVALID_REQUEST, err.Error(), nil)
			span.SetStatus(codes.Error, err.Error())
			span.SetAttributes(attribute.String("error.type", rpcErr.Error.String()))
			return "", rpcErr, err
		}
		result, err := mcp.ProcessMethod(ctx, protocolVersion, baseMessage.Id, baseMessage.Method, toolset, promptset, s.ResourceMgr, body, header)
		if err != nil {
			span.SetStatus(codes.Error, err.Error())
			// Set error.type based on JSON-RPC error code
			if rpcErr, ok := result.(jsonrpc.JSONRPCError); ok {
				span.SetAttributes(attribute.Int("jsonrpc.error.code", rpcErr.Error.Code))
				span.SetAttributes(attribute.String("error.type", rpcErr.Error.String()))
			}
		}
		return "", result, err
	}
}