feat: add mcp backed workflow server

This commit is contained in:
lda
2026-06-04 23:17:02 +07:00 Unverified
parent 0d34174a84
commit 3b8a1fe6dc
10 changed files with 862 additions and 15 deletions
+7 -1
View File
@@ -5,7 +5,11 @@ from .discovery import (
specs_from_discovered_tools,
)
from .events import McpEvent, make_event
from .server import create_broker_server
from .server import (
build_workflow_server_from_config,
create_broker_server,
workflow_server_from_service,
)
from .config import build_service_from_config, load_broker_config
from .transport import normalize_transport
from .service import WfMcpService
@@ -16,6 +20,7 @@ __all__ = [
"McpEvent",
"WfMcpService",
"build_service_from_config",
"build_workflow_server_from_config",
"create_broker_server",
"discover_connection_capabilities",
"load_broker_config",
@@ -23,4 +28,5 @@ __all__ = [
"snapshot_from_specs",
"specs_from_discovered_tools",
"normalize_transport",
"workflow_server_from_service",
]
+82
View File
@@ -2,11 +2,26 @@ from __future__ import annotations
from mcp.server.fastmcp import FastMCP
from wf_api import (
WorkflowAdminApi,
WorkflowApi,
WorkflowSourceAdminApi,
WorkflowSourceRegistryApi,
durable_workflow_api,
)
from wf_api.stores import WorkflowStores
from wf_server import WorkflowServer, WorkflowServerConfig
from .artifact_tools import register_artifact_tools
from .config import build_service_from_config
from .prompts import register_broker_prompts
from .resources import register_broker_resources
from .service import WfMcpService
from .service.source_registry_admin import SourceRegistryAdminProvider
from .service.workflow_operation_context import context_from_service
from .tools import register_broker_tools
from ..models import BrokerConfig
from ..source_registry import FileSourceRegistryStore, SourceRegistryStore
def create_broker_server(service: WfMcpService) -> FastMCP:
@@ -24,3 +39,70 @@ def create_broker_server(service: WfMcpService) -> FastMCP:
register_broker_resources(server, service)
register_broker_prompts(server, service)
return server
def workflow_server_from_service(
service: WfMcpService,
*,
config: BrokerConfig,
source_registry_store: SourceRegistryStore,
) -> WorkflowServer:
"""Adapt an MCP broker service into the neutral WorkflowServer shape.
This is intentionally in wf_mcp, not wf_server: MCP owns upstream source
management, while wf_server stays transport-neutral and MCP-free.
"""
if (
service.artifact_store is None
or service.draft_workspace_store is None
or service.run_store is None
):
raise ValueError("MCP-backed WorkflowServer requires workflow stores")
context = context_from_service(service)
api: WorkflowApi = durable_workflow_api(context)
source_admin = WorkflowSourceAdminApi(context)
admin = WorkflowAdminApi(
connections=service.connection_service,
events=service.events,
)
registry_provider = SourceRegistryAdminProvider(
source_registry_store=source_registry_store,
config_connections=config.connections,
)
source_registry_admin = WorkflowSourceRegistryApi(
provider=registry_provider,
mutation_provider=registry_provider,
)
stores = WorkflowStores(
artifact_store=service.artifact_store,
draft_workspace_store=service.draft_workspace_store,
run_store=service.run_store,
)
return WorkflowServer(
config=WorkflowServerConfig(store_root=config.store_root),
stores=stores,
context=context,
api=api,
source_admin=source_admin,
admin=admin,
events=service.events,
source_registry_admin=source_registry_admin,
)
def build_workflow_server_from_config(config: BrokerConfig) -> WorkflowServer:
"""Build a neutral WorkflowServer backed by MCP broker runtime services."""
service = build_service_from_config(config)
return workflow_server_from_service(
service,
config=config,
source_registry_store=FileSourceRegistryStore(config.store_root),
)
__all__ = [
"build_workflow_server_from_config",
"create_broker_server",
"workflow_server_from_service",
]
+15 -3
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from typing import Any, cast
from wf_mcp.events import EventBus, McpEvent, make_event
from wf_mcp.models import CatalogSnapshot
@@ -18,8 +18,20 @@ class BrokerEventRecorder:
event_bus: EventBus
def record_event(self, event: McpEvent) -> None:
self.event_bus.publish(event)
def record_event(self, event: object) -> None:
# WorkflowEventRecorder is protocol-neutral and may pass test/local
# sentinel objects. EventBus is typed for McpEvent, but the in-memory
# history sink preserves whatever object is published.
self.event_bus.publish(cast(McpEvent, event))
def record_workflow_event(
self,
event_type: str,
*,
capability_id: str,
payload: dict[str, Any],
) -> None:
self.record_kind(event_type, capability_id=capability_id, payload=payload)
def record_kind(
self,