Files
lda-wf/docs/historical/superpowers/plans/2026-06-08-wf-sources-mcp-broker-dto-conversion-boundary.md
T

15 KiB

MCP Broker DTO Conversion Boundary 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 wf_mcp.ConnectionConfig imports from wf_sources_mcp by making source-side conversion structural and moving broker DTO construction to wf_mcp.

Architecture: wf_sources_mcp owns source-provider DTOs and may adapt legacy connection-like inputs by structural protocol. It must not construct broker runtime DTOs. wf_mcp.source_registry becomes the compatibility/broker conversion module: it re-exports canonical source registry models and owns helpers that produce ConnectionConfig.

Tech Stack: Python 3.14, structural Protocol, dataclasses in tests, pytest, ruff, basedpyright, AST import guards.


Why This Slice Exists

After the ID cleanup, the remaining real wf_sources_mcp -> wf_mcp dependencies are legacy broker DTO conversions:

  • wf_sources_mcp.connections imports wf_mcp.broker.models.ConnectionConfig for type checking.
  • wf_sources_mcp.source_registry imports wf_mcp.models.ConnectionConfig at runtime to construct broker configs.

Those conversions are compatibility edges. wf_sources_mcp should own MCP source objects, not broker runtime objects.


Hard Boundaries

  • Do not move ConnectionConfig itself in this slice.
  • Do not change ConnectionConfig fields or behavior.
  • Do not change source registry JSON shape.
  • Do not change McpSourceRegistryEntry fields.
  • Do not remove wf_mcp.source_registry compatibility imports.
  • Do not import wf_mcp from any src/wf_sources_mcp/*.py file.
  • Keep existing broker call sites working.
  • Do not commit unless the caller explicitly asks for a commit.

File Map

  • Modify src/wf_sources_mcp/connections.py: replace ConnectionConfig type import with structural protocol.
  • Modify src/wf_sources_mcp/source_registry.py: keep canonical models/store and input-only conversion; remove broker DTO construction helpers.
  • Modify src/wf_mcp/source_registry.py: re-export canonical models/store and define broker DTO construction helpers.
  • Modify src/wf_mcp/broker/config.py: import broker DTO construction helpers from wf_mcp.source_registry.
  • Modify src/wf_mcp/broker/service/connection_service.py: import broker DTO construction helpers from wf_mcp.source_registry.
  • Modify tests:
    • tests/wf_sources_mcp/test_connections.py
    • tests/wf_sources_mcp/test_source_registry.py
    • tests/wf_mcp/test_source_registry.py
    • tests/wf_sources_mcp/test_import_direction_guard.py
  • Modify docs:
    • docs/current_roadmap.md
    • docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md
  • Move this plan to docs/historical/superpowers/plans/ after implementation is verified.

Task 1: Make Legacy Connection Input Structural in connections.py

Files:

  • Modify: src/wf_sources_mcp/connections.py

  • Modify: tests/wf_sources_mcp/test_connections.py

  • Step 1: Replace ConnectionConfig type import with protocols

In src/wf_sources_mcp/connections.py, remove:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from wf_mcp.broker.models import ConnectionConfig

Add:

from collections.abc import Mapping
from typing import Protocol


class LegacyConnectionConfigLike(Protocol):
    """Structural shape needed from legacy broker connection configs."""

    id: str
    server: str
    account: str
    enabled: bool
    metadata: Mapping[str, object]

Update signatures:

def mcp_source_connection_from_connection_config(
    connection: LegacyConnectionConfigLike,
) -> McpSourceConnection:
    ...


def _transport_from_connection_metadata(
    connection: LegacyConnectionConfigLike,
) -> SourceTransport | None:
    ...

Add LegacyConnectionConfigLike to __all__.

  • Step 2: Preserve metadata handling

Keep the exact current metadata logic:

  • dict metadata["transport"] supports {"kind": "stdio"} and {"kind": "http"};

  • flat "stdio" metadata supports command, args, env, cwd;

  • flat HTTP aliases support url, headers;

  • missing transport returns None;

  • unsupported transport raises ValueError.

  • Step 3: Add a no-wf_mcp fake test

In tests/wf_sources_mcp/test_connections.py, add:

from dataclasses import dataclass, field
from typing import Any


@dataclass(slots=True)
class _LegacyConnectionLike:
    id: str
    server: str
    account: str
    enabled: bool = True
    metadata: dict[str, object] = field(default_factory=dict)

Add a test using _LegacyConnectionLike with stdio metadata and assert conversion works. Existing tests that import wf_mcp.broker.models.ConnectionConfig may remain temporarily, but at least one canonical test must prove the converter does not need the concrete broker class.

  • Step 4: Run connection tests

Run:

uv run pytest tests/wf_sources_mcp/test_connections.py -q

Expected: pass.


Task 2: Move Broker DTO Construction Helpers to wf_mcp.source_registry

Files:

  • Modify: src/wf_sources_mcp/source_registry.py

  • Modify: src/wf_mcp/source_registry.py

  • Modify: tests/wf_sources_mcp/test_source_registry.py

  • Modify: tests/wf_mcp/test_source_registry.py

  • Step 1: Keep input-only seed conversion in wf_sources_mcp.source_registry

In src/wf_sources_mcp/source_registry.py, remove all wf_mcp.models.ConnectionConfig imports.

Add a structural protocol:

from collections.abc import Mapping
from typing import Protocol


class LegacyConnectionConfigLike(Protocol):
    id: str
    server: str
    account: str
    enabled: bool
    metadata: Mapping[str, object]

Keep connection_config_to_registry_entry(connection: LegacyConnectionConfigLike) -> McpSourceRegistryEntry.

This helper is allowed to stay in wf_sources_mcp because it converts legacy-shaped input into canonical source registry state and does not construct broker DTOs.

  • Step 2: Remove broker-output helpers from canonical __all__

Remove these functions from src/wf_sources_mcp/source_registry.py:

  • registry_entry_to_connection_config
  • workflow_mcp_source_to_connection_config

Remove them from __all__.

  • Step 3: Define broker-output helpers in src/wf_mcp/source_registry.py

Replace the pure shim with a mixed compatibility module:

"""Compatibility and broker conversion helpers for MCP source registry state.

Canonical registry models and stores live in `wf_sources_mcp.source_registry`.
Helpers that construct `ConnectionConfig` stay here because `ConnectionConfig`
is a broker compatibility DTO.
"""

from __future__ import annotations

from wf_sources_mcp.source_registry import (
    FileSourceRegistryStore,
    HttpSourceTransport,
    McpSourceRegistryEntry,
    SourceRegistryFile,
    SourceRegistryStore,
    SourceTransport,
    StdioSourceTransport,
    connection_config_to_registry_entry,
)

from .models import ConnectionConfig


def registry_entry_to_connection_config(entry: McpSourceRegistryEntry) -> ConnectionConfig:
    ...


def workflow_mcp_source_to_connection_config(source: object) -> ConnectionConfig:
    ...

Move the current implementations of registry_entry_to_connection_config and workflow_mcp_source_to_connection_config from wf_sources_mcp.source_registry into this module unchanged except for imports.

Ensure __all__ includes all re-exported canonical names plus the broker-output helpers.

  • Step 4: Move broker-output tests to wf_mcp

In tests/wf_sources_mcp/test_source_registry.py:

  • keep tests for McpSourceRegistryEntry;
  • keep tests for SourceRegistryFile;
  • keep tests for FileSourceRegistryStore;
  • keep tests for connection_config_to_registry_entry, but use a local _LegacyConnectionLike dataclass instead of importing wf_mcp.models.ConnectionConfig;
  • remove tests for registry_entry_to_connection_config;
  • remove tests for workflow_mcp_source_to_connection_config if present.

In tests/wf_mcp/test_source_registry.py:

  • keep or add tests for registry_entry_to_connection_config;

  • keep or add tests for workflow_mcp_source_to_connection_config;

  • assert these helpers return concrete wf_mcp.models.ConnectionConfig.

  • Step 5: Run source registry tests

Run:

uv run pytest tests/wf_sources_mcp/test_source_registry.py tests/wf_mcp/test_source_registry.py -q

Expected: pass.


Task 3: Update Broker Call Sites to Import Broker Conversions

Files:

  • Modify: src/wf_mcp/broker/config.py

  • Modify: src/wf_mcp/broker/service/connection_service.py

  • Step 1: Update imports in broker config

In src/wf_mcp/broker/config.py, import canonical models/stores from wf_sources_mcp.source_registry only when they are pure source registry objects.

Import broker-output helper from wf_mcp.source_registry:

from wf_mcp.source_registry import workflow_mcp_source_to_connection_config

Do not import workflow_mcp_source_to_connection_config from wf_sources_mcp.source_registry.

  • Step 2: Update imports in connection service

In src/wf_mcp/broker/service/connection_service.py:

from wf_mcp.source_registry import (
    connection_config_to_registry_entry,
    registry_entry_to_connection_config,
)

connection_config_to_registry_entry may be re-exported from wf_mcp.source_registry for consistency at broker call sites, even though canonical implementation remains in wf_sources_mcp.source_registry.

  • Step 3: Run broker source registry tests

Run:

uv run pytest tests/wf_mcp/test_workflow_config_bridge.py tests/wf_mcp/service/test_connection_service.py tests/wf_mcp/test_source_registry.py -q

Expected: pass.


Task 4: Add Import Guards for Broker DTO Dependencies

Files:

  • Modify: tests/wf_sources_mcp/test_import_direction_guard.py

  • Step 1: Add forbidden broker DTO import test

Append:

def test_wf_sources_mcp_does_not_import_wf_mcp_broker_dtos() -> None:
    root = Path(__file__).resolve().parents[2] / "src" / "wf_sources_mcp"
    forbidden = {"wf_mcp.models", "wf_mcp.broker.models"}
    violations: list[str] = []

    for py_file in sorted(root.rglob("*.py")):
        rel = py_file.relative_to(root.parent)
        module = str(rel.with_suffix("")).replace("/", ".").replace("\\", ".")
        tree = ast.parse(py_file.read_text(encoding="utf-8"), filename=str(py_file))
        for node in ast.walk(tree):
            if isinstance(node, ast.ImportFrom) and node.module in forbidden:
                violations.append(f"{module}:{node.lineno}: from {node.module} import ...")
            elif isinstance(node, ast.Import):
                for alias in node.names:
                    if alias.name in forbidden:
                        violations.append(f"{module}:{node.lineno}: import {alias.name}")

    assert violations == [], (
        "wf_sources_mcp still imports wf_mcp broker DTO modules:\n"
        + "\n".join(f"  {violation}" for violation in violations)
    )
  • Step 2: Run import guards

Run:

uv run pytest tests/wf_sources_mcp/test_import_direction_guard.py -q

Expected: pass.


Task 5: Update Package Root Exports

Files:

  • Modify: src/wf_sources_mcp/__init__.py

  • Step 1: Remove broker-output helper exports

Remove these names from wf_sources_mcp.__all__ and __getattr__ routing:

  • registry_entry_to_connection_config
  • workflow_mcp_source_to_connection_config

Keep:

  • connection_config_to_registry_entry
  • mcp_source_connection_from_connection_config

Those remaining helpers must be structural/input-only and must not import wf_mcp.

  • Step 2: Run package export tests

Run:

uv run pytest tests/wf_sources_mcp tests/wf_mcp/test_compat_imports.py -q

Expected: pass.

If tests expect broker-output helpers at the wf_sources_mcp package root, update them to import from wf_mcp.source_registry. Do not keep broker-output helpers at the source-provider package root.


Task 6: Update Docs and Archive Plan

Files:

  • Modify: docs/current_roadmap.md

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

  • Move: docs/superpowers/plans/2026-06-08-wf-sources-mcp-broker-dto-conversion-boundary.md to docs/historical/superpowers/plans/2026-06-08-wf-sources-mcp-broker-dto-conversion-boundary.md

  • Step 1: Update roadmap

Under the wf_sources_mcp cleanup section, add:

      Broker DTO construction moved out of `wf_sources_mcp`: source-provider
      modules use structural legacy inputs only, while `wf_mcp.source_registry`
      owns helpers that construct `ConnectionConfig`.
  • Step 2: Update long-lived boundary spec

In docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md, add a completed numbered item after the source ID item:

23. Complete: broker DTO construction removed from `wf_sources_mcp`.
    `wf_sources_mcp` accepts legacy-shaped inputs structurally, while
    `wf_mcp.source_registry` owns helpers that construct `ConnectionConfig`.

Renumber the pending broad item if needed.

  • Step 3: Archive the plan

Run:

git mv docs/superpowers/plans/2026-06-08-wf-sources-mcp-broker-dto-conversion-boundary.md docs/historical/superpowers/plans/2026-06-08-wf-sources-mcp-broker-dto-conversion-boundary.md

Expected: git status --short shows the plan under docs/historical/....


Task 7: Final Verification

Files:

  • No code edits unless verification finds a real issue.

  • Step 1: Run focused tests

Run:

uv run pytest tests/wf_sources_mcp tests/wf_mcp/test_source_registry.py tests/wf_mcp/test_workflow_config_bridge.py tests/wf_mcp/service/test_connection_service.py tests/wf_mcp/test_compat_imports.py -q

Expected: all selected tests pass.

  • Step 2: Run import dependency check

Run:

rg -n "^from wf_mcp|^import wf_mcp|wf_mcp\\." src/wf_sources_mcp

Expected: no production-code imports. A package docstring mention may remain only if it explains a compatibility concern, but prefer updating stale wording if it no longer applies.

  • Step 3: Run lint

Run:

uv run ruff check src/wf_sources_mcp src/wf_mcp/source_registry.py src/wf_mcp/broker/config.py src/wf_mcp/broker/service/connection_service.py tests/wf_sources_mcp tests/wf_mcp/test_source_registry.py tests/wf_mcp/test_compat_imports.py

Expected: All checks passed!

  • Step 4: Run typecheck

Run:

uv run basedpyright --level error src/wf_sources_mcp src/wf_mcp/source_registry.py src/wf_mcp/broker/config.py src/wf_mcp/broker/service/connection_service.py tests/wf_sources_mcp tests/wf_mcp/test_source_registry.py

Expected: 0 errors, 0 warnings, 0 notes.

  • Step 5: Check whitespace

Run:

git diff --check

Expected: no whitespace errors. CRLF warnings on Windows are acceptable.


Expected Final Report

The implementer should report:

  • Files created, modified, and moved.
  • Exact verification commands and pass/fail output.
  • Confirmation that src/wf_sources_mcp has no production imports from wf_mcp.
  • Confirmation that broker DTO construction helpers live in wf_mcp.source_registry.
  • Confirmation that source-provider conversion helpers use structural protocols.
  • Any deviations from this plan.

Do not claim "full suite passed" unless the full suite was actually run.