Skip to main content
Claude Code tips · Lesson 7 of 13 · 12 min

Add an MCP Server to Claude Code: Scopes and Permissions

Add an MCP server to Claude Code with claude mcp add: http vs stdio, local, project and user scope, .mcp.json, MCP permission rules, and the risks.

By Matthews WongReviewed on Checked against Claude Code v2.1.301

Key takeaway

An MCP server is a separate program or web service that gives Claude Code extra tools, such as a database query or a ticket lookup, over the Model Context Protocol. You add one with claude mcp add, choose where its config lives with a scope, and control what Claude may call with permission rules named mcp__server__tool.

You will be able to

  • Add an http or stdio MCP server with the right flags, and pick local, project or user scope on purpose.
  • Write permission rules that name MCP tools, and know why deny rules win.
  • Spot the security risks of a server you did not write.
Watch it work

Step through the diagram

Press Play, or step with the arrows. Every step is written out below the diagram.

From claude mcp add to one tool call

claude mcp add writes a server entry to a config file; Claude Code connects to the server, lists its mcp__server__tool names, and checks permission rules before each call.

Reviewed on 17 October 2026 · Claude Code v2.1.301

Step 1 of 6. An MCP server is a separate process or web service that offers tools. Claude Code is the client: it reads a config entry, connects, and exposes the server's tools to Claude.

Keys: ← → step · Home / End · K play or pause

  1. An MCP server is a separate process or web service that offers tools. Claude Code is the client: it reads a config entry, connects, and exposes the server's tools to Claude.

  2. claude mcp add writes the entry. Scope decides where: local is the default and private to you, project writes .mcp.json in the repo for everyone, user covers all your projects.

    claude mcp add --transport http docs https://example.com/mcp --scope projectPredict the next step A teammate pulls your repo, which has a project-scoped server. They start Claude Code. What happens? (It connects silently · It asks them to approve it first · It ignores the file until they run /mcp)
  3. In an interactive session Claude Code asks before using a project server from .mcp.json. Claude Code then connects, and claude mcp list shows each server's health.

    claude mcp list
  4. The server lists its tools. Each appears to Claude as mcp__server__tool. With tool search, only the names load at the start; full definitions are fetched when needed.

    mcp__docs__search
  5. Before the call runs, permission rules are checked in order: deny, then ask, then allow. The first match wins, however specific a later rule is. No match means a prompt.

    mcp__docs__* (allow) mcp__docs__search (deny)Predict the next step An allow rule and a deny rule both match one MCP tool call. Which one applies? (Allow, because it is more specific · Deny, because deny is checked first · Whichever is listed first in the file)
  6. The result enters the context window. Output over 10,000 tokens triggers a warning and the default cap is 25,000. A server that fetches outside content can carry prompt injection.

    MAX_MCP_OUTPUT_TOKENS=50000

What is an MCP server, and how do you add one to Claude Code?

The Model Context Protocol is an open standard for connecting AI tools to outside tools and data. In Claude Code, an MCP server is the other end of that connection: a program or web service that offers tools, and sometimes resources and prompts, which Claude can then call. Typical examples are an issue tracker, a database or a monitoring system.

Read more (2)

You add a server with claude mcp add. There are two transports you will use. For a remote server, pass --transport http and a URL; the docs recommend http for remote servers and mark the older sse transport as deprecated. For a local process, use stdio and put the server's own command after a double dash: claude mcp add my-server -- my-command --some-flag. Everything after the -- goes to the server untouched, so its flags are not mistaken for Claude's.

Manage servers with claude mcp list, claude mcp get and claude mcp remove. Inside a session, /mcp shows each server's status and lets you authenticate, reconnect or toggle it. For a remote server that needs OAuth, claude mcp login starts the browser flow from the shell. Environment variables for a stdio server go in --env, headers for an http server in --header.

Which scope should an MCP server use, and what is .mcp.json?

Scope is chosen with --scope (or -s) and answers two questions: who sees the server, and where is it stored. Local is the default: the server loads only in the current project and is not shared. Project stores it in a .mcp.json file at the repository root, so version control shares it with everyone who clones. User loads it in all of your projects but shares it with no one.

Read more (2)

When the same server name is defined in several places, local beats project, project beats user, and then come plugin servers and claude.ai connectors. The whole entry from the winning source is used; fields are not merged across scopes. A .mcp.json entry can read environment variables with a dollar-brace VAR syntax, optionally with a default after :-, in command, args, env, url and headers, so a shared file can avoid holding secrets.

Because a project file can start a process on your machine, Claude Code prompts for approval in interactive sessions before using a server from .mcp.json. claude mcp reset-project-choices clears those answers. The caveat is non-interactive use: a claude -p run cannot show the prompt, so it loads project servers without asking.

How do permissions and security work for MCP tools?

Every MCP tool is named mcp__server__tool, and permission rules use that name. A rule of mcp__puppeteer matches any tool from the puppeteer server, mcp__puppeteer__* matches them with a wildcard, and mcp__puppeteer__puppeteer_navigate matches one tool. Rules run in the order deny, ask, allow, and the first match wins, so a deny in any scope beats an allow. In Manual mode, a tool with no matching rule prompts you.

Read more (2)

