feat(tenancy): establish cloud isolation foundations

This commit is contained in:
Junyan Qin
2026-07-19 22:39:58 +08:00
parent d7adbeec1e
commit 3f01ffe63b
41 changed files with 1985 additions and 50 deletions
+14 -7
View File
@@ -32,13 +32,8 @@ from ....workspace.errors import WorkspaceError, WorkspaceInvariantError
import langbot_plugin.api.entities.builtin.resource.tool as resource_tool
import langbot_plugin.api.entities.builtin.provider.message as provider_message
from ....entity.persistence import mcp as persistence_mcp
from .mcp_stdio import (
BoxStdioSessionRuntime,
MCPServerBoxConfig as MCPServerBoxConfig, # noqa: F401 - public re-export
MCPSessionErrorPhase,
_ColdStartRetry,
_get_default_memory_mb,
) # noqa: F401
from .mcp_stdio import BoxStdioSessionRuntime, MCPServerBoxConfig, MCPSessionErrorPhase, _ColdStartRetry # noqa: F401
from .mcp_policy import require_stdio_mcp_enabled, stdio_mcp_enabled
# Synthesized LLM tools for MCP resources (not from server tools/list).
# Dispatched in MCPLoader.invoke_tool; placeholder func on LLMTool is never used.
@@ -367,6 +362,11 @@ class RuntimeMCPSession:
)
async def _init_stdio_python_server(self):
# Final transport gate: this must run before both the Box branch and
# the backwards-compatible host-stdio branch. Service/UI checks are
# usability guards; this is the execution security boundary.
require_stdio_mcp_enabled(self.ap, self.server_config)
if self._uses_box_stdio():
await self._box_stdio_runtime.initialize()
return
@@ -1502,6 +1502,12 @@ class MCPLoader(loader.ToolLoader):
for server in servers:
config = self.ap.persistence_mgr.serialize_model(persistence_mcp.MCPServer, server)
if config.get('mode') == 'stdio' and not stdio_mcp_enabled(self.ap):
self.ap.logger.info(
f'Skipping disabled stdio MCP server {server.uuid}; '
'the persisted configuration is retained but no process is launched'
)
continue
try:
binding = await self.ap.workspace_service.get_execution_binding(server.workspace_uuid)
execution_context = ExecutionContext(
@@ -1577,6 +1583,7 @@ class MCPLoader(loader.ToolLoader):
"""
execution_context = await self._assert_execution_active(context)
server_config = dict(server_config)
require_stdio_mcp_enabled(self.ap, server_config)
configured_workspace = str(server_config.get('workspace_uuid') or '').strip()
if configured_workspace and configured_workspace != execution_context.workspace_uuid:
raise ValueError('MCP server configuration belongs to another Workspace')
@@ -0,0 +1,50 @@
from __future__ import annotations
from typing import Any
MCP_STDIO_DISABLED_CODE = 'mcp_stdio_disabled'
MCP_STDIO_DISABLED_MESSAGE = 'Stdio MCP is disabled by instance policy'
class MCPStdioDisabledError(RuntimeError):
"""Raised when an instance-level policy refuses stdio MCP execution."""
code = MCP_STDIO_DISABLED_CODE
def __init__(self) -> None:
super().__init__(MCP_STDIO_DISABLED_MESSAGE)
def stdio_mcp_enabled(ap: Any) -> bool:
"""Return the independent instance-level stdio MCP feature gate.
The open-source default remains enabled for backwards compatibility. A
deployment can disable it with ``mcp.stdio.enabled: false`` (or
``MCP__STDIO__ENABLED=false``). Invalid values fail closed instead of
accidentally enabling local process execution.
"""
instance_config = getattr(ap, 'instance_config', None)
config = getattr(instance_config, 'data', None)
if not isinstance(config, dict):
return False
mcp_config = config.get('mcp', {})
if not isinstance(mcp_config, dict):
return False
stdio_config = mcp_config.get('stdio', {})
if not isinstance(stdio_config, dict):
return False
enabled = stdio_config.get('enabled', True)
return enabled if isinstance(enabled, bool) else False
def is_stdio_server(server_config: dict[str, Any] | None) -> bool:
return isinstance(server_config, dict) and str(server_config.get('mode') or '').strip().lower() == 'stdio'
def require_stdio_mcp_enabled(ap: Any, server_config: dict[str, Any] | None) -> None:
"""Fail closed for a stdio server before choosing Box or host transport."""
if is_stdio_server(server_config) and not stdio_mcp_enabled(ap):
raise MCPStdioDisabledError()