Skip to content

Scope the agent's reach so a connection isn't a blank check

The connection works, and that’s exactly the moment to slow down. You’ve given an autonomous agent a live tool that can run queries against a payments database and read your ticket tracker. An MCP server is reach, and reach you didn’t think about is reach that surprises you later — a stray prompt, a compacted context, and the agent runs a query you’d never have approved. Two questions decide how much you’ve actually handed over: where the connection lives and what the connection can do.

When you ran claude mcp add, you implicitly chose a scope. There are three, and the choice is about who else gets this server and whether it’s checked into the repo:

  • Local (the default) — the server loads only in this project and stays private to you, stored in your home ~/.claude.json. Right for a personal or experimental connection, or one whose credentials you don’t want anywhere near version control.
  • Project — the server is written into a .mcp.json file at the repo root, designed to be committed so every teammate gets it. Right for a connection the whole team should share.
  • User — the server loads across all your projects but stays private to you. Right for a personal utility you reach for everywhere.

You set it with --scope:

claude mcp add --transport stdio db --scope project -- npx -y @bytebase/dbhub \
--dsn "postgresql://readonly:pass@localhost:5432/payments"

The scopes, the precedence order when a server is defined in more than one, and the exact file each writes to are all on the MCP docs page.

There’s a safety rail built into project scope worth knowing. Because a committed .mcp.json is a way for a repo to make your agent talk to its servers, Claude Code won’t silently trust one — a project-scoped server from .mcp.json shows up as pending approval, and you have to okay it before its tools go live. That’s deliberate: pulling a repo should never auto-connect your agent to someone else’s database.

What it can do: keep the connection narrow

Section titled “What it can do: keep the connection narrow”

Scope decides who shares the server. The connection string decides what the server can do — and that’s where the real leverage is.

Look back at the database server. The DSN used a readonly user:

--dsn "postgresql://readonly:pass@localhost:5432/payments"

That’s not incidental. The agent does not need write access to debug a failing charge — it needs to read the schema and the rows. Handing it a connection that can only read is the difference between “the agent answered a question wrong” and “the agent dropped a table.” The narrowest credential that still does the job is the one to give it. This is the same instinct as the permissions chapter, pushed down a layer: there, you set what the agent could do to your machine; here, you’re setting what an external system will let the agent do, at the connection itself.

A narrow connection and a deliberate scope cover the MCP side. But the agent now has a tool, and you already know how to govern tools. MCP tools are named mcp__<server>__<tool>, and that name slots straight into the permission rules from the permissions chapter. If a server exposes something you never want fired, a deny rule is the hard wall:

.claude/settings.json
{
"permissions": {
"deny": ["mcp__db__execute"]
}
}

Now the read-only intent is enforced in two places at once — the database itself rejects writes, and the agent is forbidden from even calling the write tool. Defense in depth: the connection is narrow, and the permission layer is narrow, so a mistake has to get past both. That’s the posture you want for any server with reach into something that matters.

So the agent has reach, and the reach is fenced. But notice what both of those tools — the connection scope and the deny rule — have in common: they shape what the agent is allowed to do, and then trust it to operate inside the lines. For most work that’s the right model. For the one thing in this codebase you cannot afford to get wrong, it isn’t enough — and that’s the gate we build next.