Two guard rails keep broad rules safe. An allow rule may use a wildcard only after a literal mcp__server__ prefix, so an allow of mcp__* is skipped with a warning and approves nothing. And a mcp__ rule with parentheses is skipped when settings load; to match a parameter on an MCP tool, pass a deny rule with --disallowedTools. A deny of mcp__* is valid and removes every MCP tool from Claude's context.

The security stance in the docs is short: verify you trust each server before connecting it, because servers that fetch external content can expose you to prompt injection. An MCP tool result is text that goes straight into Claude's context, so a hostile web page or ticket can try to steer it. Prefer servers you can read, scope allow rules to specific tools, and keep secrets in environment variables rather than in a committed file.

How this site does it

The real config behind this lesson

Excerpts from the repository that builds and deploys this website, quoted as they are.

app/components/blogs/McpTransportsStdioVsHttp.tsx:8-24
const STDIO_CONFIG = `{
  "mcpServers": {
    "local-fs": {
      "command": "node",
      "args": ["./dist/server.js"]
    }
  }
}`;

const HTTP_CONFIG = `{
  "mcpServers": {
    "remote-api": {
      "type": "http",
      "url": "https://mcp.example.com/mcp"
    }
  }
}`;

This repository has no .mcp.json of its own, so this is the config shape from the site's article on MCP transports. Both files use the same mcpServers key. A stdio entry names a command and its args; an http entry sets type and a url. The server names, local-fs and remote-api, become the middle part of every tool name, for example mcp__remote-api__tool.

What breaks

Mistakes that cost real time

  • Forgetting the double dash on a stdio server

    Without --, a flag meant for the server, such as --port, is read as one of Claude's own options. Write claude mcp add my-server -- my-command --port 3000. A second trap: --env takes several KEY=value pairs, so a server name placed right after it is read as another pair. Put --transport stdio between them.

  • An allow rule that silently does nothing

    An allow rule of mcp__* is skipped with a warning, and an mcp__ rule with parentheses is skipped on load. Name the server first: mcp__github__get_* allows only that server's get_ tools. Run claude doctor to see skipped rules.

  • Expecting scopes to merge

    If a server named docs exists in local and in project scope, the local entry is used whole. Its fields are not merged with the project entry, so a missing header in the local copy is not filled in from .mcp.json.

  • An unset variable becomes literal text

    In .mcp.json, an unset variable with no default produces a warning and the config loads with the literal dollar-brace text. Add a default after :- or export the variable first. In remote url and headers, some credential variables are read as empty on purpose, so a project file cannot leak them.

  • A committed .mcp.json in unattended runs

    claude -p shows no approval prompt, so every server in a repository's .mcp.json connects. Before running it in a repo you did not write, start with --bare or pass --strict-mcp-config with your own --mcp-config, or list the server in disabledMcpjsonServers.

The three MCP scopes
local (default)This project onlyNo~/.claude.json
projectThis project onlyYes, through version control.mcp.json in the repo root
userAll your projectsNo~/.claude.json
Try it yourself

Do it on your own machine

You need Claude Code and a scratch git repository. This adds a server, inspects it, and then limits what Claude may call. The URL below is a placeholder, so swap in a real server you trust.

  1. Add an http server in project scope, so it is written to .mcp.json in the repo.

    claude mcp add --transport http docs https://example.com/mcp --scope project
  2. Open the file it wrote, and look at the entry. Then list servers and read one in detail.

    cat .mcp.json
    claude mcp list
    claude mcp get docs
  3. Add a stdio server in the default local scope. Note the double dash before the server's own command.

    claude mcp add --env API_KEY=YOUR_KEY --transport stdio airtable -- npx -y airtable-mcp-server
  4. Start Claude Code, approve the project server when asked, and run /mcp to see each server's status.

    claude
    > /mcp
  5. Pre-approve one server's tools and block a risky one in .claude/settings.json. Deny wins over allow whatever the order.

    {
      "permissions": {
        "allow": ["mcp__docs__*"],
        "deny": ["mcp__airtable"]
      }
    }
Quick quiz

Three questions before you move on

Pick an answer to see why it is right or wrong. Nothing is saved or graded, so guess freely.

  1. 1.You want every teammate who clones the repo to get the same MCP server. Which scope do you use?
  2. 2.What does the double dash do in claude mcp add my-server -- my-command --port 3000?
  3. 3.Which rule pre-approves every tool from a server named puppeteer?
Think it through

Answer in your head, then check

  1. 1.Your .mcp.json uses an environment variable for a token, and it is not set on a colleague's machine. What do they get?Show the answer

    Answer:A warning, and the config loads with the literal variable text in place of a value, so the server most likely fails to authenticate. Give the variable a default after :- where a default makes sense, or document that it must be exported.

  2. 2.Why can a deny rule in user settings block a tool that project settings allow?Show the answer

    Answer:Rules are evaluated in the order deny, ask, allow, and the first match decides. Deny rules from any scope are checked before allow rules, so specificity and file order do not matter.

  3. 3.Why is an MCP server that fetches web pages riskier than one that only reads a local file you wrote?Show the answer

    Answer:Its results are text from someone else, placed straight into Claude's context. That text can contain instructions meant to steer Claude, which is prompt injection. The docs advise trusting each server before connecting it.

Go deeper

Related deep dives on this site