mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
* fix(otel): name the RPC system and upstream on MCP tool-call spans An MCP tool-call span carried only gen_ai.*, mcp.* and litellm.* attributes. A CLIENT span holding none of the http/db/messaging/rpc families is unclassifiable, so Elastic APM indexed these spans as span.type=unknown with no span.subtype at all, and its span-links API then rejected the whole trace with "Missing required fields (span.subtype)". MCP frames every message as JSON-RPC 2.0, so the tool-call span now names rpc.system. It names server.address and server.port alongside it, derived from the already-redacted mcp_server_resource origin: naming the RPC system makes a consumer treat the span as a downstream dependency and key that dependency off the server address, so emitting one without the other labels the dependency ":0". The tools/list span is left alone. It reaches the callbacks with no upstream identity, and a listing can span several upstreams, so it has no address to attach and would produce exactly that ":0" node. The wire is untouched: streamable MCP still returns HTTP 200 with isError: true. * fix(otel): drop rpc.system when no MCP upstream address resolved server.address and server.port come from mcp_server_resource, which is absent whenever the tool name resolves to no registered server, is None for a stdio transport that has no host to log, and parses to no host for an IPv6 origin the redactor rebuilds without its brackets. rpc.system was stamped unconditionally, so each of those paths emitted it alone and named the dependency ":0", the outcome the address pair exists to prevent. Gating the system attribute on a resolved address makes the pairing structural rather than leaving it to the two extractors happening to agree. * fix(otel): require a full MCP destination before naming the RPC system The gate gave rpc.system a resolved address, but not a resolved port. A host-bearing scheme outside the HTTP(S) default-port map resolves an address alone, and mcp_servers[].url is not scheme-validated, so an origin like mcp://host or ws://host reaches the mapper and names the dependency host:0 instead of the :0 the previous commit removed. Gating on the complete pair closes it, and covers a port of 0 as well. _upstream_address_port also gets a direct contract test, including the IPv6 origin the redactor rebuilds without brackets. * fix(otel): do not raise when an MCP origin has an unparseable port _redact_mcp_resource_url rebuilds the origin without its IPv6 brackets, so a zone-scoped address leaves a truthy hostname behind that the host check admits: http://[fe80::1%25eth0]:80 becomes http://fe80::1%25eth0:80, whose hostname is fe80 and whose port raises ValueError. That propagated out of MCPToolCallSpanData.from_standard_logging_payload and cost the span. Reading both halves inside a guard degrades an unparseable origin to no address, which is already how the mapper treats an unresolvable upstream, and matches the guard the redactor puts around the same split. The scheme default port drops the dict literal so the LIT002 ceiling stays put. |
||
|---|---|---|
| .. | ||
| arize | ||
| azure_storage | ||
| bitbucket | ||
| cloudzero | ||
| code_interpreter_interception | ||
| compression_interception | ||
| datadog | ||
| dotprompt | ||
| focus | ||
| gcs_bucket | ||
| gcs_pubsub | ||
| gitlab | ||
| langfuse | ||
| levo | ||
| litellm_agent | ||
| mavvrik_focus | ||
| newrelic | ||
| open_telemetry | ||
| opik | ||
| otel | ||
| SlackAlerting | ||
| websearch_interception | ||
| rubrik_test_helpers.py | ||
| test_agentops.py | ||
| test_anthropic_cache_control_hook.py | ||
| test_athina.py | ||
| test_azure_sentinel.py | ||
| test_braintrust_logging.py | ||
| test_braintrust_span_name.py | ||
| test_custom_guardrail.py | ||
| test_custom_guardrail_recursion.py | ||
| test_custom_prompt_management.py | ||
| test_deepeval.py | ||
| test_galileo.py | ||
| test_guardrail_logging_sync.py | ||
| test_helicone.py | ||
| test_langfuse.py | ||
| test_langfuse_otel.py | ||
| test_langsmith_init.py | ||
| test_lunary.py | ||
| test_mlflow.py | ||
| test_openmeter.py | ||
| test_opentelemetry.py | ||
| test_opentelemetry_dynamic_imports.py | ||
| test_opik_utils.py | ||
| test_otel_guardrail_violation_spans.py | ||
| test_otel_team_attributes_matrix.py | ||
| test_prometheus_api_promql_escape.py | ||
| test_prometheus_budget_metric_guard.py | ||
| test_prometheus_budget_metrics_db_lookups.py | ||
| test_prometheus_budget_metrics_timeout.py | ||
| test_prometheus_cache_metrics.py | ||
| test_prometheus_client_ip_user_agent.py | ||
| test_prometheus_custom_metadata_label_counts.py | ||
| test_prometheus_end_user_cardinality.py | ||
| test_prometheus_invalid_key_filtering.py | ||
| test_prometheus_labels.py | ||
| test_prometheus_mcp_tool_metrics.py | ||
| test_prometheus_media_generation_metrics.py | ||
| test_prometheus_metric_name_consistency.py | ||
| test_prometheus_missing_metrics.py | ||
| test_prometheus_none_metadata.py | ||
| test_prometheus_overhead_with_guardrails.py | ||
| test_prometheus_queue_guardrail_metrics.py | ||
| test_prometheus_rate_limit_labels.py | ||
| test_prometheus_remaining_tokens_router_fallback.py | ||
| test_prometheus_service_tier_label.py | ||
| test_prometheus_services.py | ||
| test_prometheus_spend_logs_metadata.py | ||
| test_prometheus_stream_label.py | ||
| test_prometheus_token_detail_metrics.py | ||
| test_prometheus_user_team_metrics.py | ||
| test_prompt_manager_ssti.py | ||
| test_responses_background_cost.py | ||
| test_rubrik.py | ||
| test_s3.py | ||
| test_s3_v2.py | ||
| test_weave_otel.py | ||