215 lines
7.9 KiB
Markdown
215 lines
7.9 KiB
Markdown
# Source Architecture
|
|
|
|
This map explains how workflow capability sources fit together. Use it when
|
|
deciding where to add a new source family or when untangling `wf_sources_mcp`
|
|
from the older `wf_mcp` compatibility package.
|
|
|
|
## Short Version
|
|
|
|
```text
|
|
wf_authoring
|
|
creates NodeSpec, reducers, and authored workflows
|
|
|
|
wf_platform
|
|
defines neutral source DTOs:
|
|
CapabilitySource
|
|
CapabilityBuckets
|
|
SourceVisibility
|
|
SourcePermissions
|
|
|
|
wf_api
|
|
consumes source DTOs through WorkflowSpecProvider
|
|
owns built-in local sources:
|
|
wf.std
|
|
wf.recipes
|
|
|
|
wf_sources_mcp
|
|
implements MCP as an upstream source provider
|
|
|
|
wf_sources_python
|
|
implements trusted in-process Python source loading
|
|
|
|
wf_server
|
|
composes configured providers into WorkflowServer
|
|
|
|
wf_transport_*
|
|
exposes WorkflowServer over protocols
|
|
```
|
|
|
|
The workflow API should not care whether a capability came from MCP, Python,
|
|
OpenAPI, or a built-in source. It should see `CapabilitySource` and executable
|
|
`NodeSpec` objects.
|
|
|
|
## Package Responsibilities
|
|
|
|
| Package | Owns | Does not own |
|
|
| --- | --- | --- |
|
|
| `wf_authoring` | `@node`, `NodeSpec`, builder DSL, reducers, reusable authored ops. | Server config, source loading, MCP sessions. |
|
|
| `wf_platform` | Neutral source DTOs and source visibility/permission metadata. | Provider-specific loading or execution. |
|
|
| `wf_api` | Application operations over capabilities, drafts, artifacts, deployments, and runs. Built-in `wf.std` / `wf.recipes` sources. | MCP/Python/OpenAPI source-specific behavior. |
|
|
| `wf_sources_mcp` | MCP source ids, connection models, auth/catalog stores, discovery, SDK facade, persistent runtime pool, converters, wrappers. | MCP frontend/proxy compatibility, durable server composition. |
|
|
| `wf_sources_python` | Trusted Python module registry loading and projection to `CapabilitySource`. | Authoring primitives, registry mutation/apply, sandboxing. |
|
|
| `wf_server` | `WorkflowServer` composition from config/store/source providers. | JSON-RPC method definitions, MCP protocol frontend. |
|
|
| `wf_transport_rpc_http` | JSON-RPC HTTP app/client around an existing `WorkflowServer`. | Server startup policy, source-provider composition. |
|
|
| `wf_mcp` | Legacy/special-purpose MCP frontend, broker glue, proxy, compatibility shims. | New durable product behavior unless explicitly retiring old callers. |
|
|
|
|
## Data Flow
|
|
|
|
```text
|
|
wf_config.server.sources[]
|
|
-> wf_server.config selects source providers
|
|
-> provider loads live source inventory
|
|
-> CapabilitySource map
|
|
-> WorkflowSpecProvider
|
|
-> WorkflowApi / WorkflowServer
|
|
-> transport or CLI
|
|
```
|
|
|
|
The first shared provider seam is intentionally static:
|
|
|
|
```python
|
|
class WorkflowSourceProvider(Protocol):
|
|
def load_sources(self) -> Mapping[str, CapabilitySource]: ...
|
|
```
|
|
|
|
This covers source families that can project configured inventory into
|
|
workflow-facing `CapabilitySource` objects. Provider-specific runtime pools,
|
|
admin/apply hooks, auth, catalog caches, and live health checks stay outside
|
|
this narrow seam until a source family needs them.
|
|
|
|
## Capability, Tool, Resource, And Prompt
|
|
|
|
Use these terms precisely:
|
|
|
|
- A **source** is the owner and namespace. Examples: `everything.default`,
|
|
`wf.std`, `wf.source`, or a future OpenAPI/Python source.
|
|
- A **tool** is provider-native. For MCP, it is an MCP tool discovered from an
|
|
upstream server.
|
|
- A **workflow capability** is workflow-native. It is the `NodeSpec` shape a
|
|
graph can call with typed input, typed output, outcomes, validation, and trace
|
|
behavior. Tools can be projected into workflow capabilities, but the two are
|
|
not identical concepts.
|
|
- A **resource** is source-owned addressable content. The URI is not globally
|
|
meaningful by itself; it must be interpreted with the source that owns it.
|
|
- A **prompt** is source-owned prompt/template inventory. Listing prompts is
|
|
inventory; rendering a prompt is an upstream operation and may be stateful.
|
|
|
|
Saved workflow data should prefer logical source references over concrete
|
|
source ids. For example, a resource ref should store:
|
|
|
|
```json
|
|
{"logical_source": "drive", "uri": "gdrive://file/abc"}
|
|
```
|
|
|
|
The deployment binding decides whether `drive` means `drive.personal`,
|
|
`drive.work`, or another concrete source. Platform sources such as `wf.std` and
|
|
`wf.source` are special because their logical source id is also their concrete
|
|
source id, so they do not require deployment bindings. Legacy explicit
|
|
self-bindings such as `wf.std=wf.std` are accepted as no-op compatibility, but
|
|
new deployments should omit them. Validation rejects non-self platform bindings
|
|
such as `wf.std=custom.std` as stale or misleading configuration.
|
|
|
|
Runtime dereference is explicit. Passing a resource ref by value does not fetch
|
|
content. A helper capability such as `wf.source.read_resource` receives the ref,
|
|
uses runtime/platform context to resolve the logical source, and applies bounded
|
|
output policy before returning text into workflow state/output.
|
|
|
|
Prompt rendering uses the same source ownership rules, but it is not yet exposed
|
|
as a workflow helper source. Keep prompt support at inventory/inspection level
|
|
until there is a concrete graph use case, argument schema contract, and bounded
|
|
output policy equivalent to `wf.source.read_resource`.
|
|
|
|
For MCP, the provider also owns stateful upstream sessions:
|
|
|
|
```text
|
|
McpSourceConnection
|
|
-> McpRuntimePool
|
|
-> McpSourceClient
|
|
-> MCP ClientSession
|
|
```
|
|
|
|
For Python, the provider is simpler:
|
|
|
|
```text
|
|
PythonSourceConfig(path, module, registry)
|
|
-> PythonSourceProvider
|
|
-> import module
|
|
-> load NodeSpec registry
|
|
-> qualify specs under source id
|
|
-> CapabilitySource(kind="python")
|
|
```
|
|
|
|
For a concrete operator flow, see the
|
|
[`Python source runbook`](runbooks/python-source.md). It covers writing `ops.py`,
|
|
configuring `path`/`module`/`registry`, validating the config, calling a
|
|
capability, and running a saved workflow deployment.
|
|
|
|
## Built-Ins Versus Configured Sources
|
|
|
|
`wf.std` and `wf.recipes` are built-in local sources owned by `wf_api.local_sources`.
|
|
They are always platform-versioned content.
|
|
|
|
Configured sources are explicit server/operator choices:
|
|
|
|
- `kind: "mcp"`: upstream MCP server capabilities.
|
|
- `kind: "python"`: trusted in-process project capabilities.
|
|
- future `kind: "openapi"`: HTTP/OpenAPI operations.
|
|
|
|
Do not move `wf.std` or `wf.recipes` into `wf_sources_python`. They are not
|
|
operator-configured project sources.
|
|
|
|
Generated draft workflows may still use built-in helper sources such as
|
|
`wf.std`. Platform sources do not need deployment bindings, so deployment
|
|
examples should bind configured sources only. If validation reports a platform
|
|
source binding, remove it unless it is an old self-binding kept only for
|
|
compatibility.
|
|
|
|
## `wf_sources_mcp` Internal Layers
|
|
|
|
`wf_sources_mcp` is clearer if read from bottom to top:
|
|
|
|
```text
|
|
ids / transports / connections
|
|
source identity and MCP connection description
|
|
|
|
auth / storage
|
|
source auth records and catalog cache files
|
|
|
|
client
|
|
one live MCP ClientSession facade
|
|
|
|
runtime
|
|
persistent session pool for stateful upstream operations
|
|
|
|
sdk
|
|
one-shot adapter, operation protocols, and MCP-to-workflow converters
|
|
|
|
catalog / discovery / tool_wrappers
|
|
turn MCP tools/resources/prompts into workflow-facing source inventories
|
|
```
|
|
|
|
`wf_mcp` may still re-export or adapt some of this while old callers exist. New
|
|
durable server/source work should prefer `wf_sources_mcp` directly.
|
|
|
|
## Adding A New Source Family
|
|
|
|
Start with a provider package:
|
|
|
|
```text
|
|
src/wf_sources_<kind>/
|
|
__init__.py
|
|
loader.py or provider.py
|
|
tests/
|
|
```
|
|
|
|
Then add:
|
|
|
|
1. A `wf_config` discriminated-union source model.
|
|
2. A provider loader that returns `CapabilitySource`.
|
|
3. `wf_server.config` composition for that source kind.
|
|
4. Tests proving `cap list`, `cap call`, and one workflow run path.
|
|
5. Docs stating which parts are static, mutable, reloadable, or deferred.
|
|
|
|
Do not add source-family branches inside `wf_api` run execution. Source-specific
|
|
logic belongs in the provider package or server composition layer.
|