Files
lda-wf/docs/historical/superpowers/plans/2026-06-05-rpc-transport-config-boundary-cleanup.md
T

15 KiB

RPC Transport Config Boundary Cleanup 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: Remove direct wf_mcp imports from wf_transport_rpc_http now that neutral wf_config.server.sources[] can describe MCP sources.

Architecture: Keep JSON-RPC transport modules focused on serving a WorkflowServer. Move config-to-server composition into a server-layer module that can select local/static or MCP-backed composition by config source kind. wf_server.context remains MCP-free; the new config composition module is the explicit boundary for source-provider selection.

Tech Stack: Typer CLI, wf_config.WorkflowConfigFile, wf_server.WorkflowServer, existing MCP-backed builder in wf_mcp.broker.server, AST import-direction tests.


File Structure

  • Create src/wf_server/config.py: server composition from neutral workflow config and legacy MCP config path.
  • Modify src/wf_transport_rpc_http/cli.py: import server composition helpers from wf_server.config, not wf_mcp.broker.
  • Modify tests/wf_transport_rpc_http/test_cli.py: monkeypatch new helper paths.
  • Modify tests/wf_transport_rpc_http/test_import_direction.py: no change expected; it should pass once direct imports are removed.
  • Create tests/wf_server/test_config_composition.py: cover local/static and MCP-source selection.
  • Modify docs after implementation.

Current Context

src/wf_transport_rpc_http/cli.py currently imports:

from wf_mcp.broker import (
    build_workflow_server_from_config,
    build_workflow_server_from_workflow_config,
    load_broker_config,
)

That makes tests/wf_transport_rpc_http/test_import_direction.py fail:

def test_wf_transport_rpc_http_imports_no_wfmcp_modules() -> None:
    ...
    assert violations == []

Do not remove the test. The transport package should not import MCP modules directly.

src/wf_server/context.py has a narrower guard in tests/wf_server/test_local_static_server.py that only checks context.py for WfMcpService. Keep context.py untouched.

Task 1: Add Server Composition Tests

Files:

  • Create: tests/wf_server/test_config_composition.py

  • Step 1: Create test file

Create tests/wf_server/test_config_composition.py:

from __future__ import annotations

from wf_config import WorkflowConfigFile
from wf_server.config import (
    build_workflow_server_from_legacy_mcp_config,
    build_workflow_server_from_workflow_config,
)
from wf_server.context import WorkflowServer


def test_build_workflow_server_from_workflow_config_uses_local_static_for_no_mcp_sources(
    tmp_path,
) -> None:
    config = WorkflowConfigFile.model_validate(
        {
            "version": 1,
            "server": {
                "store": {"kind": "filesystem", "root": str(tmp_path / "store")},
                "sources": [{"kind": "stdlib", "id": "wf.std"}],
            },
        }
    )

    server = build_workflow_server_from_workflow_config(config)

    assert isinstance(server, WorkflowServer)
    assert server.config.store_root == tmp_path / "store"
    assert server.source_registry_admin is None


def test_build_workflow_server_from_workflow_config_uses_mcp_builder_for_mcp_sources(
    monkeypatch, tmp_path
) -> None:
    captured = {}

    def fake_builder(config):
        captured["source_kinds"] = [source.kind for source in config.server.sources]
        return "mcp-server"

    monkeypatch.setattr(
        "wf_server.config._build_mcp_workflow_server_from_workflow_config",
        fake_builder,
    )
    config = WorkflowConfigFile.model_validate(
        {
            "version": 1,
            "server": {
                "store": {"kind": "filesystem", "root": str(tmp_path / "store")},
                "sources": [
                    {
                        "kind": "mcp",
                        "id": "everything.default",
                        "provider": "everything",
                        "account": "default",
                        "transport": {"kind": "stdio", "command": "uvx"},
                    }
                ],
            },
        }
    )

    server = build_workflow_server_from_workflow_config(config)

    assert server == "mcp-server"
    assert captured["source_kinds"] == ["mcp"]


def test_build_workflow_server_from_legacy_mcp_config_delegates_to_mcp_builder(
    monkeypatch, tmp_path
) -> None:
    captured = {}

    def fake_builder(path):
        captured["path"] = path
        return "legacy-mcp-server"

    monkeypatch.setattr(
        "wf_server.config._build_mcp_workflow_server_from_legacy_config",
        fake_builder,
    )
    legacy_path = tmp_path / "wf_mcp.config.json"
    legacy_path.write_text('{"store_root": "store", "connections": []}', encoding="utf-8")

    server = build_workflow_server_from_legacy_mcp_config(legacy_path)

    assert server == "legacy-mcp-server"
    assert captured["path"] == legacy_path
  • Step 2: Run tests and verify failure

