Skip to content

feat(prometheus): expose MCP tool metadata in Prometheus metrics - #31899

Merged
yassin-berriai merged 1 commit into
litellm_internal_stagingfrom
litellm_prometheus-mcp-tool-metadata
Jul 2, 2026
Merged

feat(prometheus): expose MCP tool metadata in Prometheus metrics#31899
yassin-berriai merged 1 commit into
litellm_internal_stagingfrom
litellm_prometheus-mcp-tool-metadata

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Resolves LIT-3765

Linear ticket

Resolves LIT-3765

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Ran a live proxy with the prometheus callback and a real MCP server (deepwiki), config:

mcp_servers:
  deepwiki:
    transport: "http"
    url: "https://mcp.deepwiki.com/mcp"
    mcp_info:
      mcp_server_cost_info:
        default_cost_per_query: 0.01

litellm_settings:
  callbacks: ["prometheus"]

general_settings:
  master_key: sk-mcp31899

Made real MCP tool calls through the proxy's /mcp endpoint:

# initialize (capture mcp-session-id response header into $SID)
curl -s -D - http://127.0.0.1:24899/mcp/ \
  -H "Authorization: Bearer sk-mcp31899" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

# call two real deepwiki tools
for CALL in \
  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"deepwiki-ask_question","arguments":{"repoName":"BerriAI/litellm","question":"What does the litellm proxy do?"}}}' \
  '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"deepwiki-read_wiki_structure","arguments":{"repoName":"BerriAI/litellm"}}}'; do
  curl -s http://127.0.0.1:24899/mcp/ \
    -H "Authorization: Bearer sk-mcp31899" -H "mcp-session-id: $SID" \
    -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
    -d "$CALL"
done

curl -sL -H "Authorization: Bearer sk-mcp31899" http://127.0.0.1:24899/metrics/ | grep -E "^litellm_mcp_tool_(calls_total|call_spend_metric_total)"

Output, both counters increment per tool with mcp_server_name and mcp_tool_name labels:

litellm_mcp_tool_calls_total{api_key_alias="None",end_user="None",hashed_api_key="litellm_proxy_master_key",mcp_server_name="deepwiki",mcp_tool_name="ask_question",team="None",team_alias="None",user="default_user_id"} 1.0
litellm_mcp_tool_calls_total{api_key_alias="None",end_user="None",hashed_api_key="litellm_proxy_master_key",mcp_server_name="deepwiki",mcp_tool_name="read_wiki_structure",team="None",team_alias="None",user="default_user_id"} 1.0
litellm_mcp_tool_call_spend_metric_total{api_key_alias="None",end_user="None",hashed_api_key="litellm_proxy_master_key",mcp_server_name="deepwiki",mcp_tool_name="ask_question",team="None",team_alias="None",user="default_user_id"} 0.01
litellm_mcp_tool_call_spend_metric_total{api_key_alias="None",end_user="None",hashed_api_key="litellm_proxy_master_key",mcp_server_name="deepwiki",mcp_tool_name="read_wiki_structure",team="None",team_alias="None",user="default_user_id"} 0.01

Devin e2e run:

E2E demo recording

Type

🆕 New Feature

Changes

Two new Prometheus counters that fire when mcp_tool_call_metadata is present in the standard logging payload:

  • litellm_mcp_tool_calls_total counts MCP tool calls
  • litellm_mcp_tool_call_spend_metric tracks spend on MCP tool calls

Both carry mcp_tool_name and mcp_server_name labels alongside the standard key/team/user labels, enabling Grafana dashboards to break down MCP usage by server and tool

litellm/types/integrations/prometheus.py adds MCP_TOOL_NAME and MCP_SERVER_NAME to UserAPIKeyLabelNames, registers both metric names in DEFINED_PROMETHEUS_METRICS, defines their label sets in PrometheusMetricLabels (each metric gets its own annotated list so the two class attributes do not share one mutable object), and adds the two optional fields to UserAPIKeyLabelValues

litellm/integrations/prometheus.py creates the two Counter metrics in __init__, adds _increment_mcp_tool_call_metrics() which extracts mcp_tool_call_metadata from the payload metadata (with dict guards on both the metadata and the MCP entry) and increments the counters with a dedicated UserAPIKeyLabelValues carrying the MCP-specific fields, and calls it from async_log_success_event

The method builds a separate UserAPIKeyLabelValues for MCP metrics (with mcp_tool_name/mcp_server_name populated) rather than adding those fields to the main enum_values, since MCP labels only apply to these two metrics and shouldn't pollute label sets for the 30+ other counters

Link to Devin session: https://app.devin.ai/sessions/8ad6e7828a5149d0af0e4d148d7d8bcc

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai review

@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Exposes MCP tool call metadata in two new Prometheus counters — litellm_mcp_tool_calls_total and litellm_mcp_tool_call_spend_metric_total — so Grafana dashboards can slice MCP usage by mcp_server_name and mcp_tool_name alongside the standard key/team/user label set.

  • _increment_mcp_tool_call_metrics builds a separate UserAPIKeyLabelValues/PrometheusLabelFactoryContext for the MCP labels, keeping those labels out of the 30+ existing counters; both isinstance guards (for outer metadata and the nested MCP dict) prevent AttributeError on unexpected payload shapes.
  • PrometheusMetricLabels registers the two label lists as independent objects (list(...) copy for the spend metric), UserAPIKeyLabelNames gains two new enum members, and UserAPIKeyLabelValues gets the optional mcp_tool_name/mcp_server_name fields.
  • The accompanying test file uses only mocks (no network calls) and covers counter gating, label propagation, and no-op paths for missing or non-dict metadata.

