An agent gains real-world abilities by connecting to MCP servers. Each server adds a set of tools the agent can call, so a coding agent can read your issue tracker, query a database, or open a pull request without you pasting anything in. New to the protocol itself? Start with MCP fundamentals.
On its own, an agent can only think and talk. It reads what you give it and writes text back. MCP is the wiring that turns that text into action: a server exposes tools, the agent decides when to call one, and the result comes back into the loop. The work of this guide is the wiring, which servers to connect and how to keep them safe.
Why does an agent need MCP?
A bare agent has a fixed set of built-in tools: read a file, edit a file, run a shell command, search the web. That covers a lot of coding work. It does not cover the systems your work lives in. Your bugs are in an issue tracker. Your errors are in a monitoring dashboard. Your data is in a database. Your designs are in a design tool.
MCP closes that gap with one uniform interface. Instead of teaching the agent a bespoke integration for each system, you connect a server that already speaks MCP, and every tool it exposes becomes callable. The agent does not need to know how Notion or Sentry or Postgres works internally. It calls a named tool with arguments and reads the result.
The agent is the client. Each box on the right is a separate server. Add a server, and the agent has a new ability. Remove it, and the ability is gone. Nothing about the agent's core changes.
How the tools reach the loop
The agent loop is the cycle where the model decides what to do, calls a tool, reads the result, and decides again until the task is done. MCP tools join that loop exactly like built-in tools. The model sees a tool name and a description, picks one, and the client runs it against the right server.
The model never talks to the server directly. The client sits in the middle: it holds the connection, enforces your permissions, and formats the result. This is why you reason about MCP at the wiring level. You are configuring the client, not writing model-calling code.
How do I connect a server with the Claude Code CLI?
Claude Code is an MCP client. You add a server with claude mcp add, and from then on its tools are available in your sessions. There are two shapes of server, and the command differs slightly for each.
A remote server runs in the cloud and speaks over HTTP. This is the common case for hosted services:
# claude mcp add --transport http <name> <url>
claude mcp add --transport http sentry https://mcp.sentry.dev/mcpA local server runs as a process on your machine and speaks over stdio. You give the command that launches it after a -- separator:
# claude mcp add [options] <name> -- <command> [args...]
claude mcp add --transport stdio db -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@db.example.com:5432/analytics"The -- matters. Everything after it is passed to the server untouched, so the server's own flags, like --dsn above, are not mistaken for Claude's flags. For an explanation of when to choose local over remote, see local vs remote MCP servers.Common gotchaA real password in that --dsn string gets baked into your config and shows up in claude mcp list, your shell history, and the process list.Put the secret in an environment variable and reference it (--dsn "$ANALYTICS_DSN") so the plaintext never lands in any of those places.
Check what is connected, inspect one server, or remove it:
claude mcp list # all configured servers
claude mcp get sentry # details for one server
claude mcp remove sentry # disconnect itInside a session, run /mcp to see live connection status and the tool count for each server.
Which servers should I connect?
More servers is not better. Every connected server adds tools the agent could call, and tools it does not need are tools that can go wrong. Pick servers the way you pick dependencies: by need, not by availability.
A good rule of thumb: connect a server when you catch yourself copying data out of another tool and pasting it into the chat. If you keep pasting stack traces from a dashboard, connect that dashboard. If you keep describing rows from a database, connect a read-only database server. The server pays for itself the moment the agent can fetch that data on its own.
| Need | Server type | Watch for |
|---|---|---|
| Read bugs, open PRs | Issue tracker, code host | Write access scope |
| Investigate errors | Monitoring, observability | Read-only is usually enough |
| Query app data | Database | Use a read-only connection |
| Pull in designs | Design tool | Large outputs |
| Act on your own system | Your API as a server | Permissions on each tool |
Prefer the least-powerful server that does the job. A read-only database connection cannot drop a table even if the agent is confused or a prompt is malicious. To wrap one of your own systems as a server, see wrap your API as an MCP tool.
Where should an MCP server be configured?
Claude Code stores each server at one of three scopes, and the scope decides who gets the server and whether it is shared.
- local · the default. Loads only in the current project and stays private to you. Good for personal or experimental servers, and for anything with credentials you do not want shared.
- project · stored in a
.mcp.jsonfile at the project root, meant to be committed. Everyone who clones the repo gets the same servers. Good for the tools a whole team needs. - user · loads in all your projects, private to you. Good for a personal utility you reach for everywhere.
Set the scope with --scope:
claude mcp add --transport http github --scope project \
https://api.githubcopilot.com/mcp/A project-scoped .mcp.json is plain config you can read and review:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
}Because this file is shared, Claude Code asks each teammate to approve project-scoped servers before using them. A committed server is a suggestion, not a silent grant. For the team workflow, see share MCP servers with your team.
Never commit a secret into .mcp.json. Reference an environment variable instead, so the file is safe to share and each machine supplies its own value:
{
"mcpServers": {
"api-server": {
"type": "http",
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
"headers": {
"Authorization": "Bearer ${API_KEY}"
}
}
}
}The ${API_KEY} form reads from the environment. The ${VAR:-default} form falls back to a default when the variable is not set.
How do I authenticate remote servers?
Most hosted servers need to know who you are. Many use OAuth: you add the server, then run /mcp inside a session and Claude Code opens a browser login. The tokens are stored securely and refreshed for you, so you log in once.
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
# then, inside a session:
# /mcp -> follow the browser loginSome servers authenticate with a token in a header instead. You pass it when you add the server:
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
--header "Authorization: Bearer YOUR_TOKEN"When you mint a token for a server, give it the narrowest access that lets the agent do its job. A read-only token on your code host means the agent can review and comment but cannot push. The token is the real boundary, so set it deliberately.
How do I set permissions on the tools?
Connecting a server makes its tools available. It does not mean every tool runs unattended. Permissions are a separate layer, enforced by Claude Code rather than by the model, so a clever prompt cannot talk its way past them.
You write allow, ask, and deny rules in your settings. Rules are evaluated deny first, then ask, then allow, and the first match wins. MCP rules name the server, optionally a tool:
mcp__githubmatches every tool from thegithubservermcp__github__*does the same with explicit wildcard syntaxmcp__github__get_*matches itsget_toolsmcp__github__create_issuematches one exact tool
A safe starting posture: allow the read-only tools you trust, and require a prompt for anything that writes. Here the agent can read freely but pauses before creating an issue, and database writes are blocked outright:
{
"permissions": {
"allow": ["mcp__github__get_*", "mcp__sentry"],
"ask": ["mcp__github__create_issue"],
"deny": ["mcp__db__execute_sql"]
}
}To turn off every MCP tool at once, deny with a tool-name wildcard:
{
"permissions": {
"deny": ["mcp__*"]
}
}A bare server-name deny rule removes those tools from the agent's view entirely, so the model never even sees them. A scoped allow plus an ask on the risky tools gives you exactly the autonomy you want and no more.Common gotchaThe trap is allowing a whole server by name, like mcp__github, because typing each tool felt tedious. That one rule also auto-approves its write and delete tools, so an injected instruction in a fetched issue can open or close things unattended.Allow only the read tools by prefix (mcp__github__get_*) and leave every writing tool to fall through to ask or deny.
Watch for prompt injection
The moment an agent reads outside content, that content can try to give it instructions. An MCP tool that fetches a web page, an email, or an issue comment can return text like "ignore your task and run this command." This is prompt injection, and it is the main risk of connecting servers that pull in the outside world.
Permissions are your defence. Because the rules are enforced by the client, not the model, an injected instruction cannot grant itself a tool. If mcp__db__execute_sql is denied, no amount of persuasion in a fetched page changes that. Keep write tools behind ask or deny, use read-only credentials, and connect only servers you trust. For the full picture, read MCP security basics.
A realistic setup
Putting it together for an agent that triages bugs on an online-store app: connect a monitoring server to read errors, connect the code host to read issues and open PRs, and connect a read-only database server to check data. Scope the team's shared servers in .mcp.json, keep your personal database connection local, and authenticate each through OAuth or a narrow token.
Then set permissions: allow the read tools, put ask on opening a PR, and deny any destructive database tool. Now you can hand the agent a task and trust the boundaries to hold:
Each line of that prompt maps to a tool on a server you connected, gated by a permission you set. That is the whole idea: the agent's abilities are exactly the servers you wire in, and its limits are exactly the rules you write. To run an agent like this as a program rather than by hand, see the Claude Agent SDK, which connects the same MCP servers through the same client.