Run:

uv run pytest tests/wf_server/test_config_composition.py -q

Expected: fail because wf_server.config does not exist.

Task 2: Implement Server Config Composition Module

Files:

  • Create: src/wf_server/config.py

  • Step 1: Create module

Create src/wf_server/config.py:

from __future__ import annotations

from pathlib import Path

from wf_config import FilesystemStoreConfig, WorkflowConfigFile

from .context import WorkflowServer, build_local_static_workflow_server


def _has_mcp_sources(config: WorkflowConfigFile) -> bool:
    return any(getattr(source, "kind", None) == "mcp" for source in config.server.sources)


def _build_mcp_workflow_server_from_workflow_config(
    config: WorkflowConfigFile,
) -> WorkflowServer:
    """Build an MCP-backed server from neutral config.

    This import is intentionally isolated here: transport packages should not
    import MCP modules, while this server composition boundary is allowed to
    select source-provider implementations by source kind.
    """
    from wf_mcp.broker import build_workflow_server_from_workflow_config

    return build_workflow_server_from_workflow_config(config)


def _build_mcp_workflow_server_from_legacy_config(path: Path) -> WorkflowServer:
    """Build an MCP-backed server from legacy broker config."""
    from wf_mcp.broker import build_workflow_server_from_config, load_broker_config

    return build_workflow_server_from_config(load_broker_config(path))


def build_workflow_server_from_workflow_config(
    config: WorkflowConfigFile,
) -> WorkflowServer:
    """Build a WorkflowServer from neutral workflow config.

    Local/static configs use built-in sources. Configs with `kind: "mcp"`
    sources delegate to the MCP provider adapter.
    """
    if _has_mcp_sources(config):
        return _build_mcp_workflow_server_from_workflow_config(config)
    store = config.server.store
    if not isinstance(store, FilesystemStoreConfig):
        raise ValueError("wf-rpc-server currently requires filesystem store")
    return build_local_static_workflow_server(store.root)


def build_workflow_server_from_legacy_mcp_config(path: str | Path) -> WorkflowServer:
    """Build a WorkflowServer from legacy wf_mcp.config.json.

    Prefer neutral `wf_config` for new setups. This compatibility hook keeps the
    transport CLI free of direct MCP imports while existing users migrate.
    """
    return _build_mcp_workflow_server_from_legacy_config(Path(path))
  • Step 2: Run tests

Run:

uv run pytest tests/wf_server/test_config_composition.py -q

Expected: pass.

  • Step 3: Run import-boundary check

Run:

uv run pytest tests/wf_server/test_local_static_server.py::test_wf_server_context_imports_no_wfmcp_service -q

Expected: pass because context.py was not changed.

Task 3: Update RPC CLI to Use Server Composition Helpers

Files:

  • Modify: src/wf_transport_rpc_http/cli.py

  • Modify: tests/wf_transport_rpc_http/test_cli.py

  • Step 1: Update imports

Replace:

from wf_mcp.broker import (
    build_workflow_server_from_config,
    build_workflow_server_from_workflow_config,
    load_broker_config,
)

from wf_server import build_local_static_workflow_server

with:

from wf_server.config import (
    build_workflow_server_from_legacy_mcp_config,
    build_workflow_server_from_workflow_config,
)
from wf_server.context import build_local_static_workflow_server
  • Step 2: Update legacy mcp config branch

Replace:

    if mcp_config is not None:
        broker_config = load_broker_config(mcp_config)
        server = build_workflow_server_from_config(broker_config)

with:

    if mcp_config is not None:
        server = build_workflow_server_from_legacy_mcp_config(mcp_config)
  • Step 3: Remove duplicated mcp source detection from CLI

Inside the if config is not None: block, remove:

        has_mcp_sources = any(
            getattr(source, "kind", None) == "mcp"
            for source in workflow_config.server.sources
        )
        if server is None and has_mcp_sources:
            server = build_workflow_server_from_workflow_config(workflow_config)

Then replace the local/static store handling block:

        store = workflow_config.server.store
        if server is None and not isinstance(store, FilesystemStoreConfig):
            raise typer.BadParameter(
                "wf-rpc-server currently requires filesystem store"
            )
        if server is None:
            resolved_store_root = resolved_store_root or store.root

with:

        store = workflow_config.server.store
        if server is None and store_root is None:
            server = build_workflow_server_from_workflow_config(workflow_config)
        elif server is None:
            if not isinstance(store, FilesystemStoreConfig):
                raise typer.BadParameter(
                    "wf-rpc-server currently requires filesystem store"
                )
            resolved_store_root = resolved_store_root or store.root

