Overview
Claude Code connects to MCP servers declared in settings.json. Once connected, the server’s tools appear alongside built-in tools and Claude can call them in any turn. The pattern rule is: prefer MCP tools over Bash when the integration will be used across sessions. A one-off gh invocation is fine; recurring GitHub operations belong in the GitHub MCP server. See mcp-servers for designing servers; this page covers the Claude Code client configuration.
Declare MCP servers in settings.json
Each server gets an entry under mcpServers. The key is the server’s logical name; the value describes how to launch or connect to it.
{
"mcpServers": {
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"]
}
}
}Place project-specific servers in .claude/settings.json. Place personal or cross-project servers in ~/.claude/settings.json. Both files are read; project settings merge on top.
Pass secrets through environment variables, never inline
The env block in the server config accepts ${VAR} substitutions resolved from the shell environment. Never write a token or connection string as a literal value in settings.json.
Reasons: settings.json is often committed to version control. Literals leak in diffs, logs, and PR reviews. Environment variables stay in .env (gitignored) or in the CI secrets store.
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"DATABASE_URL": "${DATABASE_URL}"
}Claude Code reads the current shell environment when launching servers. Set the variables in your shell profile or a gitignored .env.local file sourced at session start.
Restrict tool exposure with allowedTools
By default, Claude Code exposes every tool a server advertises. Use allowedTools to restrict access to the tools the project actually needs.
{
"mcpServers": {
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" },
"allowedTools": [
"mcp__github__list_issues",
"mcp__github__create_pull_request",
"mcp__github__pull_request_read",
"mcp__github__merge_pull_request"
]
}
}
}Restricting to read-only tools for a project that should not create issues prevents accidents during exploratory sessions. Allowlists also document the project’s intended integration surface.
Scope servers to specific repositories
Use project-level .claude/settings.json rather than user-level ~/.claude/settings.json when the server is specific to one repo. A Postgres server pointed at the dev database for project A should not appear in project B’s sessions.
For shared servers (GitHub, Notion), declare them at the user level but restrict allowedTools per project. The user-level declaration provides the auth; the project-level restriction limits the blast radius.
Prefer MCP tools over Bash for durable integrations
The tradeoff is:
Bashwithgh pr create: works, no setup, breaks on quoting edge cases, not logged by MCP, not consistent across sessions.mcp__github__create_pull_request: typed inputs, no quoting issues, MCP-level logging, consistent schema across sessions.
Use Bash for one-off commands the agent will not repeat. Use MCP tools for operations that recur: reading PRs, posting comments, querying a database, writing to a project management tool.
The practical test: if you have written the same Bash command in three different briefs, write an MCP server for it or enable the official one.
Confirm server connectivity before relying on it in a brief
MCP servers can fail to start (missing binary, bad token, network error). A brief that assumes GitHub MCP is live will silently fall back to Bash or fail mid-session.
Add a preflight step to briefs that depend on MCP:
Phase 0: Verify MCP connectivity.
Run: list available tools and confirm mcp__github__list_issues is present.
If not present, stop and report the error.
This catches misconfiguration before the expensive phases run.