Commit graph

18 commits

Author SHA1 Message Date
Bill Easton
75b9f92504
feat: Add telemetry interop mode for FastMCP (#4046)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-07-27 16:05:29 -04:00
Jeremiah Lowin
dec25ba6be
Merge remote-tracking branch 'origin/main' into r4648
# Conflicts:
#	tests/conformance/expected-failures.yml
2026-07-26 17:40:14 -04:00
Jeremiah Lowin
5745323ecd
Cover trace propagation through the modern proxy relay 2026-07-26 17:15:30 -04:00
Jeremiah Lowin
e056a3946e
Remove server-initiated sampling and roots from the server API
Deletes fastmcp/server/sampling/, Context.sample/sample_step/list_roots, and
FastMCP(sampling_handler=). The proxy's handshake-era relay now reaches the
front session through the SDK directly.
2026-07-26 14:34:21 -04:00
Jeremiah Lowin
de14ed1b7c
Audit mode="legacy" pins in server tests and top-level stragglers
Removes pins added while making the auto-default suite pass that weren't
actually testing older-protocol-only behavior, and keeps (with a stated
reason) the ones that are. Along the way, fixes two real defects the audit
surfaced in the modern protocol path: PingMiddleware could leak a
_active_sessions entry when a connection's exit_stack closed before its
keepalive task got its first scheduler turn, and FastMCP(experimental_
capabilities=...) was silently dropped from server/discover responses
(it only ever reached the legacy initialize handshake).
2026-07-20 16:00:27 -04:00
Jeremiah Lowin
d2ac7ed3d2
Default fastmcp.Client to mode="auto"; surface extensions=/result_claims=
Client negotiates the newest mutual protocol era by default (probe
server/discover, fall back to the initialize handshake). ProxyClient and the
inspect utility explicitly pin the handshake era so proxy forwarding and
server_info reads are unchanged. SSE and multi-server config transports are
legacy-only. extensions= and result_claims= (SEP-2133) are thin passthroughs
to the SDK session.
2026-07-19 21:22:49 -04:00
Jeremiah Lowin
252a29e5e6
Preserve telemetry attributes when a sampler does not forward them (#4539)
* Reapply span attributes after creation to survive non-forwarding samplers

Tracer.start_span builds the span from sampling_result.attributes, not
the attributes kwarg — a custom Sampler that returns
SamplingResult(RECORD_AND_SAMPLE) without forwarding attributes
silently drops everything FastMCP passed at creation time. Reapply the
same attributes immediately after span creation (guarded by
is_recording()) so on_start hooks and samplers still see them, while
the finished span is guaranteed to carry FastMCP's telemetry
regardless of sampler behavior.

* Restore only missing span attributes, not a blanket reapply

Reapplying all attributes after span creation overwrote values a
sampler deliberately set (e.g. a redacted mcp.method.name) and
inflated dropped-attribute counts when the SDK's attribute limit was
hit. Compare against the span's existing attributes and restore only
the keys a non-forwarding sampler actually dropped, via a shared
restore_missing_attributes() helper in fastmcp.telemetry.

* Gate attribute restore on all-or-nothing, not per-key

Restoring only missing keys reinserted attributes the SDK's bounded
attribute map had already evicted under a low
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, evicting a different retained key and
inflating dropped_attributes beyond what the sampler actually dropped.
Gate on none of our attributes being present (plus dropped_attributes
== 0) instead — the regression this exists to fix is a sampler
dropping everything, and eviction under a limit always leaves some.
Renamed restore_missing_attributes to restore_dropped_attributes to
match.

* Gate attribute restore on empty span, not per-key presence

A sampler that intentionally supplies only its own attributes (e.g. to
strip component names or resource URIs for privacy/cardinality
control) left none of FastMCP's keys on the span, so the previous
all-or-nothing gate treated it identically to a bare non-forwarding
sampler and restored everything, defeating the filter. Key off the
span having no attributes at all instead — a bare sampler leaves it
empty, a filtering sampler doesn't.
2026-07-19 09:00:04 -04:00
nate nowack
f018f68bbf
Expose telemetry attributes on span start (#4487)
* Expose telemetry attributes on span start

🤖 Generated with Codex

* Expose sampling attributes on span start

🤖 Generated with Codex

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-07-18 19:46:19 -04:00
Jeremiah Lowin
a04f6fd911
Add telemetry off-switch and mcp.protocol.version span attribute (#4481)
* Turn OpenTelemetry instrumentation on by default with explicit off-switch

Add FASTMCP_ENABLE_TELEMETRY setting (default true) and mcp.protocol.version
span attribute for SDK parity.

* Make disabled telemetry a transparent pass-through, not a NoOpTracer

The stock NoOpTracer.start_as_current_span attaches a NonRecordingSpan, hijacking the current OTel context from any enclosing application span. When telemetry is disabled, get_tracer() now returns a non-attaching pass-through tracer so trace.get_current_span() inside handlers still resolves to the caller's span.
2026-07-17 17:02:48 -04:00
Jeremiah Lowin
ac78e6f693
Emit one SERVER span per request and adopt spec-correct error codes (#4445) 2026-07-07 07:49:06 -04:00
Jeremiah Lowin
3522a98766
Migrate to MCP Python SDK v2 (#4437) 2026-07-06 17:36:45 -04:00
Bill Easton
2d61d8a46b
fix: add OTEL spans to sampling step and tool execution (#4059)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 09:38:58 -04:00
Bill Easton
39b421a464
OTEL: Instrument all MCP list operations and enrich delegate spans (#3890)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:45:50 -04:00
Bill Easton
7184a4ca21
OTEL: Fix attribute compliance with MCP semantic conventions (#3889)
* OTEL: Fix attribute compliance and improve telemetry helpers

Attribute compliance:
- Remove rpc.system/service/method (MCP is not traditional RPC)
- Add gen_ai.tool.name on tools/call spans
- Add gen_ai.prompt.name on prompts/get spans
- Fix session_id check (truthy -> is not None)

Telemetry helper improvements:
- Add is_recording() guards to skip work on non-recording spans
- Add error.type attribute with __qualname__ on error spans
- Use isinstance check for ToolError to set "tool_error" error type
- Include exception message in span status description
- Add tool_name/prompt_name params to server_span and client_span

Client call_tool enrichment:
- Reflect tool-level errors (result.isError) on client span status
  so callers see ERROR even though the MCP protocol call succeeded

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 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>

* fix: add missing gen_ai/mcp attributes to proxy and delegate spans

- Proxy tool spans: add gen_ai.tool.name
- Proxy prompt spans: add gen_ai.prompt.name
- All delegate spans: add mcp.method.name
- Docs: remove rpc.* references, update span names and attributes table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Hoist ToolError imports to module level, add rpc.* migration note

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:28:47 -04:00
Jeremiah Lowin
564b4c0d5c
Fix ty 0.0.17 diagnostics and bump lockfile 2026-02-16 16:13:38 -05:00
Jeremiah Lowin
a6cd764b5f
Add component versioning and VersionFilter transform (#2894) 2026-01-16 20:53:18 -05:00
Chris Guidry
eaf2906916 Adopt OpenTelemetry MCP semantic conventions
Updates FastMCP's telemetry to align with the new MCP semantic conventions
from open-telemetry/semantic-conventions#2083. This gives us interoperability
with other MCP implementations while keeping fastmcp.* attributes for things
unique to our framework.

Changes:
- Span names now follow `{method} {target}` format (e.g., `tools/call greet`)
- Added `mcp.method.name` and `mcp.resource.uri` attributes
- Renamed `fastmcp.session.id` to standard `mcp.session.id`
- Kept fastmcp.* attributes for server name, component info, provider details

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 14:08:15 -05:00
Chris Guidry
046f845ddf Add OpenTelemetry tracing support
Adds opt-in distributed tracing via OpenTelemetry for observability into
FastMCP server and client operations.

Server spans are created for tool calls, resource reads, and prompt
renders with attributes like component key, component type, provider
type, session ID, and auth context. Client spans wrap outgoing calls
with trace context propagation via W3C headers in request meta.

Components provide their own span attributes through a `get_span_attributes()`
method that subclasses override - this lets LocalProvider, FastMCPProvider,
and ProxyProvider each include relevant context (original names, backend URIs).

To enable: configure an OpenTelemetry SDK with a TracerProvider before
importing fastmcp. Traces export to any OTLP-compatible backend.

Closes ENG-2813

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 09:45:41 -05:00