Files
CIAgent 0c15d3d0b2 docs(milestone): complete M2 — MCP Layer & Day 1 Adapters (v0.2)
M2 delivers the read-only MCP capability broker gateway and four Day-1
infrastructure adapters (Proxmox, SSH/Linux, GitHub, Gitea). 13 REQs (015-027)
all pass. 656 tests green. M1 non-regression verified.

MCP spec 2025-06-18 conformance verified (PROTOCOL.md + 7 tests).
Defense-in-depth SSH (broker layer 1 + Relay Agent layer 2 + no-shell exec).
Two-track LLM smoke (Track A mock-path P0 gate passes).
CI: Gitea Actions (.gitea/workflows/ci.yml) with Postgres 16 + RLS verification.

Phases shipped:
  P0  pre-execution          v0.1.0
  P1  Wave F — MCP gateway   v0.1.1
  P2  Wave G — Proxmox       v0.1.2
  P3  Wave H — SSH/Linux     v0.1.3
  P4  Wave I — Git adapters   v0.1.4
  P5  Wave J — SSE+smoke+UI  v0.1.5
  P6  Final — review+ship    v0.1.6 ← milestone release

---ci---
phase: 6
milestone: v0.2
status: complete
phase_role: final
milestone_complete: true
requirements:
  covered: [REQ-015, REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027]
  partial: []
---/ci---
2026-08-25 06:14:21 +00:00

13 KiB

Requirements

Source: CoreCI Chat v0.1 M2 Engineering Specification v1.0 (.ciagent/steer-m2-spec.md), Sarah Chen (PO), locked 2026-08-25. All 9 open questions resolved. D-006 (GitHub scopes) and D-007 (MCP transport) recorded as deviations. Milestone type: Feature (new MCP adapters are feat: phases). Tags run on the v0.1.x patch line (M1's previous minor): phase 0 seeds v0.1.0.

Predecessor M1 (COMPLETE): REQ-001..014, 038, 039, 040 (17 REQs, all PASS, shipped v0.0.1..v0.0.7).

M2 Requirements (this milestone — REQ-015..027, 13 REQs — COMPLETE, shipped v0.1.6)

All acceptance criteria verbatim from M2 spec §4. Every REQ inherits M1 invariants: INV-1 (auth gateway ordering), INV-2 (withTenant + RLS), INV-3 (SecretProvider only), INV-4 (audit completeness), INV-7 (read-only). The broker is the load-bearing safety boundary for INV-7.

MCP Capability Broker Gateway

  • REQ-015 (J2, High) Define abstract MCP tool schema — Given the broker exposes the closed read-only tool set, when a tool is registered, then it has name, description, inputSchema (JSON Schema) per MCP standard, the schema is in the broker's tool registry before any adapter invocation, any tool call with arguments not matching inputSchema returns HTTP 400 with a schema-validation error, and the tool registry is closed and enumerated with per-tenant policy able to disable individual tools but never add new ones. M2 starter set is locked at: proxmox.list_vms (inventory), proxmox.get_vm_status (live), proxmox.get_node_metrics (live), ssh.run_whitelisted_command (live), github.list_repos (inventory), github.get_recent_ci_runs (live), github.get_workflow_run (live), gitea.list_repos (inventory), gitea.get_recent_ci_runs (live).
  • REQ-01[5-9] (J1, J2, High) Route abstract MCP calls to tenant-specific adapter — Given an MCP tool call request with a tenant-scoped adapter binding (tenant_id, adapter_type, target_id), when the broker receives the call, then the call is routed to the adapter resolved by that tuple, the response is returned as an SSE stream, and routing errors return HTTP 404 with a structured error.
  • REQ-01[5-9] (J2, High) Stream tool execution output to chat UI via SSE — Given the broker invokes an adapter capability, when the adapter returns partial or complete output, then the broker emits an SSE stream on GET /api/mcp/stream/:correlationId with Content-Type: text/event-stream and each event has id, event, data fields per the SSE specification; the stream terminates with a terminal event (done or error) on completion or error. Per-call lifecycle: one stream per capability invocation; correlation ID = ULID minted at POST /api/mcp/invoke. Client disconnect (Edge 8) cancels in-flight adapter call; no audit event for client-side cancellation.
  • REQ-01[5-9] (Edge 1, Edge 7, High) Enforce read-only at MCP gateway proxy layer — Given a write-capable method per the adapter's known write surface (Proxmox: POST/PUT/DELETE; SSH: non-whitelist commands; GitHub: scopes outside metadata:read+actions:read; Gitea: POST/PUT/DELETE/PATCH on all endpoints), when the request reaches the broker, then the broker rejects with HTTP 403, appends adapter.write_rejected audit event, and never invokes the adapter; verified by a test per adapter at the M2 gate. The broker is the load-bearing safety boundary (INV-7 enforcement at the gateway, not at the adapter).
  • REQ-01[5-9] (Edge 5, High) Apply token-bucket rate limit per user and per tenant — Given a user has exceeded 60 req/min OR a tenant has exceeded 300 req/min, when any subsequent capability invocation is attempted, then the broker returns HTTP 429 with Retry-After header and no adapter call is made; rate limit state is process-local in M2 (in-memory token-bucket, capacity = rate, refill 1/sec user / 5/sec tenant).

Adapters — Day 1 Integrations

  • REQ-020 (J1, J2, High) Implement read-only Proxmox MCP adapter — Given a Proxmox adapter is configured with a PVEAuditor-scoped API token, when an MCP tool call routes to it, then the adapter calls only Proxmox GET endpoints (e.g., /api2/json/nodes, /api2/json/qemu, /api2/json/nodes/{node}/qemu/{vmid}/status/current) and never mutates state; supported capabilities include proxmox.list_vms (inventory), proxmox.get_vm_status, proxmox.get_node_metrics.
  • REQ-021 (J1, J2, High) Implement read-only SSH/Linux Server MCP adapter — Given an SSH adapter is configured via M1 Relay Agent (REQ-026), when an MCP tool call routes to it, then the adapter invokes only commands from the fixed whitelist subset (uptime, df -h, free -m, systemctl status <svc>, journalctl -n <N>, systemctl list-units --type=service) via the M1 Relay whitelist hook; the broker validates command against this subset BEFORE dispatch to the Relay Agent (defense-in-depth layer 1); the Relay Agent CheckCommand is the second enforcement layer (layer 2); non-whitelist commands return HTTP 403 (REQ-026).
  • REQ-022 (J1, J2, High) Implement read-only GitHub MCP adapter — Given a GitHub adapter is configured with a fine-grained PAT (metadata:read + actions:read minimum per D-006), when an MCP tool call routes to it, then the adapter calls only GitHub REST GET endpoints and rejects any token lacking required scopes; supported capabilities include github.list_repos (inventory), github.get_recent_ci_runs, github.get_workflow_run.
  • REQ-023 (J1, J2, High) Implement read-only Gitea MCP adapter — Given a Gitea adapter is configured with a read-only token, when an MCP tool call routes to it, then the adapter calls only Gitea REST GET endpoints and rejects any token lacking required read scopes; version-aware validation: Gitea ≥1.22 requires read:repository scope; Gitea <1.22 accepts any token with broker-side write-method blocklist (POST/PUT/DELETE/PATCH) as security backstop; supported capabilities mirror the GitHub adapter (gitea.list_repos, gitea.get_recent_ci_runs).
  • REQ-024 (Edge 3, High) Scope MCP queries to explicitly selected target in multi-target tenants — Given a tenant has multiple adapters of the same type configured (e.g., 2 Proxmox hosts), when a capability invocation is received without an explicit target_id for that adapter type, then the broker returns HTTP 400 "target required" with a list of available targets; the UI surfaces a target picker.

Adapter Authentication

  • REQ-025 (J1, High) Authenticate to Proxmox via scoped API token + PVEAuditor — Given a Proxmox adapter config submission, when the token is submitted, then the broker verifies the token's role on the target is PVEAuditor before persisting; tokens without PVEAuditor return HTTP 422 with role-violation error and no config is persisted; the token is stored via SecretProvider.set (INV-3).
  • REQ-026 (J1, Edge 7, High) Authenticate to Linux servers via SSH key + whitelist execution — Given an SSH adapter config submission, when the Relay registration token is stored via SecretProvider.set (INV-3), then all subsequent SSH commands are validated against the fixed whitelist subset at two layers: (1) broker validates command before dispatch, (2) M1 Relay Agent CheckCommand validates at execution; non-whitelisted commands return HTTP 403 with a structured error and adapter.write_rejected audit event is appended.
  • REQ-027 (J1, High) Authenticate to GitHub and Gitea via scoped API tokens — Given a GitHub or Gitea adapter config submission, when the token is submitted, then the broker validates token scopes before persisting (GitHub: fine-grained PAT with metadata:read + actions:read minimum per D-006; Gitea ≥1.22: read:repository minimum; Gitea <1.22: any token accepted with broker-side write-method blocklist as security backstop); insufficient scopes return HTTP 422 with scope-violation error and no config is persisted; the token is stored via SecretProvider.set (INV-3).

M1 Requirements (predecessor — COMPLETE, for non-regression reference)

All 17 M1 REQs (001-014, 038, 039, 040) are COMPLETE and must remain passing through M2. See M1 REQUIREMENTS.md history (git log) for the verbatim acceptance criteria. M2 adds no breaking changes to M1 systems except additive (new tables, new audit event types).

Deferred to M3 (REQ-028 → REQ-037, REQ-041 → REQ-044 — Chat, Orchestration, Hardening)

Listed for traceability; NOT in M2 scope. M3 acceptance gate: Operator can ask a natural-language diagnostic question, see streamed tool execution, and receive a cited, evidence-backed answer within 5 min p95. Async workflows persist and rejoin correctly. Usage is metered. SOC 2 controls are instrumented. All v0.1 release gates pass.

REQ-028 (chat UI), REQ-029 (NL input), REQ-030 (streaming response + citations), REQ-031 (tool traces), REQ-032 (conversation history), REQ-033 (reason about tools), REQ-034 (multi-step workflows), REQ-035 (≤20 step limit), REQ-036 (durable execution), REQ-037 (rejoin workflow), REQ-041 (SOC2 posture page), REQ-042 (Vanta instrumentation), REQ-043 (usage metering), REQ-044 (usage dashboard).

M2→M3 contract freeze (M2 spec §9): M2's gateway (5 endpoints: GET /api/mcp/tools, POST /api/mcp/invoke, GET /api/mcp/stream/:correlationId, POST /api/mcp/adapter, PATCH/DELETE /api/mcp/adapter/:id) is a stable contract from M2's acceptance gate onward. M3 treats these as a stable API. M3 token-streaming SSE endpoint (for LLM output tokens) is a separate design — not in M2 scope, but documented as the M3 chat orchestration requirement.

Out of Scope (M2 — do not build)

Feature Reason
LLM chat UI / orchestration M3 (M2 spec §2.2)
Trigger.dev task execution M3 (M2 spec §2.2)
Any write capability INV-7 hard; broker rejects 100% of write attempts (M2 spec §2.2)
S3 Object Lock WORM audit M3 per D-004 (M2 spec §2.2)
Vanta evidence collection M3 (M2 spec §2.2)
New Postgres tables for MCP caching Q4 decision: in-memory only (M2 spec §2.2)
5+ Day-1 adapters Only Proxmox, SSH, GitHub, Gitea (M2 spec §2.2)
Persistence of MCP results beyond audit events No snapshot/time-series tables (M2 spec §2.2)
Cross-tenant adapter sharing Adapters are per-tenant (M2 spec §2.2)
Custom MCP server authoring tools for customers v1.2+ (M2 spec §2.2)
Custom capability tool set per tenant Fixed tool registry; per-tenant policy may disable but not add (M2 spec §2.2)
Write actions (apply/delete/scale/restart/VM start-stop) v1.1 (M1 spec §2.2)
Hosted LLM inference Never — BYOM permanent (M1 spec §2.2)
Kubernetes / ArgoCD / Helm Not planned (M1 spec §2.2)
Slack / Teams / Discord / CLI / mobile chat surfaces v1.1+ / not planned (M1 spec §2.2)
Approval-gated remediation, Senior Approver persona v1.1 (M1 spec §2.2)
RAG over historical incidents v1.1 (M1 spec §2.2)
SOC 2 Type 1 final certification Audit-in-progress posture only (M1 spec §2.2)
Custom RBAC roles beyond Admin/Operator/Viewer v1.2+ (M1 spec §2.2)
BYOK / customer-managed encryption keys v1.2+ (M1 spec §2.2)
Multi-region deployment Single region MVP (M1 spec §5)
Windows server management Not planned v1.x (M1 spec §2.2)
Fine-tuning, custom model deployments Not planned (M1 spec §2.2)
Anything not in the spec Flag as spec-time scope question; never silently add

Traceability

Requirement Milestone Phase Status
REQ-001 M1 Wave B complete
REQ-002 M1 Wave B complete
REQ-003 M1 Wave B complete
REQ-004 M1 Wave B complete
REQ-005 M1 Wave B complete
REQ-006 M1 Wave C complete
REQ-007 M1 Wave C complete
REQ-008 M1 Wave C complete
REQ-009 M1 Wave C complete
REQ-010 M1 Wave D complete
REQ-011 M1 Wave D complete
REQ-012 M1 Wave D complete
REQ-013 M1 Wave D complete
REQ-014 M1 Wave E complete
REQ-015 M2 Phase 1 (Wave F) complete
REQ-016 M2 Phase 1 (Wave F) complete
REQ-017 M2 Phase 1+5 (Wave F+J) complete
REQ-018 M2 Phase 1+2+3+4 (Wave F+G+H+I) complete
REQ-019 M2 Phase 1 (Wave F) complete
REQ-020 M2 Phase 2 (Wave G) complete
REQ-021 M2 Phase 3 (Wave H) complete
REQ-022 M2 Phase 4 (Wave I) complete
REQ-023 M2 Phase 4 (Wave I) complete
REQ-024 M2 Phase 1 (Wave F) complete
REQ-025 M2 Phase 2 (Wave G) complete
REQ-026 M2 Phase 3 (Wave H) complete
REQ-027 M2 Phase 4 (Wave I) complete
REQ-028 M3 deferred
REQ-029 M3 deferred
REQ-030 M3 deferred
REQ-031 M3 deferred
REQ-032 M3 deferred
REQ-033 M3 deferred
REQ-034 M3 deferred
REQ-035 M3 deferred
REQ-036 M3 deferred
REQ-037 M3 deferred
REQ-038 M1 Wave A complete
REQ-039 M1 Wave A complete
REQ-040 M1 Wave A complete
REQ-041 M3 deferred
REQ-042 M3 deferred
REQ-043 M3 deferred
REQ-044 M3 deferred