Repository navigation
chore(release): backport #44066 to rc/1.104.0 - #44216
Conversation
* feat(mcp)!: disable stdio MCP servers by default stdio MCP servers now only run when the proxy is started with LITELLM_ENABLE_MCP_STDIO=true. While it is off, existing stdio servers stay registered but never start: tool listings skip them quietly, direct tool calls and health checks return a 403 naming the env var, and creating or updating a stdio server is rejected. The flag is read from the process environment only, so DB-stored environment_variables cannot turn it on. The UI reads mcp_stdio_enabled from /.well-known/litellm-ui-config to grey out the stdio transport, show a banner on stdio forms, and badge stdio server cards. BREAKING CHANGE: stdio MCP servers are off by default. Set LITELLM_ENABLE_MCP_STDIO=true in the proxy environment and restart to keep using them. * fix(mcp): ignore stdio flag from config file and read UI flag from the selected worker LITELLM_ENABLE_MCP_STDIO set under environment_variables in config.yaml is now skipped like the DB-stored value, so only the process environment can enable stdio. The dashboard reads mcp_stdio_enabled from the proxy it is managing, so a control plane shows each worker's own setting. * test(mcp): cover non-mapping payloads in the shared transport validator * fix(mcp): skip blocked stdio servers quietly in every listing and keep the UI unchanged until the flag loads Prompt, resource and resource-template listings now skip a blocked stdio server at debug level like tool listing does, instead of logging a warning per server on every call. The dashboard only treats stdio as disabled once the proxy explicitly reports mcp_stdio_enabled false, so a proxy with the flag on, or an older one without the field, renders exactly as before with no flicker while loading. * fix(mcp): route blocked stdio tool calls to the flag error and warn once per server A gateway tools/call naming a blocked stdio server's tool now returns the LITELLM_ENABLE_MCP_STDIO message instead of "Tool not found". The "will not start" warning moves out of build_mcp_server_from_table, which DB reload re-runs on every cycle for rows with a NULL updated_at and which drafts and test-connection also call. It now fires when a row first enters the registry or changes transport. * fix(ui): explain on the server detail page why a stdio server is inert The Overview and MCP Tools tabs showed "No tools available" with no reason while stdio is disabled. The detail page now shows the same warning banner as the edit form, and hands off to the form's banner once editing starts. * refactor(ui): name the stdio banner conditions on the server detail page Keeps local/no-long-condition-chain within its budget * fix(proxy): log the ignored DB-stored LITELLM_ENABLE_MCP_STDIO warning once The DB config sync re-reads environment_variables on every cycle, so a stored flag logged the warning on each sync per worker (cherry picked from commit 2b85808)
|
| export const useMcpStdioEnabled = (): boolean => | ||
| $api.useQuery("get", "/.well-known/litellm-ui-config").data?.mcp_stdio_enabled !== false; |
There was a problem hiding this comment.
Missing status enables stdio
If the UI-config request fails or omits mcp_stdio_enabled, this expression treats stdio as enabled. On a proxy where stdio is off, the dashboard offers stdio without a warning and lets an admin submit a configuration the proxy will reject. Treat an unavailable setting as unknown or disabled.
Knowledge Base Used: Dashboard and enterprise UI
| "stdio MCP servers are disabled on this proxy. Set LITELLM_ENABLE_MCP_STDIO=true on the proxy and restart to enable them"; | ||
|
|
||
| export const useMcpStdioEnabled = (): boolean => | ||
| $api.useQuery("get", "/.well-known/litellm-ui-config").data?.mcp_stdio_enabled !== false; |
There was a problem hiding this comment.
Stdio status survives worker switches
The request target changes when an operator selects another worker, but this query keeps the same cache key and worker selection does not refresh it. An already-open MCP page can therefore show the previous worker’s stdio setting, hiding an available option or offering one the current worker will reject. Include the worker in the query key or refresh the setting when workers change.
Knowledge Base Used: Dashboard and enterprise UI
TLDR
Backport of #44066 (merged on main) to rc/1.104.0 with
git cherry-pick -x, plus lock-only dependency bumps and the rebuilt Admin UI bundle. No litellm version bump, since 1.104.0 has not been releasedProblem this solves:
How it solves it:
LITELLM_ENABLE_MCP_STDIO=trueis set in the process envIntentional product change: proxies on 1.104.0 that run stdio MCP servers stop starting them until the admin sets
LITELLM_ENABLE_MCP_STDIO=trueand restarts. The proxy logs one warning per affected server naming the env varUser Flow
Before: an admin with a stdio MCP server in config.yaml upgrades to 1.104.0 and stdio keeps running, with no way to turn it off
["echo"]echo_stdio-echoreturnsecho: hi"transport": "stdio"creates a new stdio serverAfter: the same admin upgrades without setting the env var, and stdio stays off until they opt in
[], and POST /mcp-rest/tools/call returns 403 namingLITELLM_ENABLE_MCP_STDIO=true"transport": "stdio"returns 422 with the same messageLITELLM_ENABLE_MCP_STDIO=true, restart, and steps 1 to 3 of Before work againWhat is included
Each dependency commit moves only its own package version, checked by parsing the lock before and after, and
uv lock --checkwith the line's uv 0.11.7 passes on the tip. main already resolves every one of them at or above these versions. The pyjwt commit reuses the minimal lock edit from #44156. The pypdf commit also carries about 190 lines of resolver marker reshuffling that any upgrade relock of this lock produces; no other package version changesAdaptation notes
#44066 changes the same set of lines as upstream; only context and paths differ. The conflicts were all neighbours this line does not have: imports (
tool_catalog_guard,getMCPNetworkAccess,AgentResponse), twoProxyConfigattributes, aLITELLM_LICENSEline inrun_integration.sh, and upstream-only neighbour tests in three test files. For those test files only the PR's own test functions were applied, checked by name against the source. Unit tests live undertests/test_litellm/on this line instead oftests/unit/. One addition:MCPServerCard.test.tsxgetsimport userEvent from "@testing-library/user-event", which upstream already had from an earlier PR and the pick's new tests useKnown noise on this line
test_rest_endpoints.py::TestListToolsRestAPI::test_single_server_response_includes_paginated_upstream_toolsfailed once under-n 8and passed in 3 isolated runs, 2 file runs and a full rerun. It uses an HTTP server and does not touch stdio. The review also saw the pick's prompts-listing skip test fail 1 time in 3, when an unclosed aiohttp session warning leaked into its no-warning assertion. That assertion is the same on mainScreenshots / Proof of Fix
Setup: the proxy runs from this line's own venv under
env -iagainst a fresh Postgres 16 container, withSTORE_MODEL_IN_DB=True.echo_stdiois a stdio server in config.yaml running a one-tool MCP server that echoes its input.Kis the master key andPis the proxy addressBefore (5663d07)
stdio server
curl -s "$P/mcp-rest/tools/list?server_id=$SID" -H "Authorization: Bearer $K" | jq -c '[.tools[].name]'curl -s -w ' HTTP %{http_code}' $P/mcp-rest/tools/call -H "Authorization: Bearer $K" -H 'Content-Type: application/json' -d '{"server_id":"<SID>","name":"echo_stdio-echo","arguments":{"text":"hi"}}'creating a stdio server
curl -s $P/v1/mcp/server -H "Authorization: Bearer $K" -H 'Content-Type: application/json' -d '{"server_name":"stdio_base","transport":"stdio","command":"python","args":["echo_server.py"]}' | jq -c '{server_name,transport}'UI config
curl -s $P/.well-known/litellm-ui-config | jq -c '{mcp_stdio_enabled}'chat completion
curl -s $P/v1/chat/completions -H "Authorization: Bearer $K" -H 'Content-Type: application/json' -d '{"model":"gpt-5.4-mini","messages":[{"role":"user","content":"Say OK"}]}' | jq -c '{model,content:.choices[0].message.content}'After (123be8b)
stdio server
Flag unset:
With
LITELLM_ENABLE_MCP_STDIO=true, and no "will not start" lines in the boot log:creating a stdio server
"server_name":"stdio_off""server_name":"stdio_on"UI config
flag set only in config.yaml
environment_variables: {LITELLM_ENABLE_MCP_STDIO: "true"}in config.yaml. Boot log:{"mcp_stdio_enabled":false}, and the same tools/call returns the message withHTTP 403chat completion
supporting checks
_experimental,management_endpoints,auth,discovery_endpointsandproxy_servertest dirs: 12584 passed on this branch against 12558 on the base, with 0 new failures. One failure is present on both (test_team_default_params.py::test_nonexistent_default_organization_returns)Type
🆕 New Feature
Caveats (if any)
Severe
LITELLM_ENABLE_MCP_STDIO=trueon the proxy and restartLow
transportis accepted and saves command/args edits. Nothing spawns while the flag is off. Upstream feat(mcp)!: disable stdio MCP servers by default #44066 behaves the same way.circleci/scripts/run_integration.shsets the flag so this line's stdio integration tests keep running