How to Add MCP Servers to Claude Desktop: Complete Configuration Guide
Model Context Protocol (MCP) is only as useful as the servers you connect to it. Claude Desktop ships with zero MCP servers configured by default — every tool you want available has to be wired up by editing one JSON config file. This guide walks through every step, every option, and every common gotcha so you can do it once and never wrestle with it again.
Where to find the config file #
Claude Desktop reads MCP server definitions from a single JSON file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
If the file does not exist yet, create it. Claude Desktop will pick it up on the next launch.
Fastest way to open it from inside Claude Desktop: Settings → Developer → Edit Config. That button opens the file in your default editor and confirms the path Claude actually reads.
The minimum viable config #
Here is the smallest possible config — adding the official filesystem MCP server:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/you/Documents"
]
}
}
}
Save the file. Fully quit Claude Desktop — Cmd+Q on macOS, right-click the tray icon → Exit on Windows. Closing the window is not enough. Relaunch.
When Claude Desktop next starts, it spawns npx -y @modelcontextprotocol/server-filesystem /Users/you/Documents as a child process, talks to it over stdio, and exposes its tools to the LLM. In a new chat you can now ask things like "What files are in my Documents folder?" and the model has access.
Anatomy of a server entry #
Every entry under mcpServers follows the same shape:
{
"mcpServers": {
"<friendly-name>": {
"command": "<executable>",
"args": ["<arg1>", "<arg2>"],
"env": { "KEY": "value" }
}
}
}
What each field does:
| Field | Required | Purpose |
|---|---|---|
command |
yes | The executable to launch. npx, node, python, a full path, or any binary on PATH. |
args |
yes | Arguments passed to that command. Each item is a separate argv element. |
env |
no | Environment variables given to the subprocess. Used for API keys and secrets. |
cwd |
no | Working directory for the subprocess. Defaults to Claude Desktop's working dir. |
The <friendly-name> key is purely cosmetic — it shows up in Claude Desktop's MCP server status panel and in error messages. Pick something short.
Adding multiple servers #
Servers stack — just add more entries under mcpServers:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Projects"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_yourTokenHere" }
},
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgres://readonly_user:password@localhost:5432/mydb"
]
}
}
}
Each server runs as its own subprocess. They are isolated — a crash in one does not affect the others.
Passing secrets via env #
Never put API keys directly in args. They show up in process listings (ps aux on macOS, Task Manager on Windows) and leak into log files. Use env instead:
{
"mcpServers": {
"linear": {
"command": "npx",
"args": ["-y", "@linear/mcp-server"],
"env": {
"LINEAR_API_KEY": "lin_api_xyz..."
}
}
}
}
The subprocess receives LINEAR_API_KEY as an environment variable, reads it from process.env.LINEAR_API_KEY, and uses it for authentication.
The config file itself still contains the secret in plain text, so treat it like any other credentials file — keep it out of version control and consider OS-level file permissions (chmod 600 on macOS/Linux).
Using a local server (no npm package) #
If you have written your own MCP server and want to use it without publishing to npm, point command and args at the file directly:
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["/absolute/path/to/server.js"],
"env": { "DEBUG": "1" }
}
}
}
Use absolute paths. Relative paths break because Claude Desktop launches the subprocess from its own working directory, not the folder containing the config file.
For TypeScript projects in development, run via tsx:
{
"mcpServers": {
"my-custom-server": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/server.ts"]
}
}
}
This avoids needing a build step during development.
Verifying the server loaded correctly #
After restarting Claude Desktop, three things tell you the server is live:
- The MCP icon — there is a small plug/socket icon near the chat input. Click it. You should see your server's name and its tools listed.
- Tool availability — start a chat and ask a question that would require the server. The model uses it autonomously if the tools match.
- Settings → Developer → Open Logs — opens a directory with one log file per server. Each log shows the server's stderr output. If the server crashed at startup, the error is here.
If the MCP icon is absent or the server is greyed out, check the log file first. Nine times out of ten it tells you exactly what is wrong.
Common issues and fixes #
| Problem | Likely cause | Fix |
|---|---|---|
| Server not appearing in the MCP panel | Config file has invalid JSON | Paste your config into a JSON validator |
| Server starts but immediately exits | Missing dependency or wrong Node version | Check the log file in Settings → Developer → Open Logs |
| Server worked yesterday, broken today | npx cache expired or package updated breaking-ly | Add a version pin: "args": ["-y", "package-name@1.2.3"] |
| Restart did not pick up changes | Only closed the window, not the app | Cmd+Q on macOS, full Exit on Windows |
| API key works in terminal but not in Claude | Wrong env var name in config | Match exact case — GITHUB_TOKEN vs GITHUB_PERSONAL_ACCESS_TOKEN |
command not found errors |
npx or node not on PATH from Claude's launch context | Use absolute paths like /usr/local/bin/node |
The command not found issue is the most confusing one. Claude Desktop on macOS sometimes inherits a stripped-down PATH that omits Homebrew or nvm directories. The fix is to use the full path to your Node binary, which which node will show you.
Advanced: a cwd example #
If your server expects to be launched from a specific directory (for example, it loads a .env file relative to its location), set cwd:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["./dist/server.js"],
"cwd": "/Users/you/code/my-mcp-server"
}
}
}
With cwd set, both the executable path and relative paths inside the server resolve from /Users/you/code/my-mcp-server.
A complete production-style config #
Here is a realistic config for a developer who wants filesystem access, GitHub integration, Postgres queries, and a custom internal tool:
{
"mcpServers": {
"projects": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/code"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..." }
},
"db": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgres://ai_readonly:pwd@localhost:5432/staging"
]
},
"internal-api": {
"command": "node",
"args": ["/Users/you/code/internal-mcp/dist/server.js"],
"env": { "INTERNAL_API_TOKEN": "..." }
}
}
}
That is a 4-server setup giving Claude Desktop access to your code, your repo, your database, and your internal services — all isolated, all configurable per project, all secrets kept out of args.
Conclusion #
Every MCP-aware feature in Claude Desktop flows through this one config file. Once you understand the shape — command, args, env, optional cwd — adding new servers becomes a 30-second copy-paste.
The two rules that will save you the most pain:
- Always fully quit and relaunch after editing the config (not just close the window).
- Always use absolute paths for local servers.
Do those two, keep secrets in env, and you will rarely touch this file twice.
Try it yourself #
Once the GitHub MCP server is in your config, here is what asking Claude about a repo looks like:
good first issue, max 5.search_issues5 open good first issue tickets on vercel/next.js:• #69842 Improve error message when middleware file is misnamed
• #69771 Docs: clarify behaviour of revalidatePath in route handlers
• #69703 Add type tests for unstable_cache return value
• #69658 Better warning when font module is imported in a server component
• #69611 Investigate flakiness in image-component e2e tests
Want me to pull the full description of any of these?
That works because the GitHub MCP server was discovered at startup, registered its tools, and Claude routed the natural-language question to search_issues automatically. Pure configuration — no glue code anywhere.
Up next in AI & MCP
More from this topic
Enjoyed this article?
Get new AI & MCP tutorials delivered. No spam — just code-first articles when they ship.


