fix: remove resource URI from span names to avoid high-cardinality

Per MCP semantic conventions, resource URIs SHOULD NOT be included in
span names by default since they can be unbounded (especially with
templates like users://{id}/profile). The URI remains available via
the mcp.resource.uri attribute.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
strawgate 2026-04-13 21:39:44 -05:00 committed by William Easton
commit 8840779497
No known key found for this signature in database
5 changed files with 11 additions and 11 deletions

View file

@ -205,7 +205,7 @@ class ClientResourcesMixin:
"""
uri_str = str(uri)
with client_span(
f"resources/read {uri_str}",
"resources/read",
"resources/read",
uri_str,
session_id=self.transport.get_session_id(),

View file

@ -238,7 +238,7 @@ class ProxyResource(Resource):
backend_uri = self._backend_uri or str(self.uri)
with client_span(
f"resources/read {backend_uri}",
"resources/read",
"resources/read",
backend_uri,
resource_uri=backend_uri,

View file

@ -1362,7 +1362,7 @@ class FastMCP(
# Core logic: find and read resource (providers queried in parallel)
with server_span(
f"resources/read {uri}",
"resources/read",
"resources/read",
self.name,
"resource",

View file

@ -128,8 +128,8 @@ class TestClientResourceTracing:
spans = trace_exporter.get_finished_spans()
span_names = [s.name for s in spans]
# Client should create "resources/read data://config" span
assert "resources/read data://config" in span_names
# Client should create "resources/read" span (URI in attributes, not name)
assert "resources/read" in span_names
async def test_read_resource_span_attributes(
self, trace_exporter: InMemorySpanExporter
@ -151,7 +151,7 @@ class TestClientResourceTracing:
(
s
for s in spans
if s.name.startswith("resources/read data://")
if s.name == "resources/read"
and s.attributes is not None
and "fastmcp.server.name" not in s.attributes
),
@ -420,7 +420,7 @@ class TestClientErrorTracing:
(
s
for s in spans
if s.name.startswith("resources/read data://fail")
if s.name == "resources/read"
and s.attributes is not None
and "fastmcp.server.name" not in s.attributes
),
@ -431,7 +431,7 @@ class TestClientErrorTracing:
(
s
for s in spans
if s.name.startswith("resources/read data://fail")
if s.name == "resources/read"
and s.attributes is not None
and "fastmcp.server.name" in s.attributes
),

View file

@ -104,7 +104,7 @@ class TestResourceTracing:
assert len(spans) == 1
span = spans[0]
assert span.name == "resources/read config://app"
assert span.name == "resources/read"
assert span.kind == SpanKind.SERVER
assert span.attributes is not None
# Standard MCP semantic conventions
@ -135,7 +135,7 @@ class TestResourceTracing:
assert len(spans) == 1
span = spans[0]
assert span.name == "resources/read users://123/profile"
assert span.name == "resources/read"
assert span.kind == SpanKind.SERVER
assert span.attributes is not None
# Standard MCP semantic conventions
@ -163,7 +163,7 @@ class TestResourceTracing:
assert len(spans) == 1
span = spans[0]
assert span.name == "resources/read nonexistent://resource"
assert span.name == "resources/read"
assert span.status.status_code == StatusCode.ERROR