Cory Policht
← All references
REF · US-MCP-4-6-REAL-TRANSPORT-UNSKIP · STORY

Us Mcp 4 6 Real Transport Unskip

source: MCP-Server-Implementation/Epic-4-Stories/US-MCP-4.6-Real-Transport-Unskip.mdscrubbed extract

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:

  1. A real MCP transport on the Go side. cmd/mcpserver/main.go is a stub. internal/tools/{file,git,shell} exist as libraries but are not wired to any callable surface. The W4 compose explicitly accepts this with depends_on: service_started.
  2. A non-mock C# adapter mode. AIService.Infrastructure.Adapters. Outbound.MCP.MockMCPClient returns printf-style strings; it never touches a container, never produces audit events, never fires a gate.
  3. An audit-event sink. internal/tools/shell/sandbox_check.go is a unit-tested helper; there is no pipeline emitting SandboxConfirmed / WeakSandboxMode / TrustGateDecision to 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:

  1. [[US-MCP-4.5-Sandbox-E2E|US-MCP-4.5]] – the five scenarios that must unskip, and the MCP_PROVIDER flip pattern.
  2. [[../../AI-Dev-Platform/ADR/ADR-004-Sandbox-Strategy|ADR-004]] – the controls the audit sink must surface (SandboxConfirmed, WeakSandboxMode).
  3. [[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.
  4. MCPServer/internal/mcp/registration.go – the existing ListTools-descriptor hook; the transport story consumes this.
  5. MCPServer/internal/tools/shell/sandbox_check.go – the in-container sandbox probe whose verdict the audit emitter publishes.
  6. 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-slim base (not distroless/static), HEALTHCHECK subcommand, non-root uid 65532.
  • Audit-event sink — concrete EventSink emitting SandboxConfirmed / WeakSandboxMode / TrustGateDecision / ToolInvocationStart / ToolInvocationEnd.
  • Non-mock C# adapter mode — merged PR #36 (1307bb5). MCP:Client=SDK + MCP:Sdk section in appsettings; MockMCPClient stays in Development.
  • CI flip: MCP_PROVIDER=Real set in system-integration.yml for the sandbox_e2e step. All five US-MCP-4.5 scenarios go from SKIP to PASS against the digest- pinned sandboxed MCPServer + AIService images.
  • Sandbox-image prereqs — debian:bookworm-slim includes python3 + git + ca-certificates.
  • workspace_file_contains precondition check (Cycle 2 deferred item from US-MCP-4.5): augment the helper in tests/system-integration/sandbox_e2e/_harness.py so it fails loudly if AIDP_WORKSPACE_HOST_PATH resolves outside the expected bind-mount root or the directory does not exist before the test starts. Prevents a phantom-pass where the helper silently returns False because 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/MCPServer PR #21 → main 9015015 (green SHA a5ec2fd). 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-controlled allowed_remotes). 2-reviewer + fix-loop clean.
  • C# activation lane — merged homelab/AIService PR #36 → main 2fc05e1 (green SHA 1307bb5). SDK adapter activated (HTTP/SSE), _meta.correlationId on 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.Id fallback). 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:

  1. Delete bogus 0.1.0 tag on homelab/DeveloperAutomationToolsServices (points at Jan-29 “notes sync” commit).
  2. DATS CI flip: docker-compose.ci.yml — restore depends_on: mcpexecutor: service_healthy; add MCP__Client=SDK + MCP__Sdk__Endpoint=http://mcpexecutor:8080/mcp to aiservice env. system-integration.yml — flip MCP_PROVIDER: "Mock" → "Real"; remove AIDP_STUB_MCP.
  3. Compose wiring: mcpexecutor transport image + seccomp/AppArmor security_opt + /workspace bind-mount.
  4. Redeploy on .106: MCPServer (new transport image) + AIService (MCP:Client=SDK).
  5. Live capture: run harness MCP_PROVIDER=Real → PASS=5 SKIP=0 FAIL=0.
  6. Phase C: cut real v0.1.0 tag, 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.

  1. Branch – feat/US-MCP-4.6-real-transport-unskip from origin/main in homelab/MCPServer. C# adapter changes go to homelab/AIService (coordinate cross-repo PR at review time).
  2. Load Context – read the bootstrap; confirm US-MCP-4.5 harness still SKIPs cleanly on main.
  3. Red – run the US-MCP-4.5 harness with MCP_PROVIDER=Real; all five scenarios fail with RealModeNotWired / equivalent. This is the target contract.
  4. Implement – transport, audit sink, C# Real adapter, compose wiring.
  5. Verify – harness green with MCP_PROVIDER=Real; sandbox probe verdict surfaces as SandboxConfirmed; trust-gate fires only on side-effecting tools.
  6. PR – MCPServer + AIService PRs; reviewers architecture-reviewer, go-standards, dotnet-architect.

Files to Modify

  • MCPServer/cmd/mcpserver/main.go – real composition root
  • MCPServer/internal/api/mcp/ – new package: transport surface
  • MCPServer/internal/infrastructure/audit/ – new package: audit emitter consuming sandbox_check.go verdict + tool invocation events
  • MCPServer/Dockerfile – HEALTHCHECK upgraded to probe transport + sandbox status
  • AIService/AIService.Infrastructure/Adapters/Outbound/MCP/ – new RealMCPClient (or wire up the existing MCPSdkClient / MCPGrpcClient)
  • DeveloperAutomationToolsServices/docker-compose.ci.yml – restore depends_on: service_healthy for mcpexecutor; set MCP__Provider=Real
  • DeveloperAutomationToolsServices/.gitea/workflows/system-integration.yml – flip MCP_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=0 against digest-pinned images on the homelab runner
  • SandboxConfirmed audit event observed for every Real-mode run
  • WeakSandboxMode NEVER 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)
  • [[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.go whose 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