MCP Project Configuration
Guide for configuring MCP (Model Context Protocol) servers in projects.
Standard MCP Servers
Provided by installed plugins — do NOT add per-project .mcp.json entries for these:
| Server | Source | Notes |
|--------|--------|-------|
| GitHub | github plugin | Prefer gh CLI for most operations anyway |
| Slack | slack plugin | |
| Sentry | sentry plugin | |
| context7 | context7 plugin | Library docs lookup |
| betterwright | user-scoped MCP (betterwright mcp) | The browser driver on this box — do NOT add chrome-devtools or playwright per project. See the betterwright / browser skills. |
They are available in every session already; a project entry only duplicates them. Only add one to .mcp.json if a project genuinely needs a different auth/host than the plugin provides.
Configure per-project when needed:
| Server | Type | Purpose | |--------|------|---------| | Linear | HTTP | Project/issue tracking (workspace-specific token — see below) | | Project-specific servers | varies | Anything unique to the project (Postgres, custom stdio servers) |
Render is no longer a default — add it only if the project actually deploys on Render.
Configuration Files
.mcp.json (Project Root)
{
"mcpServers": {
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"headers": {
"Authorization": "Bearer ${LINEAR_TOKEN_VAR}"
}
}
}
}
.claude/settings.json
{
"enableAllProjectMcpServers": true,
"enabledMcpjsonServers": ["linear"]
}
Linear Account Selection
When setting up Linear, ask which account to use:
| Account | Token Variable |
|---------|---------------|
| Laser Focused | LASER_FOCUSED_LINEAR_TOKEN |
| What If | WHAT_IF_LINEAR_TOKEN |
If neither applies (new workspace):
- Ask the user for the token environment variable name
- User creates the token at https://linear.app/settings/api
- User adds it to their shell profile (~/.zshrc or ~/.bashrc)
Replace LINEAR_TOKEN_VAR in .mcp.json with the chosen variable.
Token Environment Setup
Tokens live in the user's shell profile (~/.zshrc or ~/.bashrc), NOT in project files:
# Linear API Tokens
export LASER_FOCUSED_LINEAR_TOKEN="lin_api_..."
export WHAT_IF_LINEAR_TOKEN="lin_api_..."
Reload after adding: source ~/.zshrc
MCP Server Types
HTTP (hosted API with auth):
{ "type": "http", "url": "https://api.service.com/mcp", "headers": { "Authorization": "Bearer ${TOKEN_VAR}" } }
stdio via npx (local tool):
{ "command": "npx", "args": ["-y", "package-name@latest"] }
stdio via local binary:
{ "command": "/path/to/binary", "args": ["--flag", "value"] }
Setup Workflow
- Check what the project actually needs — skip anything already covered globally (GitHub, Slack, Sentry, context7 plugins; betterwright for anything browser).
- Create
.mcp.jsonin the project root with only the needed servers. - If Linear: ask which workspace → pick the token variable.
- Create
.claude/settings.jsonwithenabledMcpjsonServerslisting each server. - Verify: start a new Claude Code session and confirm the MCP tools appear.
For copy-paste templates (Linear, Postgres, Render-if-needed), see mcp-templates.md.
Troubleshooting
- Server not available: check the token is exported — presence only, never print the value:
[ -n "$TOKEN_NAME" ] && echo set(secrets-intake iron rule) — then check.mcp.jsonis valid JSON, the server is listed in settings.json, and restart the session. - Auth failed: token expired, missing permissions, or env var not set in the shell that launched Claude Code.
- Duplicate tools: a project
.mcp.jsonentry shadows a plugin-provided server — remove the project entry.
Observability (required)
The observability MCPs, as a standard block. Read observability-triage for how they are used.
Proven working 2026-09-04 (every server handshaked + a live tool call) — copy verbatim; the only
per-project edits are the Grafana URL/port and the Sentry --organization-slug. Canonical copy:
/home/justin/code/coworking-mng-not-shit/.mcp.json.
Every server is stdio, and every one sources its own 0600 file under ~/.config inside
bash -lc. That is deliberate: no secret reaches the repo, and no export has to exist in the
shell that launched Claude Code (a ${VAR} in .mcp.json silently resolves to empty otherwise).
// .mcp.json — no secrets; each entry sources ~/.config/*.env itself
{
"mcpServers": {
"sentry": { "command": "bash", "args": ["-lc",
"set -a; . ~/.config/sentry.env; set +a; export SENTRY_ACCESS_TOKEN=\"$SENTRY_AUTH_TOKEN\"; exec npx -y @sentry/mcp-server@latest --host=de.sentry.io --organization-slug=laserfocused --disable-skills=project-management"] },
"posthog": { "command": "bash", "args": ["-lc",
"set -a; . ~/.config/posthog.env; set +a; exec npx -y mcp-remote@latest 'https://mcp.posthog.com/mcp?mode=cli' --transport http-only --header \"Authorization:Bearer ${POSTHOG_PERSONAL_API_KEY}\""] },
"grafana-platform": { "command": "bash", "args": ["-lc",
"set -a; . ~/.config/observability/grafana-mcp.env; set +a; exec ~/.local/bin/mcp-grafana -t stdio --disable-write --enabled-tools loki,dashboard,alerting,datasource --max-loki-log-limit 50 --loki-guardrail-mode enforce"] }
}
}
.claude/settings.json needs "enableAllProjectMcpServers": false plus an explicit
"enabledMcpjsonServers": [...] — opt in by name, never blanket-enable.
Facts that cost a lane to find, so don't re-derive them:
https://mcp.sentry.dev/mcpdoes NOT accept oursntryu_auth token — it is OAuth-only (401 invalid_token,WWW-Authenticate: Bearer realm="OAuth"), which no headless session can complete. The stdio@sentry/mcp-serverwithSENTRY_ACCESS_TOKENis the working path.~/.config/sentry.envnames the varSENTRY_AUTH_TOKEN, hence the re-export.- The org is EU →
--host=de.sentry.io.us.sentry.io404s. --organization-slugreally scopes it: with the flag the server exposes 8 tools, without it 9 (find_organizationsdisappears).--disable-skills=project-managementdrops project/team/DSN creation;inspect,seer,docs,triageremain.- PostHog
?mode=clicollapses the whole server to ONE tool,exec— a natural-language/ resource-verb dispatcher. That is the context-cheap mode and the one to use.mcp-remoteis only a stdio↔HTTP bridge;--transport http-onlyskips its SSE probe. uvx mcp-grafanadoes not exist — it is a Go binary. Install the release tarball:curl -sSL .../grafana/mcp-grafana/releases/download/v1.3.0/mcp-grafana_Linux_x86_64.tar.gz | tar xz -C ~/.local/bin mcp-grafana. The tool group isdatasource, singular.
Guardrails, not optional:
- Grafana:
--disable-writeAND a Viewer service account. Two layers — the flag hides write tools, the token makes a write 403 at the API even if a tool slips through (verified:POST /api/folders→ 403,GET /api/datasources→ 200). Mint it withPOST /api/serviceaccounts {"name":"mcp-readonly","role":"Viewer"}as admin, store it in~/.config/observability/grafana-mcp.env— never reusegrafana.env's Editor push token, which belongs topush.sh.--max-loki-log-limit 50 --loki-guardrail-mode enforceis the context defence: it caps a log pull at 50 lines and rejects an unbounded query outright. - PostHog: a personal API key. Never let an agent create or rename events — event names are irreversible. Prefer a project-scoped key when the API grows one.
- Sentry: read-mostly.
update_issueexists and resolve-in-next-release is inside the autonomy boundary; anything that deploys or changes sampling is not. - Keys live in
~/.config/sentry.env,~/.config/posthog.env,~/.config/observability/grafana-mcp.envand are referenced by NAME (secrets-intake) — never echoed, never committed. - Every one of these runs in a subagent lane and returns a compact brief (
context-hygiene).