Skip to content

OTEL: Fix attribute compliance with MCP semantic conventions #3886

@strawgate

Description

@strawgate

Summary

FastMCP's OTEL instrumentation has attribute gaps and non-standard attributes compared to the MCP Semantic Conventions. This issue tracks bringing the existing spans into spec compliance — no new spans, just fixing what we emit.

Missing required/conditionally-required attributes

  • gen_ai.tool.name — Conditionally required on tool operations. Currently not set. Should use tool.name (the public name), not fastmcp.component.key (internal registry key).
  • gen_ai.prompt.name — Conditionally required on prompt operations. Same fix.
  • error.type — Conditionally required on failure. Spec defines: JSON-RPC error code as string, or tool_error when CallToolResult.isError=true. Currently not set anywhere.
  • jsonrpc.request.id — Conditionally required when client executes a request. Not set.
  • rpc.response.status_code — Conditionally required on error responses. The only rpc.* attribute the MCP semconv references. Not set.

Missing recommended attributes

  • mcp.protocol.version — Recommended on both CLIENT and SERVER
  • jsonrpc.protocol.version — Recommended. Should be "2.0"
  • network.transport — Recommended. Should reflect transport type (pipe, tcp, inproc)
  • server.address/server.port — Recommended on CLIENT spans
  • mcp.session.id on CLIENT spans — Currently only set on SERVER

Non-standard attributes to remove

The MCP semconv is standalone under gen-ai/ and explicitly recommends against RPC conventions:

"When instrumenting MCP calls, it's RECOMMENDED to follow MCP conventions instead of RPC semantic conventions"

Additionally, rpc.system was renamed to rpc.system.name and deprecated.

  • Remove rpc.system = "mcp" — deprecated name, not in MCP semconv
  • Remove rpc.service — not in any spec, duplicates fastmcp.server.name
  • Remove rpc.method — redundant with mcp.method.name

Error handling bugs

  • CLIENT span not marked ERROR on tool failureToolError is raised in _parse_call_tool_result() (tools.py:393) after client_span exits (tools.py:162). Span never sees the error.
  • Exception recorded twice on SERVER spanserver_span catches + records, then inner call_tool wraps in ToolError and re-raises, triggering a second recording.
  • No status description — Spec says "status description SHOULD match JSONRPCError.message". Currently never set.

Methodology

Tested with console OTEL exporter (no vendor SDK) to capture raw spans. Full audit: https://github.com/strawgate/fastmcp-pr-review/blob/main/otel-audit.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing functionality. For issues and smaller PR improvements.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions