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: