13 KiB
RPC Server MCP Config Hookup Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Let wf-rpc-server serve a real MCP-backed WorkflowServer from an MCP broker config file, so wf --url ... can target a long-lived server with MCP sources and source-registry/admin surfaces.
Architecture: Keep JSON-RPC transport code as the place that selects a server composition for the process. --mcp-config loads legacy MCP broker config and calls wf_mcp.broker.build_workflow_server_from_config; existing --store-root / neutral --config behavior continues to build the local/static server. The transport still receives only a neutral WorkflowServer and calls create_rpc_app(server).
Tech Stack: Python 3.14, Typer, wf_transport_rpc_http, wf_mcp.broker, wf_server, pytest, ruff, basedpyright.
Current Context
Implemented before this plan:
wf_mcp.broker.server.build_workflow_server_from_config(config)returns a neutralWorkflowServer.wf_transport_rpc_http.cli.serve()currently always callsbuild_local_static_workflow_server(...).- Existing CLI tests in
tests/wf_transport_rpc_http/test_cli.pymonkeypatch server construction,create_rpc_app, anduvicorn.run; use that pattern.
Out of scope:
- No new neutral config schema for MCP source transports.
- No server hot reload.
- No process manager/daemon work.
- No auth redesign.
- No real socket startup in tests.
File Structure
- Modify
src/wf_transport_rpc_http/cli.py- Add
--mcp-config. - Select MCP-backed server when
--mcp-configis supplied. - Keep local/static path unchanged for existing config/store-root flows.
- Add
- Modify
tests/wf_transport_rpc_http/test_cli.py- Add help assertion for
--mcp-config. - Add server-selection tests.
- Add help assertion for
- Modify
docs/wf_cli.md- Document local/static server and MCP-backed server startup examples.
- Modify
docs/current_roadmap.md- Mark RPC server CLI MCP config hookup complete.
- Modify
docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md- Record Slice 5 status.
Task 1: Add MCP Config Server Selection to RPC CLI
Files:
-
Modify:
src/wf_transport_rpc_http/cli.py -
Modify:
tests/wf_transport_rpc_http/test_cli.py -
Step 1: Add failing help and selection tests
In tests/wf_transport_rpc_http/test_cli.py, update test_rpc_server_cli_help_mentions_store_root:
def test_rpc_server_cli_help_mentions_store_root() -> None:
result = CliRunner().invoke(app, ["--help"])
assert result.exit_code == 0
assert "--store-root" in result.output
assert "--mcp-config" in result.output
assert "--host" in result.output
assert "--port" in result.output
Add:
def test_rpc_server_cli_uses_mcp_config_server(monkeypatch, tmp_path) -> None:
config_path = tmp_path / "wf_mcp.config.json"
config_path.write_text(
json.dumps(
{
"store_root": str(tmp_path / "store"),
"connections": [],
}
),
encoding="utf-8",
)
captured: dict[str, object] = {}
def fake_load_broker_config(path):
captured["mcp_config_path"] = path
return "broker-config"
def fake_build_mcp_server(config):
captured["mcp_config"] = config
return object()
def fake_create_rpc_app(server, *, rpc_path="/rpc"):
captured["server"] = server
captured["rpc_path"] = rpc_path
return object()
def fake_uvicorn_run(app_obj, *, host, port, access_log):
captured["app"] = app_obj
captured["host"] = host
captured["port"] = port
captured["access_log"] = access_log
monkeypatch.setattr("wf_transport_rpc_http.cli.load_broker_config", fake_load_broker_config)
monkeypatch.setattr(
"wf_transport_rpc_http.cli.build_workflow_server_from_config",
fake_build_mcp_server,
)
monkeypatch.setattr("wf_transport_rpc_http.cli.create_rpc_app", fake_create_rpc_app)
monkeypatch.setattr("wf_transport_rpc_http.cli.uvicorn.run", fake_uvicorn_run)
result = CliRunner().invoke(
app,
[
"--mcp-config",
str(config_path),
"--host",
"127.0.0.9",
"--port",
"9988",
],
)
assert result.exit_code == 0, result.output
assert captured["mcp_config_path"] == config_path
assert captured["mcp_config"] == "broker-config"
assert captured["server"] is not None
assert captured["rpc_path"] == "/rpc"
assert captured["host"] == "127.0.0.9"
assert captured["port"] == 9988
assert captured["access_log"] is False
Add conflict test:
def test_rpc_server_cli_rejects_mcp_config_with_store_root(tmp_path) -> None:
config_path = tmp_path / "wf_mcp.config.json"
config_path.write_text(
json.dumps({"store_root": str(tmp_path / "store"), "connections": []}),
encoding="utf-8",
)
result = CliRunner().invoke(
app,
[
"--mcp-config",
str(config_path),
"--store-root",
str(tmp_path / "other"),
],
)
assert result.exit_code != 0
assert "--mcp-config cannot be combined with --store-root" in result.output
Run:
uv run pytest tests/wf_transport_rpc_http/test_cli.py -q
Expected: FAIL because --mcp-config is not implemented.
- Step 2: Implement
--mcp-configoption
In src/wf_transport_rpc_http/cli.py, import:
from wf_mcp.broker import build_workflow_server_from_config, load_broker_config
Add option to serve(...):
mcp_config: Path | None = typer.Option(
None,
"--mcp-config",
help="Path to MCP broker config JSON for MCP-backed workflow server.",
),
Update docstring:
"""Serve WorkflowApi over JSON-RPC HTTP."""
Before resolving local/static store root, add validation:
if mcp_config is not None and store_root is not None:
raise typer.BadParameter("--mcp-config cannot be combined with --store-root")
If mcp_config is supplied, build MCP server:
server = None
if mcp_config is not None:
broker_config = load_broker_config(mcp_config)
server = build_workflow_server_from_config(broker_config)
Then keep existing neutral --config parsing for host/port/path. When selecting
the final server, only call build_local_static_workflow_server(...) if
server is None:
if server is None:
if resolved_store_root is None:
raise typer.BadParameter(
"--store-root is required when --config is not supplied"
)
server = build_local_static_workflow_server(resolved_store_root)
Important:
-
--configmay still be used with--mcp-configfor transport host/port/path. -
--mcp-configowns the workflow server/store/source side. -
--store-rootis local/static-only and must conflict with--mcp-config. -
Step 3: Run CLI tests
Run:
uv run pytest tests/wf_transport_rpc_http/test_cli.py -q
Expected: PASS.
- Step 4: Commit
git add src/wf_transport_rpc_http/cli.py tests/wf_transport_rpc_http/test_cli.py
git commit -m "feat: serve mcp backed rpc server"
Task 2: Prove MCP Config Server Supports Registry RPC Through CLI Path
Files:
-
Modify:
tests/wf_transport_rpc_http/test_cli.py -
Step 1: Add integration-style construction test
Add this test to tests/wf_transport_rpc_http/test_cli.py:
def test_rpc_server_cli_mcp_config_builds_registry_capable_server(monkeypatch, tmp_path) -> None:
config_path = tmp_path / "wf_mcp.config.json"
config_path.write_text(
json.dumps(
{
"store_root": str(tmp_path / "store"),
"connections": [],
}
),
encoding="utf-8",
)
captured: dict[str, object] = {}
def fake_create_rpc_app(server, *, rpc_path="/rpc"):
captured["source_registry_admin"] = server.source_registry_admin
captured["rpc_path"] = rpc_path
return object()
def fake_uvicorn_run(app_obj, *, host, port, access_log):
captured["host"] = host
captured["port"] = port
monkeypatch.setattr("wf_transport_rpc_http.cli.create_rpc_app", fake_create_rpc_app)
monkeypatch.setattr("wf_transport_rpc_http.cli.uvicorn.run", fake_uvicorn_run)
result = CliRunner().invoke(app, ["--mcp-config", str(config_path)])
assert result.exit_code == 0, result.output
assert captured["source_registry_admin"] is not None
assert captured["rpc_path"] == "/rpc"
assert captured["host"] == "127.0.0.1"
assert captured["port"] == 8765
This test uses the real load_broker_config() and
build_workflow_server_from_config() but still avoids starting uvicorn.
- Step 2: Run CLI tests
Run:
uv run pytest tests/wf_transport_rpc_http/test_cli.py -q
Expected: PASS.
- Step 3: Commit
git add tests/wf_transport_rpc_http/test_cli.py
git commit -m "test: cover mcp config rpc server path"
Task 3: Update User-Facing Docs and Roadmap
Files:
-
Modify:
docs/wf_cli.md -
Modify:
docs/current_roadmap.md -
Modify:
docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md -
Step 1: Add RPC server startup docs
In docs/wf_cli.md, after the opening config paragraph, add:
## Remote Server
Start a local/static JSON-RPC workflow server:
```bash
wf-rpc-server --store-root .wf_store --host 127.0.0.1 --port 8765
Start a JSON-RPC server backed by MCP broker config and MCP-capable sources:
wf-rpc-server --mcp-config wf_mcp.config.json --host 127.0.0.1 --port 8765
Then point wf at it:
wf --url http://127.0.0.1:8765/rpc cap list
wf --url http://127.0.0.1:8765/rpc admin registry list
--mcp-config owns the server's workflow stores, MCP connections, and source
registry. --store-root is for the local/static server path and cannot be
combined with --mcp-config.
- [ ] **Step 2: Update roadmap**
In `docs/current_roadmap.md`, under **Durable API service shape**, add:
```markdown
- Completed: `wf-rpc-server --mcp-config wf_mcp.config.json` starts the
JSON-RPC transport over an MCP-backed `WorkflowServer`, making the remote
CLI path usable with MCP sources and desired source registry operations.
- Step 3: Update long-lived API spec
In docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md,
update status from "Slices 1-4 implemented" to "Slices 1-5 implemented", and
add under implementation status:
- Slice 5 complete: `wf-rpc-server --mcp-config <path>` starts JSON-RPC over an
MCP-backed `WorkflowServer`; `--store-root` remains local/static-only.
- Step 4: Run docs grep
Run:
rg -n "wf-rpc-server --mcp-config|Slices 1-5 implemented|--store-root.*--mcp-config" docs/wf_cli.md docs/current_roadmap.md docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md
Expected: all three docs mention the new server path.
- Step 5: Commit
git add docs/wf_cli.md docs/current_roadmap.md docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md
git commit -m "docs: document mcp backed rpc server"
Task 4: Final Verification
Files:
-
Verify only.
-
Step 1: Run focused tests
Run:
uv run pytest tests/wf_transport_rpc_http/test_cli.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py tests/wf_mcp/test_mcp_workflow_server.py -q
Expected: all tests pass.
- Step 2: Run lint and type checks
Run:
uv run ruff check src/wf_transport_rpc_http/cli.py tests/wf_transport_rpc_http/test_cli.py
uv run basedpyright --level error src/wf_transport_rpc_http/cli.py tests/wf_transport_rpc_http/test_cli.py
git diff --check
Expected: all commands exit 0. CRLF warnings from Git are acceptable; whitespace errors are not.
- Step 3: Final report
Report:
- changed files
- verification output
- exact command a user can run for MCP-backed RPC server
- any deviations from this plan
Do not run the full suite unless the focused verification is green.
Self-Review
- Product fit: this is the smallest visible hook after MCP-backed
WorkflowServerconstruction. - Boundary:
wf_transport_rpc_http.cliimportswf_mcp.brokeronly for process startup selection; JSON-RPC app/method modules still take a neutralWorkflowServer. - Config semantics:
--mcp-configand--store-rootconflict because they own different server composition paths. - Testing: no real socket startup; tests monkeypatch
uvicorn.run.