The HTTP routes call requireCapability at every paid chokepoint, but the
MCP/agent path bypassed the paywall entirely: the three external-service
tools stage operations whose commit calls the email / Skatteverket services
directly, with no capability check. After the 2026-07-07 trial cutover a
trial-connected non-payer using the gnubok MCP connector could still send
invoice emails and file AGI/VAT.
Close the gap with two layers, mirroring the existing TOOL_SCOPE_MAP gate:
- Dispatch gate (mcp-server/server.ts): MCP_TOOL_CAPABILITY_MAP, checked
right after the scope check, blocks a non-entitled company before any
pending op is staged. Emits errorKind='capability_denied' telemetry.
- Commit-time gate (commitPendingOperation): PAID_OPERATION_CAPABILITY_MAP,
checked before the atomic claim. The real external-service chokepoint —
applies to the MCP approve tool AND the UI approval path, and closes the
trial-connected-token window (the grant has expired by commit time). A
blocked op stays 'pending', so it is re-approvable once the company subscribes.
Adds a transport-free capabilityBlockedError() helper (shared bilingual
copy) and locks both maps with tests (maps, dispatch gate, commit gate).
Only the three write/submit tools are gated; SKV read/local tools stay free
per the statutory carve-out. No DB/migration change; self-hosted stays all-on.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>