mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-24 06:24:18 +02:00
* 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. |
||
|---|---|---|
| .. | ||
| auth | ||
| http | ||
| middleware | ||
| mount | ||
| providers | ||
| sampling | ||
| tasks | ||
| telemetry | ||
| transforms | ||
| versioning | ||
| __init__.py | ||
| test_app_state.py | ||
| test_auth_integration.py | ||
| test_auth_integration_errors.py | ||
| test_cache_hints.py | ||
| test_context.py | ||
| test_dependencies.py | ||
| test_dependencies_advanced.py | ||
| test_event_store.py | ||
| test_fastapi_testclient_compat.py | ||
| test_file_server.py | ||
| test_icons.py | ||
| test_input_validation.py | ||
| test_log_level.py | ||
| test_logging.py | ||
| test_pagination.py | ||
| test_protocol_eras.py | ||
| test_providers.py | ||
| test_server.py | ||
| test_server_docket.py | ||
| test_server_lifespan.py | ||
| test_server_safety.py | ||
| test_session_visibility.py | ||
| test_streamable_http_no_redirect.py | ||
| test_tool_annotations.py | ||
| test_tool_transformation.py | ||
| test_transport.py | ||