MCP
MCP is a way for a program to tell Claude “here are some tools, here is what each one takes, call them if you like”. The program can be a local process or something over HTTP. Claude Code starts it, asks what it has, and adds those tools to the ones it already had.
That is all of it. The reason it gets talked about more than that description warrants is that it is the thing standing between Claude and every system that is not your filesystem: your ticket tracker, your database, your logs.
Connect one
The canonical starter is a filesystem server, because it needs no account:
claude mcp add --scope local stock-files \
-- npx -y @modelcontextprotocol/server-filesystem /tmp/stockroom/data
Added stdio MCP server stock-files with command: npx -y ... to local config
Check it started:
claude mcp list
stock-files: npx -y @modelcontextprotocol/server-filesystem ... - ✔ Connected
The three scopes decide who gets it. --scope local is you, in this project. --scope user
is you, everywhere. --scope project writes a .mcp.json in the repository root, and that
one is committed, so your team gets it too.
The approval prompt is a security feature
Add the same server with --scope project and claude mcp list says something different:
stock-files: ... - ⏸ Pending approval (run `claude` to approve)
A project-scoped server lives in a file in the repository. Anyone who can commit can add one, and a stdio server is a command that runs on your machine. So Claude Code refuses to start it until you have said yes, once, interactively.
That prompt is the last thing between git clone and running somebody’s process. Read the
.mcp.json before you approve it, the same way you would read a postinstall script.
Find out what you actually granted
This is the part of the lesson worth the whole lesson.
I connected that server with an argument saying /tmp/stockroom/data, intending to give it
the data directory. Then I asked it what it thought it was allowed to touch:
Call mcp__stock-files__list_allowed_directories and print the raw result.
{"content":"Allowed directories:\n/private/tmp/l24\n/private/tmp/l24"}
The whole repository. Twice. Not the data directory I named on the command line.
And the tools it brought:
mcp__stock-files__create_directory mcp__stock-files__read_media_file
mcp__stock-files__directory_tree mcp__stock-files__read_multiple_files
mcp__stock-files__edit_file mcp__stock-files__read_text_file
mcp__stock-files__get_file_info mcp__stock-files__search_files
mcp__stock-files__list_allowed_directories mcp__stock-files__write_file
mcp__stock-files__list_directory mcp__stock-files__move_file
mcp__stock-files__list_directory_with_sizes mcp__stock-files__read_file
Fourteen tools. I wanted to read some files. I got write_file, edit_file, move_file
and create_directory, across a directory tree wider than the one I asked for.
Nothing here is a bug and nothing was hidden. The server decided its own surface, and I never asked. That is the shape of the risk with MCP, and it is not “a server might be malicious”. It is that you grant a tool surface, not a purpose, and the surface is usually larger than the purpose.
So make these two commands a habit whenever you connect something:
List the exact names of every tool the <name> server provides.
and, for anything that takes a path or a scope, ask it what it thinks that scope is. The answer is a fact about your machine, not a guess.
Narrowing it
Permission rules from lesson 18 work on MCP tools, and this is a good place for them.
{
"permissions": {
"allow": ["mcp__stock-files__read_file", "mcp__stock-files__list_directory"],
"deny": ["mcp__stock-files__write_file", "mcp__stock-files__edit_file",
"mcp__stock-files__move_file"]
}
}
A bare deny removes the tool from Claude’s context entirely, so it does not know the
capability exists. mcp__* denies every MCP tool from every server, which is a reasonable
default for a repository you are only reading.
Without rules, every one of those fourteen tools is available and governed by whatever permission mode you are in. In auto mode, that is a classifier’s judgement rather than yours.
When would a script be better than MCP?
Most of the time, honestly.
A tool is worth exposing over MCP when Claude needs to choose to call it, with arguments it works out, repeatedly, during a conversation. Querying your issue tracker qualifies. Reading rows out of a staging database qualifies.
A tool is not worth exposing when you already know it needs running. npm test does not
need MCP; it needs the allow rule from lesson 18. A nightly report does not need MCP; it
needs cron.
The test I would use: would I ever want Claude to decide, mid-task, to call this? If the answer is no, you are adding a server, a process, a config file and a tool surface in order to replace a command.
The second test, harder: does it read anything that would be embarrassing in a transcript? MCP output lands in your context and your context lands in the transcript on your disk. A server that returns customer rows returns them into a file with a retention period.
Managing them
claude mcp list # what is configured, and whether it connected
claude mcp get <name> # the details of one
claude mcp remove <name> # take it away
/mcp in a session shows the same thing plus authentication state for HTTP servers.
A server that will not connect is nearly always one of three things: the command is not on
your PATH, an environment variable it needs is missing, or it needs an OAuth login that
has not happened (claude mcp login <name>).
Your turn
Connect the filesystem server to your stockroom clone’s data directory, then find out what
you gave it.
claude mcp add --scope local stock-files \
-- npx -y @modelcontextprotocol/server-filesystem "$PWD/data"
claude mcp list
Allow its tools for the exercise, in .claude/settings.local.json:
{ "permissions": { "allow": ["mcp__stock-files__*"] } }
Then, in a session, ask two things:
List the exact names of every tool the stock-files MCP server provides. Names only.
Call mcp__stock-files__list_allowed_directories and print its raw result, nothing else.
Check: the tool list has more than ten entries and includes
write_file, and the allowed-directories result names a directory. Compare that directory with the one you typed. If they differ, you have just found the thing this lesson is about, on your own machine, in about ninety seconds.
Then take it away again, which is a step people skip:
claude mcp remove stock-files
claude mcp list
Recap
MCP is a program telling Claude what tools it has. A stdio server is a process on your machine; an HTTP one is a service.
Three scopes: local is you here, user is you everywhere, project is committed and requires an interactive approval before it will start, because it is a command a repository asked your machine to run.
You grant a tool surface, not a purpose. Ask a new server for its tool names and for what it thinks it is scoped to, because the answer is often wider than the argument you passed. Narrow it with deny rules.
Reach for a script or an allow rule instead whenever you already know when the thing should run. MCP is for tools Claude should be able to choose.
Next: packaging the command, the skill, the agent and the hook into one thing a team can install.