Keep --store-root as an override for local/static configs. --store-root is still forbidden with --mcp-config; do not add new behavior there.

  • Step 4: Update tests monkeypatch paths

In tests/wf_transport_rpc_http/test_cli.py, update existing monkeypatches to match the new boundary.

For test_rpc_server_cli_uses_configured_store_and_transport, stop monkeypatching build_local_static_workflow_server; the CLI now delegates config-based server composition to build_workflow_server_from_workflow_config.

Replace:

def fake_build_server(root):
    captured["store_root"] = root
    return object()

with:

def fake_build_server(config):
    captured["store_root"] = config.server.store.root
    return object()

Replace this monkeypatch:

monkeypatch.setattr(
    "wf_transport_rpc_http.cli.build_local_static_workflow_server",
    fake_build_server,
)

with:

monkeypatch.setattr(
    "wf_transport_rpc_http.cli.build_workflow_server_from_workflow_config",
    fake_build_server,
)

Keep the assertion:

assert captured["store_root"] == (tmp_path / ".wf_store").resolve()

For legacy MCP config tests, replace monkeypatch paths:

"wf_transport_rpc_http.cli.build_workflow_server_from_config"
"wf_transport_rpc_http.cli.build_workflow_server_from_workflow_config"

with:

"wf_transport_rpc_http.cli.build_workflow_server_from_legacy_mcp_config"
"wf_transport_rpc_http.cli.build_workflow_server_from_workflow_config"

If a test currently monkeypatches load_broker_config, remove that monkeypatch and have the fake legacy builder capture the config path directly.

For test_rpc_server_cli_uses_mcp_config_server, replace the two fake helpers:

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()

with one helper:

def fake_build_mcp_server(path):
    captured["mcp_config_path"] = path
    return object()

Replace the two monkeypatches:

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,
)

with:

monkeypatch.setattr(
    "wf_transport_rpc_http.cli.build_workflow_server_from_legacy_mcp_config",
    fake_build_mcp_server,
)

Remove the assertion:

assert captured["mcp_config"] == "broker-config"
  • Step 5: Run CLI tests

Run:

uv run pytest tests/wf_transport_rpc_http/test_cli.py -q

Expected: pass.

Task 4: Restore Transport Import-Direction Guard

Files:

  • Test: tests/wf_transport_rpc_http/test_import_direction.py

  • Step 1: Run guard

Run:

uv run pytest tests/wf_transport_rpc_http/test_import_direction.py -q

Expected: pass. If it fails, inspect src/wf_transport_rpc_http for any remaining wf_mcp imports and remove them.

  • Step 2: Run wider RPC subset

Run:

uv run pytest tests/wf_transport_rpc_http tests/wf_cli/test_remote_target.py -q

Expected: pass. The prior known failure should be gone.

Task 5: Update Docs and Mark Cleanup Complete

Files:

  • Modify: docs/current_roadmap.md

  • Modify: docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md

  • Step 1: Update roadmap

In docs/current_roadmap.md, under "Transport package boundary cleanup", append:

    Completed: `wf_transport_rpc_http` no longer imports `wf_mcp`; server
    composition from neutral or legacy MCP config lives behind `wf_server.config`.
  • Step 2: Update long-lived API spec

In docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md, under "Transport package boundary cleanup", append:

   Completed when `tests/wf_transport_rpc_http/test_import_direction.py` passes
   and the RPC transport CLI imports only `wf_config`, `wf_server`, and transport
   modules for server construction.

Task 6: Final Verification and Commit

Files:

  • All touched files.

  • Step 1: Run focused tests

Run:

uv run pytest tests/wf_server/test_config_composition.py tests/wf_transport_rpc_http tests/wf_cli/test_remote_target.py -q

Expected: pass.

  • Step 2: Run lint/type checks

Run:

uv run ruff check src/wf_server src/wf_transport_rpc_http tests/wf_server tests/wf_transport_rpc_http
uv run basedpyright --level error src/wf_server src/wf_transport_rpc_http tests/wf_server tests/wf_transport_rpc_http

Expected: pass with 0 errors.

  • Step 3: Commit

Run:

git add src/wf_server src/wf_transport_rpc_http tests/wf_server tests/wf_transport_rpc_http docs/current_roadmap.md docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md
git commit -m "refactor: isolate rpc server config composition"

Self-Review Checklist

  • src/wf_transport_rpc_http contains no direct wf_mcp imports.
  • src/wf_server/context.py remains MCP-free.
  • The MCP-specific import is isolated in src/wf_server/config.py with a docstring explaining the boundary.
  • wf-rpc-server --config still works for local/static and MCP-source neutral configs.
  • wf-rpc-server --mcp-config still works as a legacy compatibility path.