From 238894eebd52fcb8403e55b134f45039bcaa5ce2 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Tue, 20 Jan 2026 11:34:47 -0800 Subject: [PATCH] fix(core): disable MCP stdio servers by default (#8969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * update ci * push --------- Co-authored-by: Letta Co-authored-by: jnjpng Co-authored-by: Letta Bot --- letta/services/mcp_manager.py | 6 ++++++ letta/services/mcp_server_manager.py | 6 ++++++ letta/settings.py | 11 ++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/letta/services/mcp_manager.py b/letta/services/mcp_manager.py index 173c909e..0f2ad4a8 100644 --- a/letta/services/mcp_manager.py +++ b/letta/services/mcp_manager.py @@ -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: diff --git a/letta/services/mcp_server_manager.py b/letta/services/mcp_server_manager.py index 8bd8534d..f1981a03 100644 --- a/letta/services/mcp_server_manager.py +++ b/letta/services/mcp_server_manager.py @@ -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: diff --git a/letta/settings.py b/letta/settings.py index 19899cd0..10297603 100644 --- a/letta/settings.py +++ b/letta/settings.py @@ -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: