stateful proxy

This commit is contained in:
lda
2026-05-19 23:11:53 +07:00 Verified
parent 252ebab1f7
commit c820294f88
10 changed files with 414 additions and 5 deletions
+6
View File
@@ -7,6 +7,7 @@ from wf_artifacts import FileDraftWorkspaceStore, FileWorkflowArtifactStore
from ..control import BrokerConfigFile
from ..models import BrokerConfig
from ..runtime import McpRuntimePool, PersistentSessionFactory
from ..sdk import McpSdkAdapter
from ..storage import FileStore
from .service import WfMcpService
@@ -21,10 +22,15 @@ def load_broker_config(path: str | Path) -> BrokerConfig:
def build_service_from_config(config: BrokerConfig) -> WfMcpService:
"""Create a broker service with SDK adapters for configured connections."""
runtime_factory = PersistentSessionFactory()
service = WfMcpService(
store=FileStore(config.store_root),
artifact_store=FileWorkflowArtifactStore(config.store_root),
draft_workspace_store=FileDraftWorkspaceStore(config.store_root),
# Discovery can use short-lived SDK sessions. Workflow execution needs
# a persistent runtime so stateful MCP servers keep session/page state
# across sequential workflow nodes.
tool_executor=McpRuntimePool(runtime_factory.create),
)
for connection in config.connections:
service.register_connection(connection)
+14 -1
View File
@@ -41,6 +41,7 @@ from ...models import (
ConnectionConfig,
RawWorkflowPlan,
)
from ...runtime import ToolExecutor
from ...sdk import BackendAdapter
from ...shared.errors import error_payload
from ...shared.names import RESERVED_CONNECTION_IDS
@@ -72,6 +73,7 @@ class WfMcpService:
include_builtin_specs: bool = True
artifact_store: WorkflowArtifactStore | None = None
draft_workspace_store: DraftWorkspaceStore | None = None
tool_executor: ToolExecutor | None = None
def __post_init__(self) -> None:
"""Install broker-local system specs when enabled."""
@@ -130,6 +132,17 @@ class WfMcpService:
def register_adapter(self, server: str, adapter: BackendAdapter) -> None:
self.adapters[server] = adapter
def _tool_executor_for(self, connection: ConnectionConfig) -> ToolExecutor:
"""Return the executor used by generated workflow NodeSpecs.
Discovery still uses the short-lived adapter path. Generated workflow
nodes use this executor hook so config-built services can swap in a
persistent runtime pool for stateful MCP servers.
"""
if self.tool_executor is not None:
return self.tool_executor
return require_adapter(connection, self.adapters)
def save_auth(self, record: AuthRecord) -> None:
self.store.save_auth(record)
self._record_event(
@@ -606,7 +619,7 @@ class WfMcpService:
specs = specs_from_discovered_tools(
connection=connection,
auth=auth,
executor=adapter,
executor=self._tool_executor_for(connection),
tools=capabilities.tools,
emit_event=self._record_event,
)