Skip to content

fix: align MCP span naming and metric buckets with OTel MCP semantic conventions#268

Merged
adityamehra merged 3 commits intomainfrom
fix/mcp-semconv-span-naming
Apr 16, 2026
Merged

fix: align MCP span naming and metric buckets with OTel MCP semantic conventions#268
adityamehra merged 3 commits intomainfrom
fix/mcp-semconv-span-naming

Conversation

@adityamehra
Copy link
Copy Markdown
Contributor

@adityamehra adityamehra commented Apr 13, 2026

Summary

  • MCP span naming: MCPToolCall spans now use {mcp.method.name} {tool_name} format (e.g. tools/call add) instead of execute_tool {tool_name}, matching the OTel MCP semantic conventions
  • SpanKind: CLIENT for client-side, SERVER for server-side (was INTERNAL)
  • Metric buckets: All MCP duration histograms now use semconv-specified bucket boundaries [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30, 60, 120, 300]

Sample span output

From dev_assistant_client.py --console (client-side call_tool span):

{
    "name": "tools/call get_system_info",
    "kind": "SpanKind.CLIENT",
    "parent_id": "0x9b8290b6d8c0f51b",
    "status": { "status_code": "UNSET" },
    "attributes": {
        "gen_ai.operation.name": "execute_tool",
        "gen_ai.provider.name": "mcp",
        "gen_ai.agent.name": "mcp.client",
        "gen_ai.tool.name": "get_system_info",
        "gen_ai.tool.call.id": "312fadba-c1b5-46e5-b984-10440d7ffd6f",
        "gen_ai.tool.type": "extension",
        "mcp.method.name": "tools/call",
        "network.transport": "pipe"
    }
}

Before (old span naming):

name: "execute_tool get_system_info"
kind: SpanKind.INTERNAL

After (MCP semconv aligned):

name: "tools/call get_system_info"
kind: SpanKind.CLIENT  (or SERVER for server-side)

Test plan

  • make lint passes (no new issues in changed files)
  • pytest util/opentelemetry-util-genai/tests/ -- 181 passed
  • pytest instrumentation-genai/opentelemetry-instrumentation-fastmcp/tests/ -- 74 passed
  • dev_assistant_client.py --console smoke test confirms tools/call {name} spans with SpanKind.CLIENT
  • 8 new unit tests for MCPToolCall span naming, SpanKind, and semconv attributes
  • Plain ToolCall regression test (still execute_tool {name} / INTERNAL)

…conventions

MCPToolCall spans now use {mcp.method.name} {tool_name} format (e.g.
"tools/call add") with CLIENT/SERVER SpanKind, matching the OTel MCP
semconv spec. Previously used "execute_tool {tool_name}" with INTERNAL.

Added explicit bucket boundaries [0.01..300] to all MCP duration
histograms per semconv specification.

Made-with: Cursor
@@ -783,8 +784,14 @@ def _start_tool_call(self, tool: ToolCall) -> None:
Span name: execute_tool {gen_ai.tool.name}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be clearer to mention this span name only applies to non MCP tool calls

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls see below lines for the comment -

MCPToolCall instances are dispatched to _start_mcp_tool_call for
        MCP semconv span naming ({mcp.method.name} {target}) and SpanKind.

Copy link
Copy Markdown
Contributor

@wrisa wrisa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a comment.

"""
# Span name per semconv: "execute_tool {gen_ai.tool.name}"
if isinstance(tool, MCPToolCall):
self._start_mcp_tool_call(tool)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why duplicate logic for span creation in this method ? Can we just have separate logic for name and kind here for mcp tools?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to explicitly mention it to the coding agent to follow DRY. I will clean it in the next PR

@adityamehra adityamehra marked this pull request as draft April 16, 2026 00:15
@adityamehra adityamehra marked this pull request as ready for review April 16, 2026 16:29
Resolve conflict in instruments.py: keep both MCP operation duration
buckets (from this branch) and evaluation cost buckets (from main).

Made-with: Cursor
…naming

Made-with: Cursor

# Conflicts:
#	util/opentelemetry-util-genai/src/opentelemetry/util/genai/instruments.py
@adityamehra adityamehra merged commit d325175 into main Apr 16, 2026
14 checks passed
@adityamehra adityamehra deleted the fix/mcp-semconv-span-naming branch April 16, 2026 16:48
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants