7.4 KiB
Runtime Source Lifecycle
Date: 2026-06-09
Status: design direction for next source families
Related:
- Workflow config targets and sources
- Store-backed source registry
- Long-lived workflow API boundary
- Project map
Purpose
The current mutable source registry and apply/reload path are concrete for MCP
sources. The next source families should not be forced through MCP
ConnectionConfig language. This spec defines the generic lifecycle that MCP,
Python, HTTP/OpenAPI, and future source providers should share.
The rule:
Admin mutation changes desired source state. Apply/reload reconciles desired source state into live runtime sources.
wf_apikeeps seeing onlyCapabilitySource,NodeSpec, resources, prompts, and protocol surfaces.
Current State
MCP has a working implementation:
source registry desired MCP entries
-> wf admin registry apply
-> WfMcpService connections/adapters/catalog
-> WorkflowApi capability/source surfaces
This works, but it is still MCP-shaped internally:
- registry entries are
McpSourceRegistryEntry - runtime apply mutates
ConnectionService - execution uses
McpRuntimePool - compatibility glue still translates through legacy
ConnectionConfig
That is acceptable for MCP. It should not become the required model for Python or HTTP sources.
Desired Generic Lifecycle
All source families should fit this lifecycle:
config seed sources + store-backed desired sources
-> source provider manager
-> live source instances
-> capability inventory and executable NodeSpecs
-> WorkflowApi and transports
Definitions:
- Source config: deployment bootstrap in
wf_config. - Source registry: mutable desired state stored by the server.
- Source provider: implementation package for one source family, such as
wf_sources_mcp,wf_sources_python, orwf_sources_openapi. - Live source instance: in-memory runtime object created from desired state.
- Apply/reload: reconciliation from desired state to live source instances.
- Capability projection:
CapabilitySourceplus executableNodeSpecs, resources, prompts, reducers, or workflows exposed towf_api.
Provider Boundary
A future neutral provider boundary should be closer to this shape than to MCP connections:
from collections.abc import Mapping, Sequence
from typing import Any, Protocol
from wf_authoring import NodeSpec
from wf_platform import CapabilitySource
class SourceEntryLike(Protocol):
id: str
kind: str
enabled: bool
class SourceProvider(Protocol):
kind: str
async def apply_sources(
self,
*,
desired: Sequence[SourceEntryLike],
) -> dict[str, Any]:
"""Reconcile desired entries into live source instances."""
...
@property
def capability_sources(self) -> Mapping[str, CapabilitySource]:
"""Return current workflow-visible source inventory."""
...
def get_qualified_spec(self, qualified_name: str) -> NodeSpec[Any, Any]:
"""Return the executable spec for one qualified capability."""
...
This is not an immediate API contract. It is the direction: the provider owns
source-family semantics, while wf_api remains source-family neutral.
Source Family Expectations
MCP Sources
MCP sources already have:
- desired registry entries
- config seed/locked ownership
- explicit apply/reload
- persistent runtime sessions through
McpRuntimePool - catalog snapshots for tools/resources/prompts
MCP may continue to use ConnectionConfig behind its compatibility boundary
until the old wf_mcp facade is retired.
Python Sources
Python sources should be the first non-MCP proof that the architecture is not MCP-shaped.
Expected config example:
{
"kind": "python",
"id": "local.ops",
"module": "my_project.workflow_ops",
"registry": "registry"
}
Expected behavior:
- load a module/object containing
NodeSpecs or Python functions convertible toNodeSpecs - expose them under the configured source id
- execute in-process without MCP sessions
- support reload by re-importing/re-reading the configured registry
Python source reload should be explicit. Do not silently reload modules on every capability call.
HTTP/OpenAPI Sources
HTTP/OpenAPI sources should be a separate source family, not a special MCP transport.
Expected behavior:
- parse OpenAPI or explicit operation definitions
- expose operations as
NodeSpecs - use auth records where appropriate
- define a clear HTTP status/outcome policy before implementation
- keep large response/body handling bounded for CLI and workflow safety
This source family needs more design than Python because error semantics, timeouts, idempotency, and output normalization are product decisions.
Config vs Registry
Config and registry should keep their current distinction:
- Config is bootstrap, deployment intent, and portable local setup.
- Registry is mutable server-owned desired state.
lockedconfig entries own their ids and shadow registry entries.seedconfig entries initialize missing registry entries, then registry owns later admin changes.
For future source families, this ownership model should apply to source ids independently of MCP provider/account fields.
Apply Semantics
Apply/reload should:
- validate desired source entries before mutating live state
- add newly desired sources
- update changed source instances
- disable or remove deleted desired sources according to provider policy
- preserve diagnostics for referenced missing/disabled sources
- avoid mutating config files
- emit admin-visible events
Apply/reload should not:
- delete auth records by default
- delete old catalog/cache files by default
- rewrite deployments or artifacts
- turn live source failure into workflow interrupts
Workflow API Boundary
wf_api should not know whether a capability came from MCP, Python, HTTP, or a
future source family. It should depend on:
WorkflowSpecProviderCapabilitySourceNodeSpec- store/admin protocols
If a future source needs a family-specific operation, put it behind that provider package or admin surface. Do not add source-family-specific branches to workflow run execution.
First Implementation Direction
Recommended next implementation slice:
- Add
PythonSourceConfigtowf_configas a taggedserver.sourcesentry. - Create
wf_sources_pythonwith a loader formodule:objectregistries. - Project loaded
NodeSpecs intoCapabilitySource. - Wire local/static
WorkflowServerconstruction to include Python sources. - Prove
wf cap list,wf cap call, and deploymentrun startwork without importingwf_mcp.
This slice should not implement source registry mutation for Python yet. Static config first proves the provider shape. Mutable registry/apply can follow once the provider interface is real.
Open Questions
- Should generic source registry entries live in
wf_api,wf_server, or a newwf_sourcespackage? - Should provider managers be composed by
wf_server.configor by a separate source runtime package? - How much hot-reload behavior should Python sources support in development?
- What are the safe defaults for HTTP timeouts, retries, and large response handling?