Claude Code MCP Integration: Connecting External Tools
Why Claude Code Feels Limited Until You Wire It Up
Open a fresh Claude Code session and it can read your files, run your tests, and write surprisingly good code. But ask it to check the latest deploy status in your CI dashboard, pull a row from Postgres, or file a ticket in Linear, and it hits a wall. Not because the model can't reason about the task, but because it has no hands reaching outside your local filesystem and shell. That gap is exactly what the Model Context Protocol was built to close.
MCP is the piece that turns Claude Code from "a very good autocomplete for your terminal" into something closer to a teammate who can actually touch the systems your team depends on. Once you connect an MCP server, Claude Code can query your database schema before writing a migration, check open GitHub issues before starting a feature, or push a message to Slack when a long-running task finishes. None of this requires you to write custom prompt hacks or paste JSON blobs into context by hand. You register a server once, and the tools it exposes show up as first-class capabilities Claude can call.
This article walks through what MCP actually is, how to add servers to Claude Code with real claude mcp add commands, how configuration is structured under the hood, and the practical patterns that separate a useful integration from a flaky one. By the end you should be able to wire up your first external tool and understand enough of the mechanics to debug it when it inevitably misbehaves.
What MCP Actually Is, In Plain Terms
The Model Context Protocol is an open specification for how an AI application talks to external tools and data sources. Think of it as a standardized adapter. Before MCP, every AI tool that wanted to talk to, say, GitHub, had to write its own bespoke integration: its own auth flow, its own function definitions, its own error handling. If you had ten AI tools and wanted each to talk to ten services, you were looking at close to a hundred one-off integrations.
MCP flips that. A single MCP server for GitHub can be written once and then plugged into Claude Code, Claude Desktop, or any other MCP-compatible client. The server exposes a menu of capabilities, typically split into three kinds:
- Tools — functions the model can call, like
create_issueorrun_query, usually with side effects - Resources — read-only data the model can pull into context, like a file, a database schema, or a document
- Prompts — reusable prompt templates the server can hand to the client
Claude Code acts as an MCP client. When you register a server, Claude Code starts it (or connects to it over HTTP), asks it what tools it has, and then makes those tools available to the model during a session, exactly like its built-in tools (Read, Bash, Edit, and so on). The model doesn't know or care that a tool came from an MCP server versus being built into Claude Code itself — from its perspective, it's just another function it can invoke when it decides the task calls for it.
The practical upshot: connecting Claude Code to Postgres, Slack, Sentry, or your internal deploy tool is a configuration problem, not a coding problem, assuming a server already exists (and for most popular tools, one does).
The Three Ways to Connect a Server
MCP servers can run in three transport modes, and Claude Code supports all of them through the claude mcp add command.
Stdio servers run as a local subprocess. Claude Code spawns the process, and communication happens over standard input/output. This is the most common pattern for local tools — filesystem access, local databases, CLI wrappers.
claude mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /Users/pramod/projectsHere, everything after the -- is the actual command Claude Code will run to start the server. The filesystem argument before it is just the name you're registering the server under, which is what shows up later in /mcp inside a session.
SSE (Server-Sent Events) and HTTP servers run remotely and Claude Code connects to them over the network. This is how you'd connect to a hosted MCP endpoint, like an internal company tool sitting behind your VPN, or a third-party SaaS product that exposes an MCP endpoint directly.
claude mcp add --transport http linear https://mcp.linear.app/mcpor for SSE:
claude mcp add --transport sse notion-mcp https://mcp.notion.com/sseBoth stdio and HTTP-based servers end up doing the same job from Claude's perspective — the transport is just how the bytes get from Claude Code to the server process. You pick stdio when the tool lives on your machine and needs local filesystem or shell access; you pick HTTP or SSE when the tool is a hosted service you're authenticating against remotely.
Setting Up Your First Server, Step by Step
Let's connect something genuinely useful: a Postgres server that lets Claude Code inspect your schema and run read queries directly, instead of you copy-pasting \d table_name output into the chat every time you ask it to write a migration.
First, check what's already registered:
claude mcp listOn a fresh install this comes back empty. Now add the Postgres server:
claude mcp add postgres -- npx -y @modelcontextprotocol/server-postgres "postgresql://readonly_user:pass@localhost:5432/teachyou_dev"A few things worth noting about this command. The connection string is passed as an argument to the server process, not stored anywhere Claude Code manages separately by default — which means if you're on a shared machine or committing configs to a repo, you do not want your real production credentials sitting in that string (more on secrets handling further down). Second, -y tells npx to skip its "ok to install this package" confirmation prompt, which matters because Claude Code needs the command to run non-interactively.
After adding it, verify the connection:
claude mcp get postgresThis should show the server as connected along with the tools it exposes — typically something like query and a resource for listing table schemas. Now inside a Claude Code session, you can ask something like "what columns does the enrollments table have, and are there any indexes on user_id" and Claude will call the tool itself rather than asking you to run a query and paste the output back.
To remove a server you no longer need:
claude mcp remove postgresConfiguration Scopes: Local, Project, and User
One detail that trips people up is that Claude Code has three different scopes for MCP server configuration, and picking the wrong one is the single most common cause of "it worked yesterday but not today."
- Local scope (the default) stores the server config in your user-level settings, tied to the specific project directory you were in when you ran
claude mcp add. It's private to you and doesn't get shared with teammates. - Project scope writes the config into a
.mcp.jsonfile at the root of the project, which you can commit to version control so the whole team gets the same servers automatically when they check out the repo. - User scope makes the server available across every project you open Claude Code in, regardless of directory — useful for something like a personal Notion or calendar integration that isn't tied to any one codebase.
You choose the scope with a flag:
claude mcp add --scope project shared-linear -- npx -y @modelcontextprotocol/server-linear
claude mcp add --scope user personal-notion --transport http https://mcp.notion.com/mcpIf you're setting up a server the whole team should have, project scope is almost always the right call, because it means a new engineer clones the repo, runs Claude Code, and the same tools are already there without them having to know your team's internal integration list from memory. A .mcp.json file committed to a repo might look like this:
{
"mcpServers": {
"shared-linear": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-linear"],
"env": {
"LINEAR_API_KEY": "${LINEAR_API_KEY}"
}
},
"postgres-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${DATABASE_URL}"]
}
}
}Notice the ${VARIABLE} syntax. That's the important part for the next section.
Handling Secrets Without Leaking Them Into Git
The most common mistake with MCP setups is baking API keys and connection strings directly into a config file that then gets committed. If you write your Slack bot token straight into .mcp.json and push it, it's in your git history forever, full stop, even if you delete it in a later commit.
Claude Code supports environment variable expansion inside MCP config files specifically to avoid this. Instead of hardcoding a secret, reference an environment variable and let each developer (or CI environment) supply the actual value locally:
{
"mcpServers": {
"sentry": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sentry"],
"env": {
"SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}",
"SENTRY_ORG": "teachyou-ai"
}
}
}
}Each teammate then keeps SENTRY_AUTH_TOKEN in their own shell profile or a local .env file that is explicitly gitignored. When Claude Code starts the server, it substitutes the real value at runtime, and the value itself never touches the committed config.
For servers added via the CLI directly rather than editing the JSON by hand, you can pass environment variables with --env:
claude mcp add sentry --env SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN -- npx -y @modelcontextprotocol/server-sentryA second guardrail worth adopting: prefer read-only credentials wherever the underlying service supports scoped access tokens. If you're connecting Claude Code to a production database, do not point it at a role with DELETE or DROP privileges just because it was convenient. A model that's occasionally overzealous about "fixing" data it thinks looks wrong is a real failure mode, and the cheapest mitigation is simply not giving the tool destructive permissions in the first place.
Inspecting and Debugging a Connected Server
Once a server is added, you'll want to confirm what it can actually do before trusting it mid-task. Inside an active Claude Code session, the /mcp slash command opens a view of every connected server, its connection status, and the tools, resources, and prompts it's exposing.
/mcpThis is the fastest way to answer "why isn't Claude using the tool I just set up" — nine times out of ten, the server shows as disconnected or failed, and the reason is visible right there (missing environment variable, wrong path, the subprocess crashing on startup).
For a deeper look outside of a live session, the CLI gives you per-server detail:
claude mcp get sentryThis prints the resolved command, arguments, environment variables it's expecting, and current connection state. If a stdio server fails to start, it's almost always one of three things: the binary or package name is wrong, a required environment variable wasn't exported in the shell Claude Code is running from, or the command depends on a working directory that doesn't match where Claude Code launched it. Running the exact same command manually in your terminal, outside of Claude Code, is the single fastest way to isolate which of those three it is.
If you're debugging a server you're actively developing yourself, the official MCP Inspector tool is worth knowing about — it lets you connect to a server standalone and manually invoke its tools to confirm the server itself works before you ever plug it into Claude Code:
npx @modelcontextprotocol/inspector node ./my-server/index.jsIsolating "is this a problem with my server" from "is this a problem with how Claude Code is calling my server" saves a lot of circular debugging.
Practical Patterns Worth Adopting
A few habits make MCP setups noticeably more reliable in day-to-day use, beyond just getting the initial connection working.
- Name servers descriptively rather than generically —
postgres-readonly-stagingtells you and Claude more at a glance thandb, especially once you have four or five servers registered across a project - Keep destructive-capability servers (anything that can write, delete, or deploy) on user or local scope rather than project scope, so a teammate cloning the repo doesn't unknowingly inherit write access to a shared resource they didn't intend to grant
- Use
claude mcp listat the start of a new project to sanity-check exactly which servers are active before you start a task that depends on one of them - When a server exposes both a broad tool (
run_arbitrary_query) and narrower ones (get_table_schema,list_tables), prefer configuring or wrapping it to expose the narrower set for routine use — it reduces the chance of an unintended broad action and it also means Claude has to reason less about which tool is appropriate, which in practice means fewer mistakes - Periodically run
claude mcp get <name>on your core servers after upgrading either Claude Code or the server package itself, since transport or schema changes between versions are a real source of silent breakage
One pattern that's easy to miss: MCP servers can be combined with project-level CLAUDE.md instructions to tell Claude *when* to reach for a given tool. If you have a Linear server connected but never mention it anywhere, Claude will still use it when relevant, but being explicit — "when starting work on a bug, check Linear for the corresponding ticket first" — makes the behavior consistent rather than opportunistic.
It's also worth thinking about tool count as a cost, not just a convenience. Every tool a connected server exposes gets described to the model at the start of a session, which consumes context and gives the model more surface area to choose from when deciding what to call. A server that dumps forty granular endpoints into your session (one server, sometimes wraps an entire REST API one-to-one) can actually make Claude slower and less accurate at picking the right tool than a server that exposes six well-designed, purpose-built ones. If you're building an internal MCP server rather than adopting a third-party one, resist the urge to mirror your whole API surface. Design the tool list the way you'd design a good CLI: a small number of verbs that map to real workflows, not a raw passthrough of every database table or endpoint you happen to have.
Common Failure Modes and How to Read Them
A handful of failure patterns show up repeatedly enough that it's worth having them memorized rather than re-diagnosing from scratch each time.
- Server shows "failed" in `/mcp` immediately on session start — almost always a stdio process that crashed on launch. Run the exact command from your config manually in a terminal; the stack trace it prints is usually far more informative than anything Claude Code surfaces
- Server connects but the tool call times out — common with remote HTTP/SSE servers behind a slow network path or an internal VPN that Claude Code's process doesn't have routing to; test connectivity to the URL directly with
curlbefore assuming the MCP layer is at fault - Tool is listed in `/mcp` but Claude never calls it — usually a description problem on the server side rather than a Claude Code bug; if a tool's description is vague ("query the database") the model may not recognize it as relevant to a specific request, whereas a description like "run a read-only SQL query against the app database and return rows" gets invoked far more reliably
- Works for you, fails for a teammate — nearly always a missing environment variable on their machine when the config uses
${VAR}substitution; a quickecho $VARNAMEon their end usually confirms it in seconds - Server worked last week, silently stopped now — check whether the underlying npm package was updated to a new major version with a breaking config change, which happens more often than you'd like with fast-moving MCP server packages; pinning a version in your
args(e.g.@modelcontextprotocol/server-postgres@0.4.0instead of the unpinned latest) avoids this entirely for shared project configs
Treating these as a checklist rather than starting from zero each time turns MCP debugging from a half-hour detour into a two-minute fix.
A Realistic End-to-End Example
Put together, here's what a small but genuinely useful MCP setup looks like for a team shipping a web product. Three servers, three scopes, one committed config:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${STAGING_DATABASE_URL}"]
},
"sentry": {
"transport": "http",
"url": "https://mcp.sentry.dev/mcp",
"env": {
"SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}"
}
}
}
}With this committed as .mcp.json and each developer supplying their own GITHUB_TOKEN, STAGING_DATABASE_URL, and SENTRY_AUTH_TOKEN locally, a session might now look like: "check Sentry for the top unresolved error in the last 24 hours, find the related code path, check if there's an open GitHub issue for it, and if not draft one with the stack trace and a suggested fix." Every step in that sentence maps to an actual tool call Claude Code can now make on its own, instead of you manually shuttling data between four browser tabs and pasting it into a chat window.
That's the real value of MCP integration — not that it makes any single task dramatically smarter, but that it collapses the friction between "the AI reasons well" and "the AI can act on what it just reasoned," which is usually where the time actually goes.
Where to Go Next
MCP is still a young protocol, and the ecosystem of available servers is growing fast, so it's worth periodically checking the official server registry rather than assuming your team has already found everything useful. Start small: pick one tool your team checks manually multiple times a day, find or build an MCP server for it, and connect it at project scope so everyone benefits at once.
If you want a structured, hands-on walkthrough that goes beyond a single blog post — covering installation, custom tool building, debugging real connection failures, and safe secrets management in a team setting — check out the Claude Code Tutorial for Beginners course on teachyou.ai, which builds up exactly this kind of workflow from a blank terminal to a fully wired-up, multi-server development setup.
AI CodingShip full-stack AI apps at conversation speed — specs, agents, deploys, all from the terminal.
Claude CodeGo from zero to confident with Claude Code, the terminal agent that reads, edits, runs, and verifies real code.
Related reading