How to Control MCP Servers on a Per-Project Basis in Claude Code
You control MCP servers per project through two keys in your project's .claude.json file: mcpServers to override or extend definitions, and disabledMcpServers to blacklist specific servers from the global catalog.
The everything-claude-code repository implements a two-tier configuration system that separates the global catalog of available Model Context Protocol (MCP) servers from project-specific selections. This design lets teams maintain a centralized library of tools while keeping each project's active MCP set lean and purposeful.
Understanding the Configuration Architecture
The repository stores a global catalog of MCP servers in mcp-configs/mcp-servers.json. This file contains the master list with launch commands, arguments, environment variables, and descriptions for every available server. However, projects do not automatically inherit all servers from this catalog. Instead, each project must explicitly opt in to the tools it needs.
This separation prevents context window overflow—the README explicitly warns against enabling more than 10 MCP servers per project to preserve the model's effective context.
Where Project Configuration Lives
Claude reads project-level MCP configuration from either:
~/.claude.json— global user defaults.claude.jsonin the project root — project-specific overrides (takes precedence)
The local .claude.json is the recommended location for per-project control.
The Two Control Mechanisms
mcpServers: Override or Extend the Global Catalog
The mcpServers key allows complete or partial redefinition of server definitions. When present, it merges with the global catalog—project entries take precedence, and new entries extend the set.
Complete replacement example — define only the servers this project needs:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_GITHUB_PAT_HERE"
},
"description": "GitHub PR & repo ops"
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"description": "Persistent session memory"
}
}
}
Partial override example — add a custom server while inheriting the rest:
{
"mcpServers": {
"my-custom-mcp": {
"command": "node",
"args": ["custom-mcp.js"],
"description": "Internal analytics MCP"
}
}
}
disabledMcpServers: Blacklist Unwanted Servers
The disabledMcpServers key accepts an array of server names to exclude from the active set. This is the safest way to trim the global catalog without redefining every entry you want to keep.
Aggressive filtering example:
{
"disabledMcpServers": [
"firecrawl",
"supabase",
"vercel",
"railway",
"cloudflare-docs",
"cloudflare-workers-builds",
"cloudflare-workers-bindings",
"cloudflare-observability",
"clickhouse",
"context7",
"magic",
"filesystem"
]
}
Combine both keys for precise surgical control:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_GITHUB_PAT_HERE"
},
"description": "GitHub PR & repo ops"
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"],
"description": "Persistent session memory"
}
},
"disabledMcpServers": [
"firecrawl",
"supabase",
"vercel",
"railway",
"cloudflare-docs",
"cloudflare-workers-builds",
"cloudflare-workers-bindings",
"cloudflare-observability",
"clickhouse",
"context7",
"magic",
"filesystem"
]
}
How the Merge Logic Works
When Claude initializes a session, it applies this three-step resolution:
- Load global catalog from
mcp-configs/mcp-servers.json(lines 3-88) - Merge project overrides — spread
projectConfig.mcpServersoverglobalCatalog.mcpServers - Filter blacklist — remove any server whose name appears in
disabledMcpServers
The resulting set is what Claude instantiates via npx, HTTP endpoints, or custom commands.
Merge pseudocode:
const globalCatalog = loadJSON('mcp-configs/mcp-servers.json').mcpServers;
const projectConfig = loadJSON('.claude.json');
const merged = {
...globalCatalog,
...(projectConfig.mcpServers || {})
};
const active = Object.keys(merged).filter(
name => !(projectConfig.disabledMcpServers || []).includes(name)
);
Configuring MCP Servers in Practice
Step 1: Inspect the Global Catalog
Review mcp-configs/mcp-servers.json in the everything-claude-code repository to see available servers and their default configurations. Each entry includes:
command— executable to launch (typicallynpxornode)args— command arguments, often including the package nameenv— optional environment variables for authenticationdescription— human-readable purpose statement
Step 2: Create Your Project's .claude.json
Place this file in your project root. Start minimal:
{
"disabledMcpServers": []
}
Then iteratively refine based on which tools your codebase actually needs.
Step 3: Verify Active Servers
After starting Claude Code, observe which MCP servers initialize. If you exceed 10 active servers, the README warns that you risk degrading the model's context efficiency (lines 358-360).
Step 4: Handle Hook Dependencies
The hooks/hooks.json file defines automation that may depend on specific MCP servers. Disabling a server referenced by active hooks will disable those hooks automatically—verify your hooks still function after trimming the MCP list.
Key Files for MCP Configuration
| File | Purpose |
|---|---|
mcp-configs/mcp-servers.json |
Master catalog of all available MCP servers with launch configurations |
README.md |
Context window guidance and disabledMcpServers documentation (lines 358-360) |
.claude.json (project-level) |
Your per-project overrides and blacklist (you create this) |
hooks/hooks.json |
Hook definitions with potential MCP dependencies |
Summary
- Global catalog lives in
mcp-configs/mcp-servers.json— maintain this centrally, enable nothing by default. mcpServersin.claude.jsonoverrides or extends catalog entries for your project.disabledMcpServersin.claude.jsonblacklists specific servers by name.- Merge order: global catalog → project
mcpServersoverlay →disabledMcpServersfilter. - Stay under 10 active MCPs per project to protect context window efficiency.
Frequently Asked Questions
Can I disable all global MCP servers and define only project-specific ones?
Yes. Set "disabledMcpServers" to a list of all global server names, or provide a complete replacement in "mcpServers" that omits any you do not need. The override completely replaces global definitions for matching keys.
Does the .claude.json file support environment variable substitution?
No. The configuration expects literal values in env fields. For secrets, reference environment variables in your shell profile and ensure they are available when Claude launches, or use project-specific .env files loaded by your MCP wrapper.
What happens if I disable an MCP server that a hook requires?
Hooks defined in hooks/hooks.json that depend on the disabled server will fail gracefully or skip execution. Review your active hooks after modifying disabledMcpServers to ensure automation continues working.
Can different team members use different MCP selections for the same project?
Yes. Each developer can maintain personal overrides in ~/.claude.json for global defaults, or use untracked local .claude.json files (add to .gitignore) for project-specific preferences. The repository's global catalog remains the shared baseline.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →