Skip to content

[EPIC] MCP Execution Readiness and Bounded Context Grants #172

Description

@Joncallim

Issue Type

Epic.

Priority: P0 for execution reliability. This is the next architecture epic after the Executable Workforce Beta (#119, closed) because MCP ambiguity is now the main reason approved tasks can still stall at handoff.

Canonical architecture (handoff-ready, no reasoning required to implement): docs/adr/0009-mcp-admission-contract.md. Every child slice below references it.

Context

Forge distinguishes three kinds of MCP involvement, but the code does not distinguish them in one place:

  1. Planning-only MCP context — the Architect says an MCP would help; Forge records it only as run-scoped prompt instructions. It grants nothing.
  2. Bounded read-only context grants — Forge assembles a project-scoped, inspectable read-only context packet (file names, selected excerpts) after an explicit operator grant.
  3. Live MCP tool handles — a worker calling an MCP at runtime. This is deferred; it is not implemented in this epic and must read as a product boundary, not a broken install.

Current failure mode (observed): the Architect produced a valid plan and work packages; MCP access was shown as planning-only; but package handoff still blocked on MCP grant semantics. filesystem.project.write commonly appears in plans even though Forge writes generated files through its sandbox execution JSON path, not live MCP tools. Required filesystem read/list/search requirements, prompt-only overlays, project-level filesystem approval, and no-capability MCP entries are handled by different code paths with subtly different rules.

Root cause: the same beta capability policy is re-derived by four overlapping code paths that do not agree (plus a fifth client-side copy). Full evidence with file:line anchors is in ADR 0009 §Context:

  • validateMcpExecutionDesignweb/worker/mcp-execution-design.ts:326-454 (approval validation)
  • deriveMcpGrantDecisions / decisionStatusweb/worker/mcp-execution-design.ts:753-867 (preview)
  • evaluateWorkPackageMcpBrokerweb/worker/mcp-execution-design.ts:599-739 (handoff)
  • requiresFilesystemGrantApproval / summarizeFilesystemCapabilitiesweb/lib/mcps/filesystem-grants.ts:90-339
  • client re-implementation — web/app/dashboard/tasks/[id]/page.tsx:348-444

The most important consequence: web/app/api/tasks/[id]/approve/route.ts never runs the handoff broker — it gates only on the filesystem path — so a package the broker will block at handoff for a non-filesystem reason passes approval and stalls later. This epic makes planning, approval, UI state, filesystem grants, and handoff follow one admission contract so this cannot recur with the next workforce slice.

Desired Outcome

Forge should let normal approved work packages advance without confusing MCP blockers, while keeping live MCP tool access out of scope until a deliberate later security-reviewed slice.

Architecture principles

  1. One contract, many surfaces. The same MCP admission decision drives preview badges, approval blocking, handoff blocking, and recovery messaging.
  2. Planning is not permission. Architect proposals and prompt overlays are context, not tool grants.
  3. Bounded context is explicit. File/repository context is packaged, visible, and attached to run evidence.
  4. Writes stay on the Forge execution path. Generated files are written through sandbox output and Forge-applied host-repository writes, not live MCP filesystem write tools.
  5. Blocks must be actionable. Every blocked MCP state names the exact missing grant, unhealthy MCP, unsupported capability, or deferred feature.
  6. No silent privilege widening. Broad aliases cover project read/list/search only. Write, destructive, admin, secrets, and merge operations remain blocked.

The admission model — every surface consumes one package-level evaluation (admitWorkPackageMcpMcpWorkPackageAdmission) built from one per-requirement producer (admitMcpRequirementMcpAdmissionDecision); validateMcpExecutionDesign, deriveMcpGrantDecisions, evaluateWorkPackageMcpBroker, and requiresFilesystemGrantApproval become shape-preserving adapters over it. Full types + producer + adapters + the total precedence-ordered decision table are in ADR 0009; the per-requirement decision is:

type McpAdmissionDecision = {
  schemaVersion: 1
  mcpId: 'filesystem' | 'github' | string
  agent: string
  requirement: 'required' | 'optional'
  requestedCapabilities: string[]
  normalizedCapabilities: string[]
  capabilityClasses: Array<{ capability: string; class: McpCapabilityClass; deliveryKind: McpDeliveryKind | null }>
  mode:
    | 'planning_only'
    | 'bounded_context_required'
    | 'bounded_context_approved'
    | 'blocked'
    | 'deferred_live_mcp'
    | 'unknown_legacy'          // pre-#172 artifact; never re-invented
  status: 'allowed' | 'warning' | 'blocked'
  reason: string
  recoveryAction?:
    | 'continue_as_prompt_context'
    | 'approve_project_filesystem_context'
    | 'install_or_fix_mcp'
    | 'revise_plan'
    | 'defer_live_mcp_feature'
  evidenceRefs: string[]          // PLANNED scope pre-run (root + capability set); run evidence added by S4
}

Cross-surface parity is asserted on the canonical (mode, admissionStatus) pair. The grant-preview adapter keeps its legacy status field (allowed → proposed) and adds a canonical admissionStatus: 'allowed' | 'warning' | 'blocked' so shape preservation and literal status parity both hold.

Capability classes (sourced from MCP_CATALOG.runtime.capabilities, one classifier):

  • Planning-only — prompt overlays, MCP-aware subtasks, filesystem.project.write. Warn, do not block, when no live MCP handles are issued.
  • Bounded read-onlyfilesystem.project.read|list|search. May require explicit project-level filesystem approval; produce visible effective grant metadata.
  • Deferred live MCP — live tool handles; github write/branch/pr/merge/settings/secret; filesystem write/delete/admin (an enumerated set of recognized live-tool families). Blocked/deferred until a later security-reviewed runtime-grant epic. A first-class named mode, not a generic "beta scope" error. A required deferred capability blocks with revise_plan; an optional one warns with defer_live_mcp_feature.
  • Unknown / typo — a capability naming no known MCP, or a known MCP with an unrecognized/typo capability, is unknown and blocks with revise_plan — never silently treated as safe or as deferred.

Operator UX contract — the task detail page answers four questions without logs: (1) is this just planning context? (2) does this package need bounded project filesystem context (show the exact grant)? (3) is an MCP unhealthy/missing (link setup/retry)? (4) is this a deferred-live-MCP feature (say deferred, not broken install)?

Scope boundary: live MCP tool handles remain unissued; filesystem.project.write is planning-only or normalized into the sandbox write path; required filesystem read/list/search stays explicit, recoverable, and auditable; approved tasks cannot enter a handoff block that was already visible at approval.

Tasks

The epic is delivered as six sweeping child slices plus the existing Architect-assignment slice. All are linked as sub-issues of this epic and detailed with file-level architecture in their own bodies and in ADR 0009.

Child slices (dependency-ordered):

Dependency graph

#176 (S1) ─► #177 (S2) ─┬─► #178 (S3) ───────────────► #181 (S6)
                        └─► #179 (S4) ─► #180 (S5) ──► #181 (S6)
#43 (planning) ───────────────────────► #179 (S4)

Related issues (stay independent; dependencies/dependents, not subsumed):

Acceptance Criteria

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Priority 0: urgent bug or vulnerabilityenhancementNew feature or requestready-for-agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions