9.1 KiB
wf_mcp Architecture Boundaries
wf_mcp is one distribution for now, but it is organized as separable concerns.
The goal is to keep future package extraction cheap without adding packaging
overhead before the APIs settle.
For the short operator-facing map of the current nouns and tool families, start
with wf_mcp_operator_manual.md.
Packages
| Package | Responsibility |
|---|---|
wf_mcp.proxy |
Expose configured upstream MCP servers through mounted FastMCP proxy providers. Owns proxy runtime, admin tools, and proxy tool listing helpers. |
wf_mcp.broker |
Coordinate remembered connections, catalog snapshots, discovery, events, and workflow execution through broker services. |
wf_mcp.workflow |
Convert discovered MCP tools into wf_authoring / wf_core node specs. |
wf_mcp.sdk |
Speak to upstream MCP servers through the MCP Python SDK. Owns adapter protocols, SDK transport/session calls, and SDK object converters. |
wf_mcp.control |
Parse and mutate file-backed proxy/broker configuration. |
wf_mcp.storage |
Persist auth records and catalog snapshots. |
wf_mcp.shared |
Pure helpers used across concerns, such as names, pagination, and error payloads. |
The root wf_mcp package is a small public facade for common user entrypoints,
not a dump of every internal helper. If a caller needs broker internals, SDK
adapter protocols, proxy admin pieces, or shared name parsing, import the
relevant concern package directly.
Dependency Rules
wf_apidefines the workflow application surface and process-local implementation.wf_mcpmay import and adapt it;wf_apimust not importwf_mcp.wf_mcp.sdkshould not importwf_coreorwf_authoring.wf_mcp.proxyshould not importwf_mcp.workflow.wf_mcp.workflowis the only layer that converts MCP capabilities into node specs.wf_mcp.brokermay coordinatesdk,storage,control, andworkflow.wf_mcp.controlshould not know about live MCP clients or workflow execution.wf_mcp.sharedshould stay pure and should not import otherwf_mcpconcern packages.- Root compatibility shims should stay thin: import and re-export only.
Workflow API Boundary
Workflow lifecycle operations now have a protocol-neutral front door:
wf_mcp.workflow_surface.tools
-> wf_api.WorkflowApiSurface implementation
-> wf_api.WorkflowApi
-> wf_api domain services
-> WorkflowOperationContext
-> WfMcpService adapters/stores/runtime
WorkflowSurfaceHandlers is a compatibility shim only. New entrypoints should
construct WorkflowApi(context_from_service(service)) directly.
The old backend-adapter/protocol layer has been removed. WorkflowApiSurface
is now the structural workflow operation contract. The local WorkflowApi
implementation composes domain services (WorkflowCapabilityApi,
WorkflowDraftApi, WorkflowArtifactApi, WorkflowDeploymentApi,
WorkflowRunApi) from a WorkflowOperationContext.
New code should type consumers against wf_api.WorkflowApiSurface when they can
work with either local or remote implementations. Use concrete WorkflowApi
only when same-process stores/runtime are required. Do not add new callers that
import WorkflowSurfaceHandlers directly unless they are compatibility tests.
The broader application-service boundary is documented in
wf_api_architecture.md.
This is a dependency-direction cleanup, not a full domain split. Most API
methods still mirror the old workflow-surface payloads and return
dict[str, Any].
Protocol-neutral workflow helpers have moved to wf_api: constants, capability
refs, wrapper hints, next actions, raw workflow plans, runtime dependency
resolution, saved subgraph preparation, and durable run lifecycle helpers. The
old wf_mcp.workflow_surface.* helper modules are compatibility shims. MCP
tool schemas and registration still live in wf_mcp.workflow_surface.models and
wf_mcp.workflow_surface.tools.
Broker Catalogs
The broker keeps two related catalog views:
get_catalog()is the backend MCP catalog. It only includes enabled upstream connection snapshots loaded from storage.get_planner_catalog()is the workflow-planning catalog. It includes backend connection snapshots plus broker-local system sources such aswf.std.
Broker-local sources are not fake MCP backend connections. They are registered
as service spec sources so raw workflow plans can address nodes like
wf.std.runtime_error without polluting connection status, auth, adapter lookup,
or persisted backend catalog snapshots.
The longer-term source model is described in
wf_mcp_capability_sources.md. In that model,
sources own tools, workflow node specs, prompts, and resources, while broker,
proxy, planner, and admin UI surfaces project different capability kinds.
Hot Reload
Proxy reload is intentionally isolated in
wf_mcp.proxy.runtime. FastMCP does not currently expose a complete
provider/proxy unmount lifecycle that we can rely on for safe per-connection
teardown. Until that exists, reload should be treated as best-effort remounting,
not a fully safe session/subscription lifecycle.
Unified mode currently reuses ProxyRuntime as its proxy mounting engine. The
old TransparentProxyRuntime compatibility alias has been removed; use
ProxyRuntime directly.
ProxyRuntime now owns a small ProxyMountRegistry. Proxy/admin tools are
registered once on the top-level local provider; reload only clears the visible
mounted upstream provider list and rebuilds it from current config. Unchanged
enabled connections reuse their cached proxy mount instead of recreating a new
client/proxy pair every time. Disabled or removed connections are no longer
mounted after reload, but their cached mounts are only retired internally for
now; they are not safely closed or unmounted because FastMCP does not yet expose
the lifecycle hook we need.
The reused FastMCP proxies are not holding one forever-open upstream connection.
create_proxy_mount() gives create_proxy(...) a disconnected client, and
FastMCP creates fresh request clients from it. What persists across reload is the
proxy/provider object and its component-list caches. FastMCP refreshes those
caches on explicit list_* calls and otherwise expires them after its TTL.
After a successful reload, the runtime publishes local tools_changed,
resources_changed, prompts_changed, and catalog_changed events when an
event bus is supplied. The admin MCP tool projects the same event kinds into
MCP list-changed notifications for the current client session. Config mutation
tools still only stage changes and return requires_reload; they do not emit
list-changed notifications until reload remounts the visible capability set.
Internally, reload metadata uses ProxyReloadResult; MCP tools serialize that
typed result to a plain payload at the boundary.
Proxy tool listing similarly uses ProxyToolPayload / ProxyToolsPage
internally and serializes to admin MCP payloads at the boundary.
Do not add more lifecycle behavior outside ProxyMountRegistry. Cached clients
still need clear close/reconnect/error semantics, and the registry is the single
place where that future behavior should land. Prefer FastMCP's official
unmount/provider lifecycle when it becomes available.
Do not add notification proxying or long-lived subscription handling across reloads without first introducing an explicit mount lifecycle boundary.
The current practical proxy roadmap, including which FastMCP gaps are worth
working around locally and which should stay upstream-dependent for now, lives
in wf_mcp_proxy_reality_and_roadmap.md.
The public MCP mode split has been retired. The execution plan is
historical/superpowers/plans/2026-05-16-retire-legacy-mcp-modes.md:
broker and proxy were legacy public launch surfaces, while the ordinary
server now exposes both local capabilities and proxied upstream capabilities.
Internal concern packages remain useful even though the public mode choices are
gone.
Future Extraction
If this becomes multiple distributions, likely split points are:
wf-transport-mcp: MCP-facing transport that exposes neutralWorkflowServer/wf_apiworkflow, source-admin, and platform-admin surfaces to MCP clients.wf-sources-mcp: upstream MCP source provider that owns external MCP server discovery, sessions, tool invocation, resources/prompts, FastMCP integration, and conversion of discovered MCP tools into workflow node specs.wf-mcp-proxy: compatibility/proxy runtime for mounting upstream MCP servers through FastMCP, if that remains useful afterwf-sources-mcpexists.
The key boundary is direction, not naming: "MCP as a client transport" and
"MCP as an upstream workflow source" are separate roles. The current wf_mcp
package still contains both roles plus compatibility entrypoints. New
server/transport code should depend on wf_server / wf_api surfaces and keep
MCP-specific upstream behavior behind a source-provider adapter.
For now, keep one distribution and use import discipline to preserve these future split points.