Scrubbed extract of
MCP-Server-Implementation/Epic-4-Stories/US-MCP-4.6-Real-Transport-Unskip.md— cited by the Chronicle (03-the-platform-rises). Redactions: phone / service detail / secrets / clearance / personal / internal hosts.
type: implementation-plan category: Work project: MCP-Server-Implementation priority: High status: done created: 2026-06-22 updated: 2026-06-28 epic: Epic-4-MCP-Server user-story: US-MCP-4.6 effort: TBD parallel_group: phase-1-post-w4 review-profile: rigorous source: us-mcp-4.5-followup depends-on:
- US-MCP-4.1
- US-MCP-4.2
- US-MCP-4.3
- US-MCP-4.4
- US-MCP-4.5
- IP-AIS-DEPLOY-1 tags:
- implementation-plan
- epic-4
- mcp-server
- sandbox
- transport
- audit
- unskip
US-MCP-4.6: Real MCP transport + audit-event sink, and unskip the sandbox E2E harness
Epic: [[README|Epic 4: MCP Server]]
Owner Agent: go-mcp-engineer (lead) + dotnet-architect (C# adapter side)
Effort: TBD (sized at story-grooming; rough estimate 3-5d when groomed)
Priority: High
Status: In Progress (authored 2026-06-22 as a US-MCP-4.5 follow-up; activated 2026-06-27 as the W4 v0.1.0-closing wave). Scope correction (verified 2026-06-27): the C# “non-mock adapter” already exists and is complete — MCPSdkClient + McpSdkClientGateway (ModelContextProtocol SDK 1.3.0, stdio+HTTP transports), DI-selectable via MCP:Client=SDK. Do not build a new RealMCPClient; the C# lane is config activation (MCP:Sdk section + flip off MCP:Provider=Mock) + threading correlation-ID onto the outbound MCP CallTool _meta.correlationId. The real build is the Go transport (composition root + internal/api/mcp/ + concrete audit EventSink).
Mission
Take the MCPServer from a Phase-0 stub to a runnable MCP transport that
the AIService C# MCP SDK adapter can call against the sandboxed image,
and emit the audit events the platform’s trust-gate + ADR-004 controls
need to be externally observable. When green, flip
MCP_PROVIDER=Mock -> MCP_PROVIDER=Real in the system-integration
workflow so the [[US-MCP-4.5-Sandbox-E2E|US-MCP-4.5]] sandbox_e2e harness
unskips and the W4 release-gate runbook can capture the live-run demo.
Why this exists
[[US-MCP-4.5-Sandbox-E2E|US-MCP-4.5]] landed the executable contract –
five scripted scenarios encoding the ADR-004 DoD assertions – but
intentionally left them gated SKIP because, on the main of 2026-06-22,
none of them can be satisfied without:
- A real MCP transport on the Go side.
cmd/mcpserver/main.gois a stub.internal/tools/{file,git,shell}exist as libraries but are not wired to any callable surface. The W4 compose explicitly accepts this withdepends_on: service_started. - A non-mock C# adapter mode.
AIService.Infrastructure.Adapters. Outbound.MCP.MockMCPClientreturns printf-style strings; it never touches a container, never produces audit events, never fires a gate. - An audit-event sink.
internal/tools/shell/sandbox_check.gois a unit-tested helper; there is no pipeline emittingSandboxConfirmed/WeakSandboxMode/TrustGateDecisionto a queryable index that an E2E can grep.
US-MCP-4.5 chose Option A (xfail scaffold) explicitly to avoid bleeding into US-MCP-4.1/4.2/4.3 scope. This story is where the real plumbing lives.
Context Bootstrap
Read before writing any code, in order:
- [[US-MCP-4.5-Sandbox-E2E|US-MCP-4.5]] – the five scenarios that must
unskip, and the
MCP_PROVIDERflip pattern. - [[../../AI-Dev-Platform/ADR/ADR-004-Sandbox-Strategy|ADR-004]] – the controls the audit
sink must surface (
SandboxConfirmed,WeakSandboxMode). - [[US-PLAT-4.1-Tool-Capability-Registry|US-PLAT-4.1]] – the
BlastRadiusHint/ capability declarations the trust-gate keys off to decide which calls fire a gate. MCPServer/internal/mcp/registration.go– the existing ListTools-descriptor hook; the transport story consumes this.MCPServer/internal/tools/shell/sandbox_check.go– the in-container sandbox probe whose verdict the audit emitter publishes.AIService.Infrastructure.Adapters.Outbound.MCP.{MockMCPClient, MCPSdkClient,MCPGrpcClient}– the existing client surface (the SDK and gRPC variants are stubbed; pick one as the real path).
Gate Policy
| Trigger | Gate Type | Condition |
|---|---|---|
| The five US-MCP-4.5 scenarios still SKIP after this story merges | Trust Gate | The whole point is to unskip them |
| Sandbox probe verdict not surfaced as an audit event | Trust Gate | ADR-004 controls must be externally observable, not just unit-asserted |
| Trust-gate decision not recorded for any side-effecting tool call | Trust Gate | Defeats the platform’s safety story if approvals are not auditable |
| Correlation-ID masked or altered at the AIService -> MCP boundary | Confidence Gate | Tracing must remain coherent across the seam |
Deliverables
- MCP transport on the Go side — merged PR #21 (
a5ec2fd). HTTP/SSE via go-sdk, composition root, tool routing. - Sandboxed image health-check —
debian:bookworm-slimbase (not distroless/static), HEALTHCHECK subcommand, non-root uid 65532. - Audit-event sink — concrete
EventSinkemittingSandboxConfirmed/WeakSandboxMode/TrustGateDecision/ToolInvocationStart/ToolInvocationEnd. - Non-mock C# adapter mode — merged PR #36 (
1307bb5).MCP:Client=SDK+MCP:Sdksection in appsettings;MockMCPClientstays in Development. - CI flip:
MCP_PROVIDER=Realset insystem-integration.ymlfor the sandbox_e2e step. All five US-MCP-4.5 scenarios go fromSKIPtoPASSagainst the digest- pinned sandboxed MCPServer + AIService images. - Sandbox-image prereqs —
debian:bookworm-slimincludespython3+git+ca-certificates. -
workspace_file_containsprecondition check (Cycle 2 deferred item from US-MCP-4.5): augment the helper intests/system-integration/sandbox_e2e/_harness.pyso it fails loudly ifAIDP_WORKSPACE_HOST_PATHresolves outside the expected bind-mount root or the directory does not exist before the test starts. Prevents a phantom-pass where the helper silently returnsFalsebecause the workspace was never mounted. - Live capture (gated on [[../../AI-Service-Implementation/Epic-Identity/IP-AIS-DEPLOY-1-AIService-Go-Live|IP-AIS-DEPLOY-1]]):
run the same harness once against the deployed AIService on
proxmox-docker-host; capture log + screenshot for the W4 demo.
Execution progress
Code lanes — complete 2026-06-27
- Go transport lane — merged
homelab/MCPServerPR #21 → main9015015(green SHAa5ec2fd). HTTP/SSE MCP server (official go-sdk), audit sink, slim tool-runtime base image (debian:bookworm-slim), git_push allowlist hardening (server-side env var, removed caller-controlledallowed_remotes). 2-reviewer + fix-loop clean. - C# activation lane — merged
homelab/AIServicePR #36 → main2fc05e1(green SHA1307bb5). SDK adapter activated (HTTP/SSE),_meta.correlationIdon wire (baggage, no silent fallback), TrustGateDecision audit via Coryp.Logging scope props, read-only classification unified (McpToolClassification),CorrelationContext.Resolve()returns only baggage (no silent?? current.Idfallback). 2-reviewer + fix-loop clean.
Integration lane — IN PROGRESS (2026-06-27)
Code lanes merged. Resolved: execute_tool WS command already implemented in MessageHandler.cs:529; harness _harness.py:109 already calls call_tool with command: execute_tool. The earlier gap about missing tool-execution surface is closed.
Remaining work:
- Delete bogus
0.1.0tag onhomelab/DeveloperAutomationToolsServices(points at Jan-29 “notes sync” commit). - DATS CI flip:
docker-compose.ci.yml— restoredepends_on: mcpexecutor: service_healthy; addMCP__Client=SDK+MCP__Sdk__Endpoint=http://mcpexecutor:8080/mcpto aiservice env.system-integration.yml— flipMCP_PROVIDER: "Mock"→"Real"; removeAIDP_STUB_MCP. - Compose wiring: mcpexecutor transport image + seccomp/AppArmor
security_opt+/workspacebind-mount. - Redeploy on
.106: MCPServer (new transport image) + AIService (MCP:Client=SDK). - Live capture: run harness
MCP_PROVIDER=Real→PASS=5 SKIP=0 FAIL=0. - Phase C: cut real
v0.1.0tag, DATS submodule bumps, flip Phase-1 trackers → done, wave-exit grep.
Tracked at Tasks/Work/integration-lane-us-mcp-4.6.md.
Wire contract (frozen 2026-06-27 — extracted from the US-MCP-4.5 harness)
Transport: HTTP/SSE (MCP protocol) per [[../../AI-Dev-Platform/ADR/ADR-009-MAF-Integration-Strategy|ADR-009 §Amendment 2026-06-27]]. The harness (tests/system-integration/sandbox_e2e/) is the executable contract; both lanes build to satisfy it.
Correlation-ID carrier: the canary travels on the MCP CallTool request as _meta.correlationId. C# sets it (from Activity.Current/orchestrator correlation-ID); Go reads it from _meta, stamps every audit event with it, and echoes it back in the tool result. Unmasked — no hash/substring transform.
Tool result envelope (surfaced from the AIService WS session that call_tool drives): { "success": bool, "correlation_id": str, "output": str, "stderr": str }.
Audit events (read_audit_events parses them from the services’ structured logs). Each is a JSON object: { "type": str, "source": str, "correlation_id": str, "tool": str, "decision"?: str }.
type |
source |
Emitter | When | Extra |
|---|---|---|---|---|
SandboxConfirmed |
MCPServer |
Go | startup EnforceSandbox ok |
— |
WeakSandboxMode |
MCPServer |
Go | sandbox degraded (must NEVER appear in Real mode) | — |
ToolInvocationStart / ToolInvocationEnd |
MCPServer |
Go | each CallTool | tool = tool name |
TrustGateDecision |
AIService |
C# | each side-effecting tool call | tool = name, decision = approve |
Gate firing rule: TrustGateDecision is emitted for side-effecting tools (write_file, run_command, git_add, git_commit, git_push) and never for read-only (read_file, git_status). Field key is tool (not tool_name).
Sandbox image: run_command seccomp scenario invokes python3 -c … → the sandboxed MCPServer image must have python3 on PATH (or the probe is rebaked; assertion ptrace_rc=-1 ptrace_errno=1 is fixed).
Execution Phases
TDD-ordered per [[../../AI-Dev-Platform/ADR/ADR-017-Human-Review-Branching-And-TDD-Workflow|ADR-017]].
rigorous profile.
- Branch –
feat/US-MCP-4.6-real-transport-unskipfromorigin/maininhomelab/MCPServer. C# adapter changes go tohomelab/AIService(coordinate cross-repo PR at review time). - Load Context – read the bootstrap; confirm US-MCP-4.5 harness still SKIPs cleanly on main.
- Red – run the US-MCP-4.5 harness with
MCP_PROVIDER=Real; all five scenarios fail withRealModeNotWired/ equivalent. This is the target contract. - Implement – transport, audit sink, C# Real adapter, compose wiring.
- Verify – harness green with
MCP_PROVIDER=Real; sandbox probe verdict surfaces asSandboxConfirmed; trust-gate fires only on side-effecting tools. - PR – MCPServer + AIService PRs; reviewers
architecture-reviewer,go-standards,dotnet-architect.
Files to Modify
MCPServer/cmd/mcpserver/main.go– real composition rootMCPServer/internal/api/mcp/– new package: transport surfaceMCPServer/internal/infrastructure/audit/– new package: audit emitter consumingsandbox_check.goverdict + tool invocation eventsMCPServer/Dockerfile– HEALTHCHECK upgraded to probe transport + sandbox statusAIService/AIService.Infrastructure/Adapters/Outbound/MCP/– newRealMCPClient(or wire up the existingMCPSdkClient/MCPGrpcClient)DeveloperAutomationToolsServices/docker-compose.ci.yml– restoredepends_on: service_healthyfor mcpexecutor; setMCP__Provider=RealDeveloperAutomationToolsServices/.gitea/workflows/system-integration.yml– flipMCP_PROVIDER: "Mock"to"Real"on the sandbox_e2e step
Definition of Done
- US-MCP-4.5 sandbox_e2e harness exit summary:
PASS=5 SKIP=0 FAIL=0against digest-pinned images on the homelab runner -
SandboxConfirmedaudit event observed for every Real-mode run -
WeakSandboxModeNEVER observed in Real-mode (any occurrence is a hard fail) - Trust-gate decision recorded for every side-effecting tool call; none recorded for read-only calls
- Correlation-ID present and equal end-to-end across AIService -> MCP audit events and the tool result envelope
- Live run captured against deployed AIService (gated on IP-AIS-DEPLOY-1)
- Status ->
review(code lanes merged; integration lane pending)
Related
- [[US-MCP-4.5-Sandbox-E2E|US-MCP-4.5]] – the harness this story unskips
- [[US-MCP-4.1-File-Tools|US-MCP-4.1]] / [[US-MCP-4.2-Shell-Tool|US-MCP-4.2]] / [[US-MCP-4.3-Git-Tools|US-MCP-4.3]] – tool packages this story wires to a transport
- [[US-MCP-4.4-Sandbox-Profile|US-MCP-4.4]] – the
sandbox_check.gowhose verdict the audit emitter publishes - [[../../AI-Dev-Platform/ADR/ADR-004-Sandbox-Strategy|ADR-004]] – the controls the audit sink must surface
- [[../../AI-Service-Implementation/Epic-Identity/IP-AIS-DEPLOY-1-AIService-Go-Live|IP-AIS-DEPLOY-1]] – live-run dependency