On this page
To limit what an agent can do with an MCP server, block tools where the call is refused, not only where the list is shown. Use a deny rule in your client's tool permissions, the server's read-only flag, and a token or role that can't write. Hiding a tool saves context. It doesn't always stop a call, and a server allowlist picks which servers run, not what their tools can do.
This question keeps coming up. One r/mcp poster who
tested 340+ MCP servers
argued that hiding tools only saves context, and the real boundary is
rejecting the call, including calls by name that skip the tool list. On
r/ClaudeAI, people asked
how to handle MCP tools that can modify real data;
the top answer was read-only by default with separate guardrails on writes.
Other threads ask about
tool overload,
new risks from agent protocols
(the top answer starts with an allowlist), a security team asking
which MCP servers are allowed,
read-only first, writes behind an allowlist,
a NetSuite
restricted role that still lists every standard tool,
and
what readOnlyHint: true may claim.
We read these from search snippets, so check a thread before you quote it.
Every client setting below was checked against the client's own docs on 5 November 2026. Spec quotes are from MCP 2026-07-28, the current version.
Hidden from the model vs refused when called
A tool can be stopped at three points. Only the later two are real limits.
- The list the model sees. The client asks the server for its tools
(
tools/list) and puts them in the model's context. Remove a tool here and the model doesn't know about it. That saves context. It is not a lock on its own: the model, a prompt injection or a script can still try the name. - The client's check on each call. Before the client sends
tools/call, it can deny the call, ask you first, or let it run. This holds for anything that goes through the client. - The server and the system behind it. A read-only server doesn't have the write tool at all. The database or API checks the token or role on every request. This holds whatever the client does.
The MCP spec backs this split. A server "MAY vary [the tool list] by the
authorization presented on the request — for example, returning only the
tools the caller's granted scopes permit". And it says servers "MUST...
implement proper access controls". A call to a tool the server doesn't have
gets a protocol error (-32602, "Unknown tool").
The spec also says "there SHOULD always be a human in the loop with the ability to deny tool invocations." That's point 2. It's a good habit, not a replacement for point 3: a read-only server and a token that can't write (steps 3 and 4 of the checklist below).
MCP tool permissions by client: what each setting does
"Documented" means the client's docs say so. "Not documented" means we found no statement either way, so don't rely on it.
| Client | Setting | Hides from the model | Refuses the call |
|---|---|---|---|
| Claude Code | deny rule with the tool's name, e.g. mcp__github__merge_pull_request |
Yes (documented) | Yes |
| Claude Code | ask rule |
No | Asks you every time |
| Claude Code | allow rule |
No | No, it runs without asking |
| Claude Code | Server toggled off in /mcp |
Yes, the server isn't connected | Yes |
| Claude Code (admin) | allowedMcpServers / deniedMcpServers, managed-mcp.json |
Whole servers only | Whole servers only |
| Cursor | Default approval prompt | No | Asks you every time |
| Cursor | Run Mode allowlist | No | No, allowlisted tools run at once |
| Cursor | Server toggle in Customize | Yes, "won't load or appear in chat" | Yes |
| Cursor (admin) | Team MCP allowlist: command, URL and tool allowlists | Servers outside it don't run | Tool allowlists limit which tools "can run automatically" |
| Cursor | Turn off one tool | Not documented | Not documented |
| VS Code | Tools picker (deselect a tool) | Removes it from what the agent has | Not documented |
| VS Code | chat.tools.eligibleForAutoApproval set to false |
No | Always asks, never auto-approves |
| VS Code | preToolUse hook that denies |
No | Yes (documented) |
| VS Code | Disable on a server | Yes | Yes |
| VS Code (admin) | allowedMcpServers / deniedMcpServers, chat.mcp.access: "none" |
Whole servers only | Whole servers only |
The pattern: every client can switch off a whole server, and every client can make you approve calls. Only Claude Code documents a per-tool rule that both hides the tool and refuses it.
Claude Code: deny rules are the hard block
Claude Code's permissions docs
are explicit. Rules run "deny, then ask, then allow", and "An allow rule can't
carve an exception out of a deny rule." A deny rule that names a whole tool
"removes the tool from Claude's context entirely, so Claude never sees it."
A glob deny such as mcp__* does the same for every MCP tool.
So a deny rule both hides and refuses. To shut off one server's write tools:
{
"permissions": {
"deny": [
"mcp__github__merge_pull_request",
"mcp__github__delete_*"
],
"ask": ["mcp__github__*"]
}
}
Two details catch people out. Allow globs must name a real server
("mcp__*" as an allow rule approves nothing). And rules with parentheses on
MCP tools are skipped in settings files; to match a tool's arguments, pass the
deny rule with --disallowedTools instead. A deny at any level, including
managed settings, can't be undone by an allow somewhere else.
The full rule syntax, server names with dots, and allow examples are in Claude Code MCP setup. We won't repeat them here.
For teams, Claude Code's
managed MCP page adds server
allowlists and denylists. Match servers by serverUrl or serverCommand, not
serverName: the docs warn a name "is not a security control" because "a user
can call any server github." These lists work on whole servers. Per-tool
limits are still deny rules.
Cursor: approval, admin allowlist, server toggle
From Cursor's MCP docs:
- Approval is on by default. "Cursor asks for approval before using MCP tools by default." MCP follows the same Run Modes as terminal commands: "in Auto-review mode, allowlisted MCP tools run immediately and everything else is routed through the classifier."
- Admins can set an allowlist. Under Team Settings > MCP Configuration, command entries approve local servers and URL entries approve remote ones. "Tool allowlists restrict which tools from an approved server can run automatically." Read that wording closely: it's about running without a prompt. The docs don't say an unlisted tool is blocked.
- You can switch off a whole server. Open Customize in the sidebar and use the toggle. "Disabled servers won't load or appear in chat."
- There's no documented way to turn off one tool. If you need one tool gone, use the server's own flags (next section) or a narrower token.
If you switched a server off and its tools vanished, that's the toggle, not a bug. If tools vanished on their own, see MCP tools not loading in Cursor.
VS Code: the Tools picker and approvals
VS Code's tools page says to "Select or deselect tools to control which ones are available to the agent." It also says "Tool availability is separate from tool approval" and that enabling a tool "doesn't bypass the configured approval and permission controls." A chat request can have at most 128 tools enabled.
Is a deselected tool a hard block? The docs don't say. They don't state whether a deselected tool could still be called by name. Treat the picker as a way to keep the list short and save context, not as a security control.
What VS Code does document, from its approvals page and AI settings for enterprises:
- Always ask for one tool: set it to
falseinchat.tools.eligibleForAutoApproval. The confirmation dialog then doesn't offer auto-approval for it. The terminal rules are similar: afalserule "requires approval. It does not block the command." - Refuse a call: "A
preToolUsedenial can block a tool call." That's a hook you write. - Reset what you approved: run Chat: Reset Tool Confirmations, or Chat: Manage Tool Approval to change single approvals.
- Admins:
allowedMcpServersanddeniedMcpServerslimit servers, andchat.mcp.accessset to"none"turns MCP off. We found no policy that blocks one named tool.
Server read-only flags that drop write tools
The strongest per-tool limit often lives in the server. A read-only mode usually doesn't register write tools at all, so they are missing from the list and a call to them fails at the server. Some examples from our listings:
- GitHub: the
/mcp/readonlyURL orX-MCP-Readonly: trueheader (hosted),--read-only(local). Toolsets cut the list further. - Supabase:
read_only=trueruns every query as a read-only Postgres user, andfeatures=limits tool groups. - MongoDB:
--readOnlydrops create, update and delete tools. - Linear: a separate read-only endpoint.
The full table is in read-only modes in our directory.
Two warnings:
- A flag is code, and code has bugs. An advisory for the DBHub database
server
(GHSA-j656-3hf2-fvjc,
fixed in 0.22.6) showed its read-only SQL check could be bypassed on MySQL
and MariaDB with a comment trick. MongoDB's own docs warn that
aggregateis labelled a read tool, yet$outor$mergewrites data. - A label is not a flag.
readOnlyHint: trueis the server describing itself. The spec says clients "MUST consider tool annotations to be untrusted unless they come from trusted servers." See MCP tool annotations.
That's why the flag is step 3 and the token is step 4.
The token or role is the real limit
Every request the server makes to GitHub, Postgres or Stripe carries a credential. The upstream system checks it every time, whichever client, tool list or prompt sent the call. If the credential can't write, nothing the agent does through that server can write.
- Use a narrow token. A fine-grained GitHub token limited to a few repos
and read permissions. A database user with a read role on one database.
MongoDB's docs say: "Never use database
writecredentials with the MCP Server." - Prefer OAuth with narrow scopes where the server supports it. A
well-built server hides the tools your scopes can't use: GitHub's local
server does this when you narrow
--oauth-scopes. See MCP authentication. - Expect the list and the role to disagree. In the NetSuite thread above, a restricted role still listed every standard tool. That's the opposite case: the model sees the tools, but the role refuses the call. Safe, but noisy, and the failures cost retries. Narrow the list too.
- One identity per agent. A shared admin token makes every limit above only as good as the client config on each laptop.
A gateway can enforce the same rules for a whole team in one place; see MCP gateway.
Lock down a server in 5 steps
0 of 5 done
Tick these off for each server that can change something.
1. Switch off servers you don't need today
Toggle them off in the client (/mcp in Claude Code, Customize in Cursor,
Disable in VS Code). Fewer servers, fewer tools to worry about. This also
saves the most context; see
why MCP servers eat your context window.
2. Deny or always-ask the write tools
Claude Code: deny the tools you never want and ask the rest. VS Code: set
write tools to false in chat.tools.eligibleForAutoApproval. Cursor: don't
add write tools to your Run Mode allowlist.
3. Turn on the server's read-only mode
Use the flag, URL or header from the server's README. If it has none, check
for --tools, --toolsets or features to load only what you use.
4. Swap in a read-only token or role
Make the credential match the job. A read-only job gets a read-only key. Then a bug in steps 2 or 3 can't turn into a write.
5. Re-check after every update
New versions can add tools. Look at the tool list again, and check Registry Watch for advisories. Our 5-minute safety check covers what to look for.
When a client setting isn't enough
If an agent will touch production data, a client setting on someone's laptop is a weak line. Put the limit in the server and the credential, and log the calls. Building a server with read-only mode and per-agent tokens from the start is the kind of work we do; if you need one, get in touch.
Frequently asked questions
How do I disable a single MCP tool?
In Claude Code, add a deny rule with its full name, like
mcp__github__merge_pull_request. That hides it and refuses the call. In
VS Code, deselect it in the Tools picker, but the docs don't say that blocks a
call by name. In Cursor, there's no documented per-tool switch: use the
server's read-only flag or a narrower token.
Can I disable one MCP tool in Cursor?
Not according to Cursor's docs. You can turn a whole server off in Customize, keep the approval prompt on, and admins can set tool allowlists, which control which tools run automatically. To remove a tool, use the server's own options.
Does deselecting a tool in VS Code block it?
VS Code's docs say deselecting makes a tool unavailable to the agent, but
don't say whether a call by name is refused. For a documented block, use a
preToolUse hook that denies the call, or a read-only server and token.
What is an MCP allowlist?
A list of what's allowed to run. In admin settings it usually means servers: Claude Code, Cursor and VS Code all let admins allow servers by URL or command. In Cursor, a Run Mode allowlist means tools that run without asking. An allowlist of servers doesn't limit what an allowed server's tools can do.
Are MCP tool permissions enough to stop an agent?
They stop calls that go through that client. They don't help if the same token is used somewhere else, or if a read-only tool writes anyway. Pair them with a read-only server mode and a token that can't write.
How do I make an MCP server read-only?
Use the server's own read-only flag, URL or header from its README: for
example --read-only or the /mcp/readonly URL for GitHub, read_only=true
for Supabase, --readOnly for MongoDB. Then give it a read-only token or
role, because a flag is code and can have bugs.
Is it safe to auto-approve tools marked readOnlyHint?
Only for servers you trust. The label comes from the server, and the spec says to treat it as untrusted otherwise. Claude Code doesn't skip prompts because of it; your permission rules decide.
Does hiding MCP tools save context?
Yes. Fewer tools in the list means fewer tokens before you type. That's the main reason to hide tools; blocking is a separate job. Why MCP servers eat your context window covers measuring it.