
# Approved MCP Servers — Registry & Governance
# Approved MCP Servers — Registry & Governance
## Why this document exists
Connecting to an MCP server is easy — that’s the point of the protocol.
A developer gets a URL (and maybe a header), points a client at it, calls
`list_tools()`, and starts calling capabilities. No new SDK, no bespoke
auth flow, no custom response parsing to learn per system.
That ease is exactly why this document matters. The protocol standardizes
*how* you connect. It says nothing about *what you’re allowed to touch*
once you’re connected. A read-only docs server and a server that can
write to production are indistinguishable at the protocol level — both
are just a URL and a `call_tool()` line. The governance has to live
somewhere else. This is that somewhere else.
## What this document is not
This is not an MCP tutorial. Developers can learn the plumbing — server,
client, transport, discover-then-call — from the project’s own
`README.md` and `docs/MCP_PLUMBING.md`. This document assumes that
literacy and focuses on one question: **which servers are your
developers allowed to connect to, and what is each one actually
authorized to do?**
## The registry
Every approved server gets one row. No server should be in active use
by a team without a row here.
|Server name |Connection string |Auth|Scope / capabilities |Uses sampling|Data sensitivity|Owner |Approved by|Review date|
|---------------------------------------|---------------------------|----|--------------------------------------------|-------------|----------------|------------------|-----------|-----------|
|*(example)* Joke of the Day (local dev)|`http://127.0.0.1:8000/mcp`|none|`joke_of_the_day`, `hello` — no side effects|no |none |Learning/demo only|— |— |
| | | | | | | | | |
| | | | | | | | | |
Fill in real rows as servers are approved. Column definitions:
- **Connection string** — the exact URL a client should be given.
Never a URL a developer has to guess or construct.
- **Auth** — `none`, `bearer token`, `OAuth`, or an internal scheme.
State it plainly; don’t make someone go read the server’s source.
- **Scope / capabilities** — the actual tool names and what each one
can do, not a vague category. “Can read tickets” and “can read and
close tickets” are different scopes and need different approval.
- **Uses sampling** — `yes` if any tool on this server can send a
request back to the connected client’s LLM (MCP’s sampling
capability) rather than just fetching or writing data. See “Servers
that call an LLM” below — this is a distinct risk category from
ordinary tool calls and needs its own sign-off.
- **Data sensitivity** — what kind of data flows through this server:
public, internal, confidential, regulated. This drives who’s allowed
to connect to it, not just whether it works.
- **Owner** — the team or person who runs the server and answers when
it breaks or is misused.
- **Approved by / review date** — who signed off, and when this row is
next due for re-review. Servers don’t stay approved forever by
default.
## Distributing this to developers
Two common patterns:
1. **A `MCP.md` per project or team**, checked into the repo itself,
listing only the servers relevant to that codebase — a subset of
this registry, scoped down. This is what a developer (or an AI
coding assistant reading the repo) actually consults day to day.
1. **A central registry** (this document, or a real internal service)
that project-level `MCP.md` files are generated from or link back
to, so there’s one source of truth for what’s actually approved.
Either way, a developer should never need to ask “is this server okay
to use” in a chat channel. The answer should be sitting in the repo.
## Scoping a new server before it’s approved
Before a server gets a row in the registry, whoever owns it should be
able to answer:
- **What can each tool actually do?** Read-only and read-write tools
should usually be separate tools, not one tool with a `mode` flag —
it’s easier to review and easier to restrict access to just the
read-only half.
- **What’s the blast radius of a bad call?** A tool that returns
documentation and a tool that deletes a record carry very different
risk even if both take one string argument.
- **Who can authenticate to it, and how is that credential scoped?**
Prefer tokens scoped to the minimum permission the tool needs, not
a general-purpose account credential reused across systems.
- **Is it logged?** If a tool call goes wrong, can you find out who
called it, when, and with what arguments?
- **What happens on a bad or unauthorized request?** A 401/403 is the
server doing its job. Confirm it actually returns one rather than
failing open.
## Servers that call an LLM
Most MCP servers just fetch or write data — a docs lookup, a ticket
query, a file read. Some servers do something different: a tool call
triggers its own LLM reasoning step, not just a data operation. This
is a distinct category and deserves its own line of review, because
the risk isn’t just “what data can this touch” — it’s “what can this
get a model to say or decide.”
There are two different mechanisms, and they carry different risk:
**1. The server calls its own LLM.** The tool holds its own API key
(OpenAI, Anthropic, whoever) and makes a model call as part of doing
its job — summarizing a document, classifying a ticket, drafting a
reply. This is straightforward from a governance angle: it’s really
just another external API call the server happens to make, and it
should be reviewed the same way you’d review any outbound call to a
third-party API — what data leaves, what comes back, who’s paying for
it, what happens if that model call fails or is slow.
**2. The server uses MCP sampling.** Instead of holding its own model
subscription, the server sends a request *back* to whichever LLM is
already connected as the client — the same model the developer or
agent is using — asks it to reason about something, and uses that
response inside the tool’s logic. No separate API key for the server
owner, no model lock-in. A server built this way can have no LLM of
its own at all; it’s pure orchestration that borrows the intelligence
already in the loop.
Sampling is the one to flag carefully in this registry, for two
reasons:
- **It’s a prompt-injection surface.** A sampling call lets a server
put text in front of *your* connected model and get it to act on
that text with your model’s authority — including, potentially,
authority to call other tools you’ve approved. A malicious or
compromised server can use sampling to try to manipulate the very
model that’s supposed to be deciding what to trust.
- **It’s not visible from the tool list alone.** `list_tools()` won’t
tell you a tool uses sampling — you have to know, or be told, or
read the server’s implementation. That’s exactly why it belongs as
its own column in this registry rather than folded into “scope.”
Any server with `Uses sampling: yes` should get the same level of
scrutiny you’d give a server that can call other approved tools on
your behalf — because that’s effectively what it can do.
## Header and credential handling — the short version
(Full detail in this project’s `README.md`, under “A note on headers
and security.” Restated briefly here because it’s the part most likely
to go wrong at organizational scale.)
- Credentials belong in environment variables or a secrets manager —
never hardcoded into a client script, a config file, or this
document.
- Prefer scoped, revocable tokens over shared or account-level
credentials. A leaked scoped token is a contained incident; a leaked
account credential is not.
- Connection strings in this registry should always be `https://` for
anything beyond local development. A credential sent over plain
`http://` is not actually protected by having one at all.
- Rotate and re-review tokens on a schedule, not only when something
goes wrong.
## Review cadence
Treat this registry the way you’d treat any access-control list: it
should be reviewed on a schedule, not just when a new server shows up.
A server that’s still approved but no longer used is a bigger risk
than one that was never approved — nobody’s watching it. At minimum:
- New servers: approved before first production use, not after.
- Existing servers: re-reviewed at the cadence set in the “Review
date” column — quarterly is a reasonable default absent a better
reason.
- Deprecated servers: removed from the registry and, ideally, from
DNS/routing — not just left off the list while still reachable.

Comments