16 KiB
MCP Runtime Package Move 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: Move persistent MCP runtime ownership from wf_mcp.runtime to wf_sources_mcp.runtime while preserving old imports as compatibility shims.
Architecture: The typed McpSourceConnection seam and shared open_mcp_session() now exist. This slice makes wf_sources_mcp.runtime canonical for persistent MCP sessions, pool reuse, and connection fingerprinting. wf_mcp.runtime.* should become thin re-export shims only; behavior should not change and persistent runtime remains tool-call-only.
Tech Stack: Python 3.14, dataclasses, asyncio actor/queue pattern, MCP Python SDK ClientSession, pytest, ruff, basedpyright.
Current State
Canonical source-provider code already exists:
wf_sources_mcp.connections.McpSourceConnectionwf_sources_mcp.client.open_mcp_sessionwf_sources_mcp.sdk.ToolCallResultwf_sources_mcp.sdk.converters.tool_result_to_call_result
Old runtime files still live in wf_mcp:
src/wf_mcp/runtime/factory.pysrc/wf_mcp/runtime/session.pysrc/wf_mcp/runtime/pool.py
The current McpRuntimePool has temporary compatibility glue:
McpSourceConnection -> _legacy_connection_config() -> PersistentSessionFactory
After this plan, that back-conversion should disappear. The canonical runtime factory should accept McpSourceConnection directly.
Non-Goals
- Do not broaden persistent runtime beyond
call_tool. - Do not add persistent
read_resource,get_prompt,invoke_method, orsend_notification. - Do not move
McpSdkAdapter. - Do not touch MCP proxy/frontend transport.
- Do not change workflow runtime semantics.
- Do not change on-disk auth/catalog/source registry formats.
Target File Structure
Create:
src/wf_sources_mcp/runtime/__init__.pysrc/wf_sources_mcp/runtime/session.pysrc/wf_sources_mcp/runtime/factory.pysrc/wf_sources_mcp/runtime/pool.pytests/wf_sources_mcp/test_runtime.py
Modify:
src/wf_mcp/runtime/__init__.py-> re-export shimsrc/wf_mcp/runtime/session.py-> re-export shimsrc/wf_mcp/runtime/factory.py-> re-export shimsrc/wf_mcp/runtime/pool.py-> re-export shimsrc/wf_mcp/broker/config.py-> canonical import fromwf_sources_mcp.runtime- any other production imports found by
rg 'wf_mcp\\.runtime' src tests/wf_mcp/test_compat_imports.py-> shim identity testsdocs/current_roadmap.mddocs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md
Task 1: Create Canonical Runtime Session
Files:
-
Create:
src/wf_sources_mcp/runtime/session.py -
Test:
tests/wf_sources_mcp/test_runtime.py -
Step 1: Add session tests
Create tests/wf_sources_mcp/test_runtime.py with:
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
import pytest
from mcp.types import CallToolResult, TextContent
from wf_sources_mcp.auth import AuthRecord
from wf_sources_mcp.connections import McpSourceConnection
from wf_sources_mcp.runtime import PersistentMcpSession
from wf_sources_mcp.transports import StdioSourceTransport
def _connection() -> McpSourceConnection:
return McpSourceConnection(
id="demo.personal",
provider="demo",
account="personal",
transport=StdioSourceTransport(command="fake"),
)
@pytest.mark.asyncio
async def test_persistent_session_call_callback_normalizes_tool_result() -> None:
async def call_tool(tool_name: str, payload: dict[str, Any]) -> CallToolResult:
assert tool_name == "echo"
assert payload == {"text": "hi"}
return CallToolResult(
content=[TextContent(type="text", text="ok")],
structuredContent={"echoed": "hi"},
)
session = PersistentMcpSession(
connection=_connection(),
auth=AuthRecord(connection_id="demo.personal", scheme="none"),
call_callback=call_tool,
)
result = await session.call_tool("echo", {"text": "hi"})
assert result.outcome == "ok"
assert result.output == {"echoed": "hi"}
@pytest.mark.asyncio
async def test_persistent_session_raises_without_transport() -> None:
session = PersistentMcpSession(connection=_connection(), auth=None)
with pytest.raises(RuntimeError, match="no tool call transport"):
await session.call_tool("echo", {})
- Step 2: Run failing tests
Run:
uv run pytest tests/wf_sources_mcp/test_runtime.py -q
Expected: fail because wf_sources_mcp.runtime does not exist.
- Step 3: Implement canonical session
Create src/wf_sources_mcp/runtime/session.py by moving the implementation from src/wf_mcp/runtime/session.py, but change imports/types:
- Import
AuthRecordfromwf_sources_mcp.auth. - Import
McpSourceConnectionfromwf_sources_mcp.connections. - Import
ToolCallResultandtool_result_to_call_resultfromwf_sources_mcp. PersistentMcpSession.connectionmust beMcpSourceConnection, notConnectionConfig.
Keep:
-
RawToolCaller -
clientinjection path -
call_callbackpath -
close_callback -
error message
"persistent MCP session has no tool call transport" -
Step 4: Run session tests
Run:
uv run pytest tests/wf_sources_mcp/test_runtime.py -q
uv run basedpyright --level error src/wf_sources_mcp/runtime
Expected: pass.
Task 2: Create Canonical Runtime Factory
Files:
-
Create:
src/wf_sources_mcp/runtime/factory.py -
Modify:
tests/wf_sources_mcp/test_runtime.py -
Step 1: Add factory owner-task tests
Append to tests/wf_sources_mcp/test_runtime.py:
from wf_sources_mcp.runtime.factory import PersistentSessionFactory
class _FakeFactory(PersistentSessionFactory):
def __init__(self) -> None:
self.calls: list[tuple[str, dict[str, object]]] = []
self.closed = False
async def _call_tool(self, tool_name: str, payload: dict[str, object]):
self.calls.append((tool_name, payload))
return CallToolResult(
content=[TextContent(type="text", text="ok")],
structuredContent={"echoed": payload["text"]},
)
async def _close(self) -> None:
self.closed = True
async def _create_with_stack(self, stack, connection, auth):
class _FakeClient:
async def call_tool(self, tool_name, payload):
return await self_factory._call_tool(tool_name, payload)
self_factory = self
return _FakeClient()
@pytest.mark.asyncio
async def test_persistent_session_factory_serializes_tool_calls() -> None:
factory = _FakeFactory()
session = await factory.create(_connection(), None)
first = await session.call_tool("echo", {"text": "one"})
second = await session.call_tool("echo", {"text": "two"})
await session.close()
assert first.output == {"echoed": "one"}
assert second.output == {"echoed": "two"}
assert factory.calls == [
("echo", {"text": "one"}),
("echo", {"text": "two"}),
]
- Step 2: Implement factory
Create src/wf_sources_mcp/runtime/factory.py by moving the implementation from src/wf_mcp/runtime/factory.py, but change types/imports:
PersistentSessionFactory.create(connection: McpSourceConnection, auth: AuthRecord | None)._SessionOwner.connection: McpSourceConnection._create_with_stack(stack, connection: McpSourceConnection, auth)uses:
session = await stack.enter_async_context(open_mcp_session(connection, auth))
return session
-
Remove any
ConnectionConfigimport. -
Keep
_ToolCallRequestand_SessionOwnerprivate. -
Keep actor/queue behavior unchanged.
-
Step 3: Run factory tests
Run:
uv run pytest tests/wf_sources_mcp/test_runtime.py -q
uv run basedpyright --level error src/wf_sources_mcp/runtime
Expected: pass.
Task 3: Create Canonical Runtime Pool
Files:
-
Create:
src/wf_sources_mcp/runtime/pool.py -
Modify:
src/wf_sources_mcp/runtime/__init__.py -
Modify:
tests/wf_sources_mcp/test_runtime.py -
Step 1: Add pool tests
Append to tests/wf_sources_mcp/test_runtime.py:
from wf_sources_mcp.runtime import McpRuntimePool, connection_runtime_fingerprint
@pytest.mark.asyncio
async def test_runtime_pool_reuses_unchanged_connection() -> None:
created: list[McpSourceConnection] = []
async def create_session(connection: McpSourceConnection, auth: AuthRecord | None):
created.append(connection)
return PersistentMcpSession(
connection=connection,
auth=auth,
call_callback=lambda tool_name, payload: CallToolResult(
content=[TextContent(type="text", text="ok")],
structuredContent={"echoed": payload["text"]},
),
)
pool = McpRuntimePool(session_factory=create_session)
connection = _connection()
await pool.call_tool(connection, None, "echo", {"text": "one"})
await pool.call_tool(connection, None, "echo", {"text": "two"})
assert created == [connection]
def test_runtime_fingerprint_changes_when_transport_changes() -> None:
original = _connection()
changed = McpSourceConnection(
id="demo.personal",
provider="demo",
account="personal",
transport=StdioSourceTransport(command="changed"),
)
assert connection_runtime_fingerprint(original) != connection_runtime_fingerprint(
changed
)
- Step 2: Implement pool
Create src/wf_sources_mcp/runtime/pool.py by moving the implementation from src/wf_mcp/runtime/pool.py, but make it canonical:
RuntimeConnectionshould beMcpSourceConnection.SessionFactoryshould acceptMcpSourceConnection, notConnectionConfig.- Remove
_legacy_connection_config. - Remove imports from
wf_mcp. connection_runtime_fingerprintshould acceptMcpSourceConnection.- Keep reuse/close behavior unchanged.
Create src/wf_sources_mcp/runtime/__init__.py:
from .factory import PersistentSessionFactory
from .pool import McpRuntimePool, connection_runtime_fingerprint
from .session import PersistentMcpSession
__all__ = [
"McpRuntimePool",
"PersistentMcpSession",
"PersistentSessionFactory",
"connection_runtime_fingerprint",
]
- Step 3: Run runtime tests
Run:
uv run pytest tests/wf_sources_mcp/test_runtime.py -q
uv run basedpyright --level error src/wf_sources_mcp/runtime
Expected: pass.
Task 4: Turn wf_mcp.runtime Into Compatibility Shims
Files:
-
Replace:
src/wf_mcp/runtime/session.py -
Replace:
src/wf_mcp/runtime/factory.py -
Replace:
src/wf_mcp/runtime/pool.py -
Modify:
src/wf_mcp/runtime/__init__.py -
Test:
tests/wf_mcp/test_compat_imports.py -
Step 1: Add shim identity tests
In tests/wf_mcp/test_compat_imports.py, add:
def test_runtime_shims_reexport_wf_sources_mcp_runtime() -> None:
from wf_mcp.runtime import (
McpRuntimePool as OldMcpRuntimePool,
PersistentMcpSession as OldPersistentMcpSession,
PersistentSessionFactory as OldPersistentSessionFactory,
connection_runtime_fingerprint as old_connection_runtime_fingerprint,
)
from wf_sources_mcp.runtime import (
McpRuntimePool,
PersistentMcpSession,
PersistentSessionFactory,
connection_runtime_fingerprint,
)
assert OldMcpRuntimePool is McpRuntimePool
assert OldPersistentMcpSession is PersistentMcpSession
assert OldPersistentSessionFactory is PersistentSessionFactory
assert old_connection_runtime_fingerprint is connection_runtime_fingerprint
- Step 2: Replace old runtime files with shims
src/wf_mcp/runtime/session.py:
"""Compatibility shim for the canonical MCP source runtime session."""
from wf_sources_mcp.runtime.session import PersistentMcpSession, RawToolCaller
__all__ = ["PersistentMcpSession", "RawToolCaller"]
src/wf_mcp/runtime/factory.py:
"""Compatibility shim for the canonical MCP source runtime factory."""
from wf_sources_mcp.runtime.factory import PersistentSessionFactory
__all__ = ["PersistentSessionFactory"]
src/wf_mcp/runtime/pool.py:
"""Compatibility shim for the canonical MCP source runtime pool."""
from wf_sources_mcp.runtime.pool import (
McpRuntimePool,
SessionFactory,
connection_runtime_fingerprint,
)
__all__ = [
"McpRuntimePool",
"SessionFactory",
"connection_runtime_fingerprint",
]
Keep src/wf_mcp/runtime/protocols.py as-is if it already shims ToolExecutor.
Update src/wf_mcp/runtime/__init__.py to re-export from wf_sources_mcp.runtime plus ToolExecutor:
from wf_sources_mcp.runtime import (
McpRuntimePool,
PersistentMcpSession,
PersistentSessionFactory,
connection_runtime_fingerprint,
)
from .protocols import ToolExecutor
__all__ = [
"McpRuntimePool",
"PersistentMcpSession",
"PersistentSessionFactory",
"ToolExecutor",
"connection_runtime_fingerprint",
]
- Step 3: Run shim tests
Run:
uv run pytest tests/wf_mcp/test_compat_imports.py tests/wf_sources_mcp/test_runtime.py -q
Expected: pass.
Task 5: Update Production Imports To Canonical Runtime
Files:
-
Modify:
src/wf_mcp/broker/config.py -
Search all source files
-
Step 1: Find old runtime imports
Run:
rg -n 'wf_mcp\.runtime|from \.\.runtime|from \.runtime' src tests
- Step 2: Update production imports
For production code outside shim files, import canonical runtime from wf_sources_mcp.runtime.
Likely file:
src/wf_mcp/broker/config.py
Replace:
from wf_mcp.runtime import McpRuntimePool, PersistentSessionFactory
or relative equivalents with:
from wf_sources_mcp.runtime import McpRuntimePool, PersistentSessionFactory
Do not update tests that intentionally verify compatibility shims.
- Step 3: Run focused production tests
Run:
uv run pytest tests/wf_mcp/test_stateful_runtime.py tests/wf_mcp/server/test_config.py::test_server_reuses_real_upstream_session_across_workflow_requests -q
uv run basedpyright --level error src
Expected: pass.
Task 6: Preserve Existing Stateful Runtime Tests
Files:
-
Modify:
tests/wf_mcp/test_stateful_runtime.pyonly if required -
Test:
tests/wf_mcp/test_stateful_runtime.py -
Step 1: Run existing tests unchanged first
Run:
uv run pytest tests/wf_mcp/test_stateful_runtime.py -q
Expected: should pass through shims. If it fails only because helper subclasses still type ConnectionConfig, update the tests to import canonical runtime but keep behavior assertions unchanged.
- Step 2: Do not weaken behavior assertions
The following behavior must remain tested:
- pool reuses unchanged connection fingerprint
- pool replaces changed connection fingerprint
- session owner serializes calls through one owner task
- closed sessions are closed via callback
- crashing factory surfaces errors to queued calls
If any of these tests need edits, preserve the same assertions and explain why in final report.
Task 7: Documentation And Verification
Files:
-
Modify:
docs/current_roadmap.md -
Modify:
docs/superpowers/specs/2026-06-03-long-lived-workflow-api-boundary.md -
Step 1: Update docs
Roadmap/spec should say:
-
persistent MCP runtime moved to
wf_sources_mcp.runtime -
wf_mcp.runtime.*are compatibility shims -
runtime remains tool-call-only
-
next slice is moving
McpSdkAdaptertowf_sources_mcp.sdk.adapter -
Step 2: Final verification
Run:
uv run pytest tests/wf_sources_mcp tests/wf_mcp/test_stateful_runtime.py tests/wf_mcp/test_compat_imports.py tests/wf_mcp/server/test_config.py::test_server_reuses_real_upstream_session_across_workflow_requests -q
uv run ruff check src tests
uv run basedpyright --level error src
git diff --check
Expected:
- focused tests pass
- ruff passes
- basedpyright has 0 errors
- no whitespace errors
If ruff check src tests finds unrelated pre-existing errors, do not fix unrelated files in this slice. Report exact files/errors and run ruff check on changed files instead.
Final Report Requirements
The final report must state:
- runtime files moved or shimmed
- no behavior expansion beyond
call_tool wf_mcp.runtimecompatibility status- whether any tests had to change and why
- exact verification commands and outputs