fix(core): disable MCP stdio servers by default (#8969)

* fix(core): disable MCP stdio servers by default

Stdio MCP servers spawn local processes on the host, which is not
suitable for multi-tenant or shared server deployments. This change:

- Changes `mcp_disable_stdio` default from False to True
- Enforces the setting in `get_mcp_client()` and `create_mcp_server_from_config()`
- Users running local/single-user deployments can set MCP_DISABLE_STDIO=false
  to enable stdio-based MCP servers (e.g., for npx/uvx tools)

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* update ci

* push

---------

Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: jnjpng <jin@letta.com>
Co-authored-by: Letta Bot <jinjpeng@gmail.com>
This commit is contained in:
Charles Packer
2026-01-20 11:34:47 -08:00
committed by Caren Thomas
parent 5645ca8107
commit 238894eebd
3 changed files with 22 additions and 1 deletions

View File

@@ -419,6 +419,9 @@ class MCPManager:
"""
# Create base MCPServer object
if isinstance(server_config, StdioServerConfig):
# Check if stdio MCP servers are disabled (not suitable for multi-tenant deployments)
if tool_settings.mcp_disable_stdio:
raise ValueError("MCP stdio servers are disabled. Set MCP_DISABLE_STDIO=false to enable them.")
mcp_server = MCPServer(server_name=server_config.server_name, server_type=server_config.type, stdio_config=server_config)
elif isinstance(server_config, SSEServerConfig):
mcp_server = MCPServer(
@@ -832,6 +835,9 @@ class MCPManager:
server_config = SSEServerConfig(**server_config.model_dump())
return AsyncFastMCPSSEClient(server_config=server_config, oauth=oauth, agent_id=agent_id)
elif server_config.type == MCPServerType.STDIO:
# Check if stdio MCP servers are disabled (not suitable for multi-tenant deployments)
if tool_settings.mcp_disable_stdio:
raise ValueError("MCP stdio servers are disabled. Set MCP_DISABLE_STDIO=false to enable them.")
server_config = StdioServerConfig(**server_config.model_dump())
return AsyncStdioMCPClient(server_config=server_config, oauth_provider=None, agent_id=agent_id)
elif server_config.type == MCPServerType.STREAMABLE_HTTP:

View File

@@ -516,6 +516,9 @@ class MCPServerManager:
"""
# Create base MCPServer object
if isinstance(server_config, StdioServerConfig):
# Check if stdio MCP servers are disabled (not suitable for multi-tenant deployments)
if tool_settings.mcp_disable_stdio:
raise ValueError("MCP stdio servers are disabled. Set MCP_DISABLE_STDIO=false to enable them.")
mcp_server = MCPServer(server_name=server_config.server_name, server_type=server_config.type, stdio_config=server_config)
elif isinstance(server_config, SSEServerConfig):
mcp_server = MCPServer(
@@ -1003,6 +1006,9 @@ class MCPServerManager:
server_config = SSEServerConfig(**server_config.model_dump())
return AsyncFastMCPSSEClient(server_config=server_config, oauth=oauth, agent_id=agent_id)
elif server_config.type == MCPServerType.STDIO:
# Check if stdio MCP servers are disabled (not suitable for multi-tenant deployments)
if tool_settings.mcp_disable_stdio:
raise ValueError("MCP stdio servers are disabled. Set MCP_DISABLE_STDIO=false to enable them.")
server_config = StdioServerConfig(**server_config.model_dump())
return AsyncStdioMCPClient(server_config=server_config, oauth_provider=None, agent_id=agent_id)
elif server_config.type == MCPServerType.STREAMABLE_HTTP:

View File

@@ -37,7 +37,16 @@ class ToolSettings(BaseSettings):
mcp_list_tools_timeout: float = 30.0
mcp_execute_tool_timeout: float = 60.0
mcp_read_from_config: bool = False # if False, will throw if attempting to read/write from file
mcp_disable_stdio: bool = False
mcp_disable_stdio: bool = Field(
default=True,
description=(
"Disable MCP stdio server type. When True (default), creating or connecting to "
"MCP servers using stdio transport will fail. Stdio MCP servers spawn local "
"processes, which is not suitable for multi-tenant or shared server deployments. "
"Set to False for local or single-user deployments where stdio-based MCP servers "
"are needed (e.g., running local tools via npx or uvx)."
),
)
@property
def modal_sandbox_enabled(self) -> bool: