Skip to content

MCP 2b-5: enable writes behind the full gate (completes #17)#33

Merged
thepagent merged 1 commit into
mainfrom
mcp-enable-writes
Jul 13, 2026
Merged

MCP 2b-5: enable writes behind the full gate (completes #17)#33
thepagent merged 1 commit into
mainfrom
mcp-enable-writes

Conversation

@thepagent

Copy link
Copy Markdown
Contributor

Summary

Final slice of #17: write tools become available for authenticated agents, behind every layer the previous slices built. This closes the remaining [2b] gate items and completes the Phase 2 gate — after this merges, v0.4.0 can ship per the release plan.

The gate, end to end

A write call must pass, in order:

  1. X-Ghpool-Key agent authentication (MCP Phase 2a: X-Ghpool-Key agent authn + default-deny tool allowlist #26)
  2. session-to-agent binding (MCP 2b-1: session-to-agent binding + dual-key rotation #28)
  3. default-deny tool allowlist (MCP Phase 2a: X-Ghpool-Key agent authn + default-deny tool allowlist #26)
  4. write classification + the new enable_writes flag
  5. repo allowlist, deny-if-unresolvable (MCP 2b-2: write classification + per-agent repo allowlists #30)
  6. per-agent in-flight cap (new: max_inflight_writes, default 4; at-cap → 429 before any side effect)
  7. fail-closed pre-flight audit record (MCP 2b-4: durable fail-closed audit for write-classified calls #32; unpersistable → 503, never reaches GitHub)
  8. forwarded with a scoped App installation token; response buffered, tool-level outcome (isError) audited (MCP 2b-4: durable fail-closed audit for write-classified calls #32)

New in this slice

  • enable_writes (default false) with startup validation: true requires [[mcp.agents]] + [mcp.github_app] + [mcp.audit] — the "never in network-trust mode, never on PATs, never unaudited" rules are enforced in code at boot, not documented hopes. Default upstream flips from /mcp/readonly to the full surface when set (explicit upstream always wins).
  • Scoped installation tokens (community-review adoption): an agent whose repos are all exact entries under a single owner gets tokens minted with the API's repositories parameter — GitHub itself enforces the repo boundary, demoting ghpool's argument parsing to defense-in-depth. Wildcards/mixed owners fall back to installation-wide. Per-envelope token cache (one credential per policy envelope, per the homogeneous-pool rule).
  • Per-agent in-flight write cap via RAII guard.
  • Safe-retry semantics: ghpool never retries forwarded calls; ambiguous outcomes (e.g. connection lost mid-response) are audited as undeterminable (tool_error: null) and surfaced — retry decisions belong to the caller. This was already true; it is now documented as a guarantee and closes the [2b] safe-retry item.
  • Docs: README write section, required App permission set table (closes that [2b] item), revocation semantics (in-memory pins + restart = revocation; upstream DELETE is a no-op so ghpool is the session authority — closes the design-note item).

Tested

84 tests (7 new): validation matrix (each missing section rejected, full set accepted), upstream default flip, scope_envelope rules (exact/single-owner → names; wildcard/mixed/empty → installation-wide), scoped-mint per-envelope caching against the mock GitHub API, write allowed end-to-end (200 + X-MCP-Tools injected + request/result audit records with agent attribution), denials still enforced with writes on (wrong repo / off-allowlist → 403, audit untouched), in-flight guard unit + 429-at-cap integration. Clippy -D warnings clean.

Deliberately not in this PR

  • Live write e2e: creating real issues from CI needs a sandbox-repo decision (and shouldn't ride the daily cron). Proposed as a follow-up: an opt-in e2e section gated on an explicit env var, exercised manually per release.
  • Per-agent permissions scoping at mint (repo scoping ships now; permission scoping is a compatible follow-up).

After this

All 14 #17 gate items complete → v0.4.0 release (per the recorded plan).

Final slice of #17's [2b] items — write tools become available, bounded by
every previously shipped layer plus this slice's additions:

- enable_writes flag (default false). Startup validation HARD-REQUIRES
  [[mcp.agents]] + [mcp.github_app] + [mcp.audit] — writes are never
  available in network-trust mode, never run on pooled PATs, never
  unaudited. Default upstream flips to the full write surface when set.
- Scoped installation tokens (community review): when an agent's repos
  are all exact entries under one owner, its token is minted with the
  repositories parameter — GitHub enforces the repo boundary itself.
  Wildcards/mixed owners fall back to installation-wide (proxy checks
  remain). Per-envelope token cache in AppTokenProvider.
- Per-agent in-flight write cap (max_inflight_writes, default 4, 0 =
  unlimited) via RAII guard; at-cap writes get 429 before any side effect.
- Safe-retry: ghpool never retries forwarded calls (documented; ambiguous
  outcomes are audited as undeterminable and surfaced to the caller).
- Docs: README write section incl. required App permission set; revocation
  semantics (restart clears in-memory pins; upstream DELETE is a no-op —
  ghpool is the session authority).

84 tests (7 new: write-gate validation matrix, upstream default flip,
scope_envelope rules, scoped-mint envelope caching, write allowed e2e with
audit trail + X-MCP-Tools, denials still enforced with writes on, in-flight
guard + 429 at cap).
@thepagent
thepagent merged commit 283b171 into main Jul 13, 2026
1 check passed
@thepagent
thepagent deleted the mcp-enable-writes branch July 13, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants