24 KiB
Auth Store Boundary Slice 1 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: Add a neutral auth record/store boundary and make current MCP auth loading prefer source auth_ref while preserving legacy connection-id behavior.
Architecture: wf_api owns neutral auth ids, records, and the read-only auth-store protocol. wf_mcp keeps its current AuthRecord compatibility type, but adds adapter helpers that convert neutral records into MCP runtime records and centralize MCP payload interpretation. No diagnostics, admin mutation, OAuth, or secret-manager behavior in this slice.
Tech Stack: Python 3.14, dataclasses, protocols, pytest, ruff, basedpyright.
Scope
Implement only:
- neutral auth model/protocol in
wf_api - MCP file-store adapter bridge
- MCP runtime auth resolution by
metadata["auth_ref"]with legacy fallback - central MCP auth payload interpretation helper
Do not implement:
- auth admin CLI/RPC/MCP tools
- auth diagnostics in deployment validation
- OAuth/browser flows
- encrypted file format
- source-provider packages beyond current
wf_mcp
Files
- Create:
src/wf_api/auth.py- neutral
AUTH_ID_PATTERN validate_auth_id- frozen
AuthRecord - read-only
AuthStoreprotocol
- neutral
- Modify:
src/wf_api/__init__.py- export neutral auth symbols
- Create:
tests/wf_api/test_auth.py- neutral auth model/protocol tests
- Create:
src/wf_mcp/auth.py- adapter helpers between neutral auth and
wf_mcp.models.AuthRecord - centralized MCP header/env extraction
- adapter helpers between neutral auth and
- Modify:
src/wf_mcp/storage/store.py- add neutral
load_auth_record(auth_ref)/save_auth_record(record)methods - keep old
save_auth(AuthRecord)/load_auth(connection_id)methods
- add neutral
- Modify:
src/wf_mcp/broker/service/upstream_transport.py- add
load_connection_auth(connection)helper - update all
load_auth(connection.id)call sites to use the helper - keep
load_auth(connection_id)facade for compatibility
- add
- Modify:
src/wf_mcp/broker/service/source_catalog.py- change
AuthLoaderfromCallable[[str], AuthRecord | None]toCallable[[ConnectionConfig], AuthRecord | None] - update hydrated wrapper calls to pass the connection object
- change
- Modify:
src/wf_mcp/broker/service/core.py- pass
self.upstream.load_connection_authtoSourceCatalogService
- pass
- Modify:
src/wf_mcp/sdk/adapter.py- replace local
_auth_headers/ env payload reads withwf_mcp.authhelpers
- replace local
- Modify:
src/wf_mcp/runtime/factory.py- replace local
_auth_headers/ env payload reads withwf_mcp.authhelpers
- replace local
- Test:
tests/wf_mcp/test_auth.py- adapter bridge tests
- Test:
tests/wf_mcp/service/test_upstream_transport.pyauth_reflookup / fallback tests
- Test: existing focused suites listed below
Task 1: Neutral auth model and store protocol
Files:
-
Create:
src/wf_api/auth.py -
Modify:
src/wf_api/__init__.py -
Create:
tests/wf_api/test_auth.py -
Step 1: Write neutral auth tests
Create tests/wf_api/test_auth.py:
from __future__ import annotations
from collections.abc import Mapping
from typing import assert_type
import pytest
from wf_api.auth import AuthRecord, AuthStore, validate_auth_id
def test_validate_auth_id_accepts_safe_dotted_ids() -> None:
assert validate_auth_id("github.work") == "github.work"
assert validate_auth_id("api_ci-1") == "api_ci-1"
@pytest.mark.parametrize("auth_id", ["", ".hidden", "../secret", "bad/id"])
def test_validate_auth_id_rejects_unsafe_ids(auth_id: str) -> None:
with pytest.raises(ValueError, match="auth id must start"):
validate_auth_id(auth_id)
def test_auth_record_is_immutable_and_mapping_typed() -> None:
record = AuthRecord(
id="github.work",
scheme="bearer",
payload={"token": "secret"},
metadata={"owner": "test"},
)
assert record.id == "github.work"
assert record.scheme == "bearer"
assert record.payload["token"] == "secret"
assert_type(record.payload, Mapping[str, object])
with pytest.raises(AttributeError):
record.scheme = "headers" # type: ignore[misc]
class MemoryAuthStore:
def __init__(self, records: dict[str, AuthRecord]) -> None:
self.records = records
def load_auth(self, auth_ref: str) -> AuthRecord | None:
return self.records.get(auth_ref)
def test_auth_store_protocol_is_read_only_lookup() -> None:
record = AuthRecord(id="github.work", scheme="opaque", payload={"x": 1})
store: AuthStore = MemoryAuthStore({"github.work": record})
assert store.load_auth("github.work") is record
assert store.load_auth("missing") is None
- Step 2: Run test to verify failure
Run:
uv run pytest tests/wf_api/test_auth.py -q
Expected: fails because wf_api.auth does not exist.
- Step 3: Implement neutral auth module
Create src/wf_api/auth.py:
from __future__ import annotations
import re
from collections.abc import Mapping
from dataclasses import dataclass, field
from typing import Protocol
AUTH_ID_PATTERN = r"^[A-Za-z0-9_][A-Za-z0-9_.-]*$"
def validate_auth_id(value: str) -> str:
"""Validate auth refs that are safe as store keys and path segments.
Auth refs deliberately carry no provider semantics. Source providers decide
how a resolved auth record is interpreted.
"""
if not re.fullmatch(AUTH_ID_PATTERN, value):
raise ValueError(
"auth id must start with alphanumeric or underscore and contain "
"only [A-Za-z0-9_.-]"
)
return value
@dataclass(frozen=True, slots=True)
class AuthRecord:
"""Neutral credential record resolved by auth ref.
`scheme + payload` is a compatibility bridge, not the long-term taxonomy.
Keep payload interpretation inside provider adapters so a future
discriminated union can replace this without touching workflow/config code.
"""
id: str
scheme: str
payload: Mapping[str, object]
metadata: Mapping[str, object] = field(default_factory=dict)
def __post_init__(self) -> None:
validate_auth_id(self.id)
if not self.scheme:
raise ValueError("auth scheme must be non-empty")
class AuthStore(Protocol):
"""Read-only runtime credential lookup by auth ref."""
def load_auth(self, auth_ref: str) -> AuthRecord | None: ...
__all__ = [
"AUTH_ID_PATTERN",
"AuthRecord",
"AuthStore",
"validate_auth_id",
]
- Step 4: Export from
wf_api
Modify src/wf_api/__init__.py:
from .auth import AUTH_ID_PATTERN, AuthRecord, AuthStore, validate_auth_id
Add these entries to __all__:
"AUTH_ID_PATTERN",
"AuthRecord",
"AuthStore",
"validate_auth_id",
- Step 5: Run focused tests
Run:
uv run pytest tests/wf_api/test_auth.py tests/wf_api/test_import_direction.py -q
uv run ruff check src/wf_api/auth.py tests/wf_api/test_auth.py
uv run basedpyright --level error src/wf_api tests/wf_api/test_auth.py
Expected: all pass.
Task 2: MCP auth adapter helpers
Files:
-
Create:
src/wf_mcp/auth.py -
Create:
tests/wf_mcp/test_auth.py -
Step 1: Write MCP adapter tests
Create tests/wf_mcp/test_auth.py:
from __future__ import annotations
from wf_api.auth import AuthRecord as NeutralAuthRecord
from wf_mcp.auth import (
mcp_auth_env,
mcp_auth_headers,
mcp_auth_from_neutral,
neutral_auth_from_mcp,
)
from wf_mcp.models import AuthRecord as McpAuthRecord
def test_mcp_auth_from_neutral_preserves_scheme_and_payload() -> None:
neutral = NeutralAuthRecord(
id="github.work",
scheme="bearer",
payload={"token": "secret"},
metadata={"owner": "test"},
)
mcp = mcp_auth_from_neutral(neutral)
assert mcp == McpAuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "secret"},
)
def test_neutral_auth_from_mcp_preserves_payload() -> None:
mcp = McpAuthRecord(
connection_id="github.work",
scheme="headers",
payload={"headers": {"X-Test": "yes"}},
)
neutral = neutral_auth_from_mcp(mcp)
assert neutral.id == "github.work"
assert neutral.scheme == "headers"
assert neutral.payload == {"headers": {"X-Test": "yes"}}
def test_mcp_auth_headers_extracts_explicit_headers_and_bearer_token() -> None:
auth = McpAuthRecord(
connection_id="api.work",
scheme="bearer",
payload={"headers": {"X-Test": "yes"}, "token": "secret"},
)
assert mcp_auth_headers(auth) == {
"X-Test": "yes",
"Authorization": "Bearer secret",
}
def test_mcp_auth_headers_does_not_override_authorization_header() -> None:
auth = McpAuthRecord(
connection_id="api.work",
scheme="bearer",
payload={
"headers": {"Authorization": "Basic already"},
"token": "secret",
},
)
assert mcp_auth_headers(auth) == {"Authorization": "Basic already"}
def test_mcp_auth_env_returns_string_map_only() -> None:
auth = McpAuthRecord(
connection_id="mcp.local",
scheme="env",
payload={"env": {"TOKEN": "secret", "BAD": 123}},
)
assert mcp_auth_env(auth) == {"TOKEN": "secret"}
- Step 2: Run test to verify failure
Run:
uv run pytest tests/wf_mcp/test_auth.py -q
Expected: fails because wf_mcp.auth does not exist.
- Step 3: Implement MCP adapter module
Create src/wf_mcp/auth.py:
from __future__ import annotations
from wf_api.auth import AuthRecord as NeutralAuthRecord
from .models import AuthRecord as McpAuthRecord
def mcp_auth_from_neutral(record: NeutralAuthRecord) -> McpAuthRecord:
"""Adapt neutral auth to the current MCP compatibility record."""
return McpAuthRecord(
connection_id=record.id,
scheme=record.scheme,
payload=dict(record.payload),
)
def neutral_auth_from_mcp(record: McpAuthRecord) -> NeutralAuthRecord:
"""Adapt legacy MCP auth into the neutral record shape."""
return NeutralAuthRecord(
id=record.connection_id,
scheme=record.scheme,
payload=dict(record.payload),
)
def mcp_auth_headers(auth: McpAuthRecord | None) -> dict[str, str]:
"""Return HTTP headers understood by MCP HTTP transports.
This is intentionally MCP-specific. Neutral code must not inspect payload
keys such as `headers` or `token`.
"""
if auth is None:
return {}
raw_headers = auth.payload.get("headers", {})
headers = {
str(key): str(value)
for key, value in raw_headers.items()
if isinstance(key, str) and isinstance(value, str)
} if isinstance(raw_headers, dict) else {}
token = auth.payload.get("token")
if isinstance(token, str) and "Authorization" not in headers:
headers["Authorization"] = f"Bearer {token}"
return headers
def mcp_auth_env(auth: McpAuthRecord | None) -> dict[str, str]:
"""Return environment variables understood by MCP stdio transports."""
if auth is None:
return {}
raw_env = auth.payload.get("env", {})
if not isinstance(raw_env, dict):
return {}
return {
str(key): str(value)
for key, value in raw_env.items()
if isinstance(key, str) and isinstance(value, str)
}
__all__ = [
"mcp_auth_env",
"mcp_auth_from_neutral",
"mcp_auth_headers",
"neutral_auth_from_mcp",
]
- Step 4: Run focused tests
Run:
uv run pytest tests/wf_mcp/test_auth.py -q
uv run ruff check src/wf_mcp/auth.py tests/wf_mcp/test_auth.py
uv run basedpyright --level error src/wf_mcp/auth.py tests/wf_mcp/test_auth.py
Expected: all pass.
Task 3: Store adapter bridge
Files:
-
Modify:
src/wf_mcp/storage/store.py -
Modify:
tests/wf_mcp/test_auth.py -
Step 1: Add store bridge tests
Append to tests/wf_mcp/test_auth.py:
from pathlib import Path
from wf_mcp.storage import FileStore
def test_file_store_saves_and_loads_neutral_auth_record(tmp_path: Path) -> None:
store = FileStore(tmp_path)
record = NeutralAuthRecord(
id="github.work",
scheme="bearer",
payload={"token": "secret"},
metadata={"owner": "test"},
)
store.save_auth_record(record)
loaded = store.load_auth_record("github.work")
assert loaded == record
def test_file_store_legacy_auth_methods_still_work(tmp_path: Path) -> None:
store = FileStore(tmp_path)
legacy = McpAuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "secret"},
)
store.save_auth(legacy)
assert store.load_auth("github.work") == legacy
assert store.load_auth_record("github.work") == NeutralAuthRecord(
id="github.work",
scheme="bearer",
payload={"token": "secret"},
)
- Step 2: Run tests to verify failure
Run:
uv run pytest tests/wf_mcp/test_auth.py::test_file_store_saves_and_loads_neutral_auth_record tests/wf_mcp/test_auth.py::test_file_store_legacy_auth_methods_still_work -q
Expected: fails because FileStore.save_auth_record / load_auth_record do not exist.
- Step 3: Extend store interface and file store
Modify src/wf_mcp/storage/store.py:
Add imports:
from wf_api.auth import AuthRecord as NeutralAuthRecord
from ..auth import mcp_auth_from_neutral, neutral_auth_from_mcp
Add to class Store:
def save_auth_record(self, record: NeutralAuthRecord) -> None:
raise NotImplementedError
def load_auth_record(self, auth_ref: str) -> NeutralAuthRecord | None:
raise NotImplementedError
Add to class FileStore:
def save_auth_record(self, record: NeutralAuthRecord) -> None:
"""Save neutral auth through the legacy MCP file shape."""
self.save_auth(mcp_auth_from_neutral(record))
def load_auth_record(self, auth_ref: str) -> NeutralAuthRecord | None:
"""Load neutral auth from the legacy MCP file shape."""
record = self.load_auth(auth_ref)
if record is None:
return None
return neutral_auth_from_mcp(record)
Do not change the existing on-disk JSON shape in this task.
- Step 4: Run focused tests
Run:
uv run pytest tests/wf_mcp/test_auth.py -q
uv run ruff check src/wf_mcp/storage/store.py tests/wf_mcp/test_auth.py
uv run basedpyright --level error src/wf_mcp/storage/store.py tests/wf_mcp/test_auth.py
Expected: all pass.
Task 4: Runtime auth resolution by auth_ref with fallback
Files:
-
Modify:
src/wf_mcp/broker/service/upstream_transport.py -
Modify:
src/wf_mcp/broker/service/source_catalog.py -
Modify:
src/wf_mcp/broker/service/core.py -
Modify:
tests/wf_mcp/service/test_upstream_transport.py -
Step 1: Add upstream resolution tests
Open tests/wf_mcp/service/test_upstream_transport.py. Add imports if missing:
from pathlib import Path
from wf_mcp.models import AuthRecord, ConnectionConfig
from wf_mcp.storage import FileStore
Add helper if the file does not already have one:
def _transport(root: Path) -> UpstreamTransportService:
events: list[object] = []
return UpstreamTransportService(
store=FileStore(root),
event_sink=events.append,
)
Add tests:
def test_upstream_load_connection_auth_prefers_auth_ref(tmp_path: Path) -> None:
service = _transport(tmp_path)
service.save_auth(
AuthRecord(
connection_id="github.creds",
scheme="bearer",
payload={"token": "secret"},
)
)
service.save_auth(
AuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "wrong"},
)
)
connection = ConnectionConfig(
id="github.work",
server="github",
account="work",
metadata={"auth_ref": "github.creds"},
)
assert service.load_connection_auth(connection) == AuthRecord(
connection_id="github.creds",
scheme="bearer",
payload={"token": "secret"},
)
def test_upstream_load_connection_auth_falls_back_to_connection_id(
tmp_path: Path,
) -> None:
service = _transport(tmp_path)
service.save_auth(
AuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "legacy"},
)
)
connection = ConnectionConfig(
id="github.work",
server="github",
account="work",
)
assert service.load_connection_auth(connection) == AuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "legacy"},
)
def test_upstream_load_connection_auth_ignores_non_string_auth_ref(
tmp_path: Path,
) -> None:
service = _transport(tmp_path)
service.save_auth(
AuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "legacy"},
)
)
connection = ConnectionConfig(
id="github.work",
server="github",
account="work",
metadata={"auth_ref": 123},
)
assert service.load_connection_auth(connection) == AuthRecord(
connection_id="github.work",
scheme="bearer",
payload={"token": "legacy"},
)
If the file already has a helper for UpstreamTransportService, use it instead
of adding _transport.
- Step 2: Run tests to verify failure
Run:
uv run pytest tests/wf_mcp/service/test_upstream_transport.py -q
Expected: fails because load_connection_auth does not exist.
- Step 3: Implement
load_connection_auth
Modify src/wf_mcp/broker/service/upstream_transport.py.
Add method to UpstreamTransportService after load_auth:
def load_connection_auth(self, connection: ConnectionConfig) -> AuthRecord | None:
"""Resolve auth for a connection, preferring explicit source auth_ref.
Legacy MCP auth records are keyed by connection id. New source registry
and neutral config entries carry `auth_ref`; keep both paths until the
old compatibility surface has no callers.
"""
auth_ref = connection.metadata.get("auth_ref")
if isinstance(auth_ref, str):
return self.load_auth(auth_ref)
return self.load_auth(connection.id)
- Step 4: Replace direct connection-id auth lookups in upstream transport
In src/wf_mcp/broker/service/upstream_transport.py, replace these patterns:
auth = self.load_auth(connection.id)
with:
auth = self.load_connection_auth(connection)
Also replace the live-check lookup:
auth = self.load_auth(source_id)
with:
auth = self.load_connection_auth(connection)
Keep load_auth(connection_id) unchanged as a compatibility facade.
- Step 5: Update source catalog auth callback
Modify src/wf_mcp/broker/service/source_catalog.py.
Change:
AuthLoader = Callable[[str], AuthRecord | None]
to:
AuthLoader = Callable[[ConnectionConfig], AuthRecord | None]
Inside spec_from_snapshot_entry.invoke_tool, change:
connection = self.connection_lookup(entry.connection_id)
auth = self.load_auth(entry.connection_id)
to:
connection = self.connection_lookup(entry.connection_id)
auth = self.load_auth(connection)
- Step 6: Wire source catalog to the new resolver
Modify src/wf_mcp/broker/service/core.py.
In SourceCatalogService(...) construction, change:
load_auth=self.upstream.load_auth,
to:
load_auth=self.upstream.load_connection_auth,
- Step 7: Run focused tests
Run:
uv run pytest tests/wf_mcp/service/test_upstream_transport.py tests/wf_mcp/service/test_catalog.py tests/wf_mcp/service/test_workflow_runtime.py -q
uv run ruff check src/wf_mcp/broker/service/upstream_transport.py src/wf_mcp/broker/service/source_catalog.py src/wf_mcp/broker/service/core.py tests/wf_mcp/service/test_upstream_transport.py
uv run basedpyright --level error src/wf_mcp/broker/service tests/wf_mcp/service/test_upstream_transport.py
Expected: all pass.
Task 5: Centralize MCP payload interpretation
Files:
-
Modify:
src/wf_mcp/sdk/adapter.py -
Modify:
src/wf_mcp/runtime/factory.py -
Test: existing MCP auth/runtime tests
-
Step 1: Update SDK adapter imports
Modify src/wf_mcp/sdk/adapter.py.
Add import:
from ..auth import mcp_auth_env, mcp_auth_headers
Delete local _auth_headers.
In stdio branch, replace:
if auth is not None:
auth_env = auth.payload.get("env")
if isinstance(auth_env, dict):
env = {**(env or {}), **auth_env}
with:
auth_env = mcp_auth_env(auth)
if auth_env:
env = {**(env or {}), **auth_env}
In HTTP branch, replace:
headers = _auth_headers(auth)
with:
headers = mcp_auth_headers(auth)
- Step 2: Update runtime factory imports
Modify src/wf_mcp/runtime/factory.py.
Add import:
from ..auth import mcp_auth_env, mcp_auth_headers
Delete local _auth_headers.
In stdio branch, replace:
if auth is not None:
auth_env = auth.payload.get("env")
if isinstance(auth_env, dict):
env = {**(env or {}), **auth_env}
with:
auth_env = mcp_auth_env(auth)
if auth_env:
env = {**(env or {}), **auth_env}
In HTTP branch, replace:
httpx.AsyncClient(headers=_auth_headers(auth) or None)
with:
httpx.AsyncClient(headers=mcp_auth_headers(auth) or None)
- Step 3: Run focused tests
Run:
uv run pytest tests/wf_mcp/test_auth.py tests/wf_mcp/service/test_adapters.py tests/wf_mcp/service/test_workflow_runtime.py tests/wf_mcp/service/test_upstream_transport.py -q
uv run ruff check src/wf_mcp/auth.py src/wf_mcp/sdk/adapter.py src/wf_mcp/runtime/factory.py
uv run basedpyright --level error src/wf_mcp/auth.py src/wf_mcp/sdk/adapter.py src/wf_mcp/runtime/factory.py
Expected: all pass.
Task 6: Docs and final verification
Files:
-
Modify:
docs/current_roadmap.md -
Modify:
docs/superpowers/specs/2026-06-06-auth-source-secrets-boundary.md -
Step 1: Update roadmap status
In docs/current_roadmap.md, under the auth/source secrets boundary bullet,
append:
First implementation slice complete: neutral auth records/store protocol
exist in `wf_api`, MCP runtime auth resolution prefers explicit `auth_ref`
with legacy connection-id fallback, and MCP payload interpretation is
isolated in provider-specific adapter helpers.
- Step 2: Update spec status
In docs/superpowers/specs/2026-06-06-auth-source-secrets-boundary.md, add
after ## Purpose:
## Status
Slice 1 implements the neutral auth record/store protocol and MCP compatibility
bridge. Diagnostics, auth admin surfaces, and provider-specific auth unions are
future slices.
- Step 3: Run final verification
Run:
uv run pytest tests/wf_api/test_auth.py tests/wf_api/test_import_direction.py tests/wf_mcp/test_auth.py tests/wf_mcp/service/test_upstream_transport.py tests/wf_mcp/service/test_catalog.py tests/wf_mcp/service/test_workflow_runtime.py tests/wf_mcp/service/test_adapters.py -q
uv run ruff check src/wf_api src/wf_mcp tests/wf_api/test_auth.py tests/wf_mcp/test_auth.py tests/wf_mcp/service/test_upstream_transport.py
uv run basedpyright --level error src/wf_api src/wf_mcp tests/wf_api/test_auth.py tests/wf_mcp/test_auth.py tests/wf_mcp/service/test_upstream_transport.py
Expected: all pass.
- Step 4: Final report
Report:
- files created/modified
- verification outputs
- whether any old
auth.payload[...]reads remain outsidesrc/wf_mcp/auth.py - deviations from this plan
Search command for the payload check:
rg -n 'auth\.payload|get\("headers"|get\("token"|get\("env"' src/wf_mcp
Expected: payload interpretation is either in src/wf_mcp/auth.py or unrelated
connection/source metadata handling. If adapter/runtime files still read
auth.payload, fix before reporting.