Prerequisite: MCP triple-connect overview (connect first, then lock down permissions); setup walkthrough in progress. For quick jumps, use the problem map below; for the full spec, read the entire article—this is the MCP permissions authoritative spec (v1.0), not a connectivity tutorial.
Problem map (search entry · jump to section)
- How do I configure Claude Code MCP permissions? →
~/.claude.jsonminimal example · Control plane · Policy matrix - How do I minimize GitHub PAT scope? → GitHub MCP · PAT and scopes
- Should CodeGraph MCP be read-only? → CodeGraph read-only index · FAQ
- How do I keep MCP away from production? → API staging read-only · Trust boundary · Checklist
- Permissions look right but errors or odd behavior? → Incident paths · planned MCP troubleshooting (L4-Q07)
Need a 5-minute setup? A lightweight how-to page (L4-Q08) will cover copy-paste steps; this article keeps architecture and security-review depth.
Reading path (~15 min · full spec): Problem map → One-line summary → System model → Trust boundary and data flow → Policy matrix and control plane → Config snippets → Attack chain. Suitable as team SOP or security-review attachment.
One-line security model
MCP permission safety means splitting what the Agent can see from what it can write, and defaulting everything to read-only. Writes, production reach, and CI credentials must sit outside the trust boundary—or be explicitly enabled in a separate config profile.
Default principle
Index and context are read-only; write access must be explicitly isolated (separate MCP service / token / environment). Code changes go through local git; production API and Runner credentials do not belong in Claude MCP config.
Scope: authoritative spec (problems + auditability)
The problem map above ties search intent to concrete sections; from here we enter the security architecture spec. Connectivity acceptance checks whether tools are registered (mcp__github__*); permission acceptance checks reach at call time: paths, APIs, GitHub resources, and secret scope.
When Claude Code, CodeGraph, and GitHub Runner share a Cloud Mac host, MCP tokens and Runner secrets must be separate accounts—never share a GitHub App private key or place production DATABASE_URL in the Agent workspace (remote Mac context: Cloud Mac vs local Mac).
Layer 1 · System model (anchor before the matrix)
Triple-connect MCP splits into three roles—each with a distinct read/write risk boundary, not three “plug and forget” extensions (architecture: MCP overview):
Claude Code session
│
├─ GitHub MCP → intent (where work comes from: issue / PR / comment)
├─ CodeGraph MCP → context (what to change: impact / symbols / deps)
└─ API MCP → truth (is it correct: staging data / contracts)
Risk axis: intent-layer writes hit collaboration surfaces;
context-layer writes corrupt index or source;
truth-layer writes mix staging and production data.Deployment cross-reference: CodeGraph in five minutes covers “graph is available”; this article covers “graph and API default to read-only in front of the Agent.” Series part 1: architecture / triple-connect; part 3 planned: MCP threat model (STRIDE · attack surface · credential flow).
Trust boundary
For architecture reviews, cite this diagram: the Agent session sits in the inner zone; MCP is the sole gateway to external systems. Any call that crosses production, a writable filesystem, or CI credentials is a boundary violation.
┌─────────────────────────────────────────────────────────┐
│ Trust zone A · Claude Code session (user / Agent) │
│ │
│ ┌──────────────── MCP layer ─────────────────┐ │
│ │ GitHub MCP (intent) ──► external collab │ │
│ │ CodeGraph MCP (ctx) ──► local index/graph│ │
│ │ API MCP (truth) ──► staging database │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
[ GitHub API ] [ .codegraph/ ] [ staging API ]
external · collab local · read-only non-production
Trust boundary (must not cross by default):
✗ Production database / production API URL
✗ Writable filesystem (whole repo / monorepo root)
✗ CI / Runner credentials (GITHUB_TOKEN · App private key)
✗ .env.production · adjacent project secretsMatrix mapping: rows “production = deny” and “CI/Runner = do not attach to MCP” operationalize the boundaries above.
Data flow
Permission problems are really about how data moves between Agent → MCP → external systems and where side effects appear. The boundary diagram answers “may it cross?”; data flow answers “what happens after it crosses?”
User prompt ↓ Claude Code (Agent state · conversation context) ↓ MCP tool call (GitHub / CodeGraph / API) ↓ External response (issue body · graph JSON · staging rowset) ↓ Agent interpretation (plan · PR description · next tool args) ↓ Side effects (PR comment · DB INSERT · .codegraph/ rewrite · CI trigger) Control points (policy matrix rows): · Before MCP call → read-only MCP service / minimal scope · Before re-inject → PII redaction · no production fields · Before side effect → write service disabled → tool chain should fail
The attack-chain walkthrough below (§ Misconfiguration attack chain) follows this flow step by step: which hop’s response the Agent misuses, and which PR / table / index side effect results.
Common permission incident paths (how Agents mis-operate)
These happen even when config “looks reasonable”—use as code-review and security-review checklist items:
- GitHub MCP writes comments / labels → triggers CI bots or auto-merge rules that depend on comments, advancing the pipeline without human confirmation.
- CodeGraph or filesystem MCP with repo write access → Agent fixing an issue rewrites
.codegraph/or adjacent subproject config; index is poisoned andcodegraph_impactis untrustworthy. - API MCP with staging write access → test rows mix with production mock fields; Agent treats wrong schema as “truth” in the PR description.
- Filesystem MCP reads workspace root → scans
.env.productionor adjacent monorepo secrets into conversation context or issue comments.
Layer 2 · Default policy principles (conclusions before the matrix)
The matrix answers “how to configure each row”; this section keeps three team-policy conclusions (without re-defining the three MCP roles):
- Claude sessions default to read-only MCP services (
*-ronaming); any write needs a short-lived token plus an explicitly enabled second MCP service. - CI / Runner is not exposed through MCP; nightly index rebuilds and integration-test writes run in workflows, not from chat.
- Production is invisible to the Agent: no production URL, no
DATABASE_URL, no merge/push PAT permanently in~/.claude.json.
Policy matrix (recommended default rows)
Designed for machine execution and review: beyond resource and default policy, columns mark Claude auto-call, CI trigger, and production reach for policy-as-code or onboarding checklists. Write needs require PR justification and expiry.
| MCP / resource | Stack role | Default policy | Claude session | CI / Runner | Production | Common misconfig |
|---|---|---|---|---|---|---|
| GitHub repo | Intent | PAT read + issues (as needed); short-lived token for PR/push writes |
Allow (read-only service) | Do not attach MCP; use App / GITHUB_TOKEN |
Deny production-branch writes | Full repo write permanently in ~/.claude.json |
CodeGraph .codegraph/ |
Context | Process read-only; tools limited to query / impact | Allow (read-only) | Allow nightly index rebuild job | N/A | Whole repo writable via MCP |
| API / database | Truth | Staging read-only account; SELECT-only tools | Allow api-ro |
Integration tests may use separate api-rw schema |
Deny | Production DATABASE_URL in Claude workspace |
Local .env* |
— | Not exposed via MCP; docs + .env.example |
Deny full-directory filesystem scan | Secrets only in Runner vault | Deny reading .env.production |
Workspace at monorepo root scans neighbor projects |
| Runner / CI secrets | — | Do not enter mcpServers |
Deny | Allow (Runner only) | Deny sharing with MCP | Shared GitHub App private key |
Control plane (who enforces policy)
The matrix answers “what should we do?”; this section answers “who enforces it at which layer”—turning best practices into enforceable tiers.
Policy enforcement points: 1. MCP service config (~/.claude.json · static scopes · read-only default) 2. OS / filesystem (repo and .codegraph/ read-only mount · workspace isolation) 3. GitHub App / PAT console (fine-grained · single repo · expiring write tokens) 4. CI pipeline (production URL gate · secret scan · ban PAT in repo) 5. Runner vault (CI creds separate from MCP env · not in mcpServers) Maps to matrix columns: · Claude session → ①② · CI / Runner → ④⑤ · Production → ③④ (reject prod endpoints / permanent full-write PAT)
A printable checklist will be single-paged (planned MCP best-practices checklist, see § Further reading); connectivity errors go to the planned MCP troubleshooting page—separate from this permissions spec.
GitHub MCP · PAT and scopes
Goal: let Claude Code read issues and cite them in comments, without permanently handing “merge PR” capability to the conversational Agent.
Recommended tiers
- Daily development (read-heavy):
repo(private repo read),read:org(if multi-repo under org); addissuesfor issue-driven flow. - Automated writes (short-lived): separate fine-grained PAT, single repo, Contents/Issues write checked, 7–30 day expiry, remove from
mcpServerswhen done.
Token storage: environment variables (e.g. GITHUB_MCP_TOKEN) preferred—never commit to repo; on Cloud Mac use instance-level secrets, on local Mac inject via Keychain / 1Password.
# Example: scope intent only—verify against current GitHub PAT UI # Fine-grained · single repo · Contents read + Issues read/write
CodeGraph · read-only index practice
CodeGraph’s value is querying the graph, not letting MCP mutate index files. Recommendations:
- After
codegraph initat repo root, treat.codegraph/as build output (gitignore or separate backup). - MCP process is read-only on source; rebuild index via human or CI script, not chat.
- If Agent reports empty
codegraph_impact, check index and working directory first (overview problem table)—do not grant write access first.
Large-repo context: CodeGraph and AI coding agents; permission policy unchanged: graph stays read-only.
API MCP · staging read-only
API MCP aligns “what real data looks like.” Least-privilege approach:
- Configure staging base URL only; production URL never appears in
mcpServersenv block. - DB account
SELECTonly; for writes use another MCP service name (e.g.api-staging-rw), disabled in Claude config by default. - If responses contain PII, redact at MCP layer or forbid pasting raw JSON into issue comments.
~/.claude.json minimal example (structure)
Field names may vary by version; the point is split MCP services, split env vars, minimize default enabled set.
{
"mcpServers": {
"github-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_TOKEN_READONLY}"
}
},
"codegraph": {
"command": "codegraph",
"args": ["mcp"],
"env": {
"CODEGRAPH_READ_ONLY": "1"
}
}
}
}Note: CODEGRAPH_READ_ONLY is illustrative—check your CodeGraph / MCP implementation; if no switch exists, use OS permissions (read-only mount) for the same effect.
Pre-launch checklist (summary · full page planned)
- □ Is GitHub PAT minimum scope and not committed to git?
- □ Is there an API MCP service that writes production? If yes, is it disabled by default?
- □ Does CodeGraph expose only impact/query tools?
- □ On Cloud Mac, does the Claude workspace point to a single repo, avoiding neighbor
.envfiles? - □ Is the Runner registration token separate from the MCP token?
- □ Do team members know which doc to update after tightening permissions? (this article + overview)
Misconfiguration attack chain walkthrough
Based on common real-world combos: config looks “efficient” on the surface, but chained Agent calls exceed permission intent.
1. Initial config (looks reasonable)
- Single
mcpServersprofile: GitHub MCP (repowrite) + filesystem + CodeGraph in one process. - CodeGraph writable at monorepo root; API MCP points to staging but account has INSERT.
- Claude workspace = repo root, no
.env*exclusion.
2. Agent trigger
User: “Fix bug from issue #42 and update reproduction steps in the PR description.” Agent calls GitHub read issue → CodeGraph impact → API sample from staging → filesystem config change.
3. Permission escalation path
- Filesystem writes config in adjacent subproject (not the issue target path).
- CodeGraph rewrites
.codegraph/under write access; impact results drift. - API tool INSERTs test row; field names match production mock; Agent copies into PR body.
- GitHub MCP posts comment with write scope; label rule fires; CI auto-queues.
4. Data / code impact
- Wrong config lands in PR; staging DB has dirty rows; untrustworthy index causes follow-on Agent sessions to misjudge.
- If PR description contains staging
DATABASE_URLfragment, secret scan alerts (still a leak event).
5. Post-incident fix (not connectivity)
- Revoke PAT → fine-grained read-only, single repo, 7–30 day write token in separate profile.
- CodeGraph read-only mount; index rebuild only via Runner nightly job.
- Split API into
api-ro/api-rw; Claude defaults toapi-roonly. - Runner secret scan + forbid MCP and CI sharing App private key.
Series further reading (layered · on blog roadmap)
This page = Page 1 · authoritative spec (architecture + policy + review). Lighter pages below handle how-to and troubleshooting—cross-linked, so one page does not try to be both encyclopedia and cheat sheet:
| Layer | Page | qid | Plan | Search intent |
|---|---|---|---|---|
| Page 1 | MCP permissions spec (this article) | L4-Q04 | ✅ | Permission model · best practices · security spec |
| Page 2 | How to configure Claude Code MCP permissions (how-to) | L4-Q08 | 6/11 | 5-minute steps · copy-paste · verify |
| Page 3 | MCP troubleshooting | L4-Q07 | 6/12 | Common errors · permission/connectivity symptoms |
| Extended | MCP threat model | L4-Q05 | 6/10 | STRIDE · credential flow · attack surface |
| Extended | Best-practices checklist (single page) | L4-Q06 | 6/13 | Printable checklist · team onboarding |
Planned slugs (zh): mcp-quanxian-zenme-peizhi (how-to) · mcp-changjian-guzhang-paichang (troubleshooting) · mcp-weixie-moxing-stride-gongji-mian (threat model).
Evolution: policy as code
This policy matrix is already an auditable table; the next series phase can materialize row rules as executable artifacts—for example JSON policy (mcpServers allow-list), MCP permission DSL, CI gates (ban production URLs / full-write PAT in repo). After the threat model (③) is final, open a separate enforcement runbook—do not mix with this permissions spec.
FAQ
Can CodeGraph be read-only?
Yes—and recommended. Update index via CI or manual process; conversation layer queries only.
Does GitHub MCP need write access?
Issue comments / labels: yes; merge and push: short-lived token + human confirmation, not 24/7 in Claude config.
Can I share secrets with Runner?
No. MCP serves interactive Agents; Runner serves CI—different exposure and rotation cadence.
MCP not working after install?
Connectivity symptoms: overview problem map; planned MCP troubleshooting (L4-Q07) will collect common errors and permission misconfigs.
Document metadata · series
Version: AI Agent permissions security spec v1.0 (stable). Series: Cloud Mac AI Stack · L4 security spec — MCP permissions model and threat control.
| Part | Document | Core output | Status |
|---|---|---|---|
| ① | MCP architecture | Intent / context / truth | Overview ✅ |
| ② | MCP permissions model | Policy · data flow · control plane | This article ✅ |
| ③ | MCP threat model | STRIDE · credential flow | L4-Q05 · 6/10 |
| — | How-to (how to configure permissions) | Lightweight steps page | L4-Q08 · 6/11 |
| — | Troubleshooting | Error index | L4-Q07 · 6/12 |
| — | Checklist | Single-page checklist | L4-Q06 · 6/13 |
Cloud Mac AI Stack · L4 security spec
MCP permissions model and threat control
① Architecture · ② Permissions model (this article) · ③ Threat model (planned) · CodeGraph in five minutes
Back to blog · full series