fix(mcp): run pre_call_tool_check on OpenAPI/local-registry path (VERIA-7) - #27016
fix(mcp): run pre_call_tool_check on OpenAPI/local-registry path (VERIA-7)#27016yuneng-berri merged 2 commits into
Conversation
…IA-7) `execute_mcp_tool` dispatches in two ways: managed MCP servers go through `_handle_managed_mcp_tool`, which calls `MCPServerManager.pre_call_tool_check` to enforce allowed/banned tool lists, key/team `object_permission` tool grants, and parameter validation. OpenAPI-backed tools, however, were resolved via `global_mcp_tool_registry` and dispatched directly to `_handle_local_mcp_tool` — entirely skipping `pre_call_tool_check`. A caller could invoke any registered OpenAPI tool regardless of their key/team permissions, including administrative or destructive operations on the upstream API. Run `pre_call_tool_check` before the local-registry dispatch whenever the resolved server is set (the same condition used to surface server context to the managed path). Honor any guardrail-modified arguments the hook returns. Errors raised by the hook propagate up before `_handle_local_mcp_tool` runs. Tests cover both directions: the pre-call hook fires when the local tool resolves alongside a server, and a hook-raised HTTPException prevents the local handler from being invoked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR closes an auth-bypass on the OpenAPI/local-registry MCP tool path by inserting Confidence Score: 5/5Safe to merge; no P0 or P1 issues found — only P2 suggestions. The core security fix is correctly implemented: pre_call_tool_check fires before local dispatch, proxy_logging_obj is sourced from the canonical module, and the mcp_server=None path is hard-blocked. Both previous review thread concerns are resolved. Remaining findings are P2 (backwards-compat flag suggestion and a test assertion fragility), neither of which blocks correctness. No files require special attention beyond the P2 notes above.
|
| Filename | Overview |
|---|---|
| litellm/proxy/_experimental/mcp_server/server.py | Adds pre_call_tool_check before local-registry dispatch; correctly guards against mcp_server is None with a 503, imports proxy_logging_obj from the canonical module, and propagates guardrail-modified arguments. The 503 hard-failure for any tool without a server mapping is a backwards-incompatible change with no feature flag. |
| tests/test_litellm/proxy/_experimental/mcp_server/test_openapi_tool_auth.py | New mock-only test file with 3 tests covering hook-fires, hook-blocked, and server-not-resolvable paths; the PR description lists "2 pass" but the file contains 3 tests. The proxy_logging_obj is not None assertion in test 1 is tied to module-level initialisation state. |
Reviews (2): Last reviewed commit: "fix(mcp): use canonical proxy_logging_ob..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…resolvable
Greptile flagged two follow-ups on the OpenAPI/local-registry pre-call
check:
1. **P1 runtime crash via None proxy_logging_obj.**
`kwargs.get("proxy_logging_obj")` is `None` on the MCP entry path,
and `pre_call_tool_check` calls `proxy_logging_obj._create_mcp_request_object_from_kwargs`
unconditionally after the security checks, which would have crashed
every legitimate call with `AttributeError`. Source the logging
object from `litellm.proxy.proxy_server` the same way
`_handle_managed_mcp_tool` already does.
2. **P2 authorization-bypass window when mcp_server is None.**
Previously the new check was guarded by `if mcp_server is not None`,
so any local tool whose registry entry had no resolvable server (a
startup-race window before `_initialize_tool_name_to_mcp_server_name_mapping`
completes, or an orphaned registry entry) ran without the security
check. Tools registered via openapi_to_mcp_generator are always tied
to a server, so a missing one is a configuration/timing fault — fail
the call with 503 instead of dispatching unguarded.
Tests: existing two pass with an added assertion that
`proxy_logging_obj` is non-None at the call site, plus a new test that
covers the 503 deny branch when the tool→server mapping is missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Greptile P1 + P2 fixed in 8ee599a:
|
|
Both fixes are solid. The implementation is correct: P1 — The P2 — The 503 before the The three tests give good coverage of the three branches: normal dispatch, policy deny, and infrastructure deny. LGTM. |
|
Bumping for re-review. @greptileai please re-trigger. |
c154b0d
into
BerriAI:litellm_internal_staging
…-bypass fix(mcp): run pre_call_tool_check on OpenAPI/local-registry path (VERIA-7)
Summary
`execute_mcp_tool` dispatches in two ways:
A caller could therefore invoke any registered OpenAPI tool regardless of their key/team permissions, including administrative or destructive operations on the upstream API.
This patch runs `pre_call_tool_check` before the local-registry dispatch whenever the resolved server is set (the same condition used to surface server context to the managed path) and honors any guardrail-modified arguments the hook returns. Errors raised by the hook propagate up before `_handle_local_mcp_tool` runs.
Behavior changes
Test plan
Type
🐛 Bug Fix
✅ Test