Confidence Score: 5/5

This PR is safe to merge — the new MCP metric path is purely additive, guarded by two isinstance checks, and isolated to a dedicated label-value object that does not touch any existing counter's label set.

The change is narrowly scoped: two new counters wired only through _increment_mcp_tool_call_metrics, which bails out early on any unexpected payload shape. Previously flagged issues (mutable shared list, missing dict guards) have all been corrected in this version. No regressions are introduced to existing counters or the success-logging critical path.

No files require special attention.

Important Files Changed

Filename Overview
litellm/integrations/prometheus.py Adds two new MCP Counter metrics with proper isinstance dict guards and a dedicated UserAPIKeyLabelValues/PrometheusLabelFactoryContext so MCP labels stay isolated from other counters. No issues found.
litellm/types/integrations/prometheus.py Adds MCP_TOOL_NAME/MCP_SERVER_NAME to UserAPIKeyLabelNames, registers both metric names in DEFINED_PROMETHEUS_METRICS, defines label lists as independent objects (spend list uses list(...) copy), and adds optional fields to UserAPIKeyLabelValues.
tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py New mock-only test file covering registration, label propagation, spend counter gating, and graceful no-ops for missing/non-dict metadata. All tests use mocks — no real network calls. No issues found.

Reviews (5): Last reviewed commit: "feat(prometheus): expose MCP tool metada..." | Re-trigger Greptile

Comment thread litellm/integrations/prometheus.py Outdated
Comment thread litellm/types/integrations/prometheus.py Outdated
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds two Prometheus counters — litellm_mcp_tool_calls_total and litellm_mcp_tool_call_spend_metric — that fire whenever mcp_tool_call_metadata is present in the standard logging payload, enabling Grafana dashboards to break down MCP usage by tool and server name.

  • Metric registration, label definitions (mcp_tool_name, mcp_server_name), and the UserAPIKeyLabelValues dataclass are cleanly extended in litellm/types/integrations/prometheus.py; the new _increment_mcp_tool_call_metrics() helper is correctly isolated so MCP-specific labels don't pollute the 30+ other counters.
  • The test suite (14 mock-only unit tests) covers registration, increment, no-op, and label-propagation paths and uses the intentional PrometheusLogger._inc_labeled_counter(self, …) unbound-style call so the real label-building logic runs even against a MagicMock instance.

Confidence Score: 4/5

Safe to merge; the new code path is additive and only activates when MCP metadata is present in a log payload.

The implementation is well-structured and tested. The only concerns are defensive: mcp_meta is not checked to be a dict before .get() is called (a truthy non-dict value stored under mcp_tool_call_metadata would raise AttributeError in the success-log path), and the two new PrometheusMetricLabels class attributes omit List[str] annotations unlike every other label list in the class.

The _increment_mcp_tool_call_metrics method in litellm/integrations/prometheus.py and the new class attributes in litellm/types/integrations/prometheus.py are the only areas worth a second look.

Important Files Changed

Filename Overview
litellm/integrations/prometheus.py Adds two new Prometheus counters and _increment_mcp_tool_call_metrics() that fires from async_log_success_event; logic is sound but mcp_meta is not guarded as dict before .get() is called.
litellm/types/integrations/prometheus.py Registers MCP metric names, adds label sets to PrometheusMetricLabels, and extends UserAPIKeyLabelValues; new class attributes lack List[str] type annotations unlike the rest of the class.
tests/test_litellm/integrations/test_prometheus_mcp_tool_metrics.py New test file with 14 unit tests covering metric registration, counter increment, no-op behaviour, and label propagation; tests are mock-only and comply with the repository's testing rules.

Reviews (2): Last reviewed commit: "feat(prometheus): expose MCP tool metada..." | Re-trigger Greptile

Comment thread litellm/integrations/prometheus.py
Comment thread litellm/types/integrations/prometheus.py Outdated
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai review

@yassin-berriai
yassin-berriai force-pushed the litellm_prometheus-mcp-tool-metadata branch from 72bf712 to f641746 Compare July 2, 2026 07:34
@yassin-berriai

Copy link
Copy Markdown
Contributor

@greptileai review

@yassin-berriai
yassin-berriai force-pushed the litellm_prometheus-mcp-tool-metadata branch from f641746 to 9949ba0 Compare July 2, 2026 07:47
@yassin-berriai

Copy link
Copy Markdown
Contributor

@greptileai review

@yassin-berriai
yassin-berriai enabled auto-merge (squash) July 2, 2026 07:47
@yassin-berriai
yassin-berriai merged commit 85db18e into litellm_internal_staging Jul 2, 2026
123 checks passed
@yassin-berriai
yassin-berriai deleted the litellm_prometheus-mcp-tool-metadata branch July 2, 2026 07:56
Rodrigo-Palma pushed a commit to Rodrigo-Palma/litellm that referenced this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants