# Running your own MCP servers, or not

> Running your own MCP servers is cheap for one team and expensive for a company. The line is not the servers, it is credentials, revocation and the audit trail.

**TL;DR** Running MCP servers yourself is the right answer for a single team with a handful of systems and one trusted operator. It stops being the right answer at the point where you have to say who may reach what, revoke it quickly, and prove afterwards which account an agent actually touched. Those three problems are not server problems, so adding servers does not solve them.

## What running your own MCP servers actually buys you

An [Model Context Protocol](https://modelcontextprotocol.io/) server you run is
a process you control. You choose the version, you see
the logs, you can read the code, and nothing about your data leaves a boundary
you did not draw. For a team that is already comfortable operating services,
none of that is hard, and the first server is genuinely quick.

It also fits a real situation. One team, three or four systems, a shared service
account per system, and one person who knows how it is wired. That team does not
have an access-control problem, because everyone on it is allowed to reach
everything the servers reach. The server is doing the only job it needs to do,
which is speaking the protocol.

The reason to look further is not that this setup is bad. It is that two of its
assumptions stop holding at a predictable point, and neither of them is about
servers.

## The credential math changes with the second team

A self-hosted server needs a credential for the system behind it. With a shared
service account, that is one credential per system, and the agent acts as that
account for everybody.

That is workable until someone asks a question the shared account cannot answer:
should this person be able to reach this record. A shared account has one answer
for the whole company, so the moment two groups need different answers, the
credential has to become per person. Now the count is people multiplied by
systems, and every one of those is a secret that has to be issued, stored,
rotated and eventually removed.

In Elaichi, connector credentials are not held by the control plane at all. A
separate credential service owns per-account secrets, encrypted at rest with
AES-256-GCM, and owns refresh as well. A refresh that fails marks the connection
`needs_reauth` instead of failing quietly, which matters because a quiet
credential failure looks exactly like an agent deciding not to do something.

Single sign-on in front of that endpoint is a separate decision, and it
[settles identity rather than authorization](/blog/mcp-server-with-sso/).

There is one address for clients: `POST /mcp`, standard MCP over Streamable
HTTP, behind OAuth. There are no per-toolbox URLs and no embedded tokens, so
there is no per-user server to mint, list or revoke. The endpoint is constant
and the grant is what varies.

## Revocation is the part that decides it

Ask how long it takes to cut somebody off. The answer separates the two
approaches more cleanly than any feature list.

With servers holding per-person credentials, revocation is a sweep. You need the
list of every credential that person holds, across every server, and you need it
to be complete. Nothing produces that list for you, and an incomplete sweep
leaves a working credential behind with no signal that it is still there.

Through a control plane the timings are specific, and worth stating precisely
because they are not all the same. Removing or suspending a member revokes every
live grant in the same transaction as the membership change, and the OAuth grant
is re-read from the org store on every single call with no cache, so for those
the next call is genuinely the boundary. Role membership and restrictions are
different: they resolve through a 60-second cache plus edge propagation, so a
change to what a role may reach takes effect within about two minutes, on every
surface alike.

Two minutes is not instant, and saying so is the point. A number you can plan
around beats a promise you cannot verify.

## Offboarding has a step people forget

Removing a person is not only a credential problem. It is also a question of
what happens to the connections they set up.

Elaichi refuses the removal if a personal connection that person owns is still
referenced by a toolbox entry, until that connection is resolved: transferred to
the org, to a team, or to another member, or deleted. Unreferenced personal
connections are cleaned up. A private connection is not transferable at all,
because a credential that only its owner could ever use does not become somebody
else's when its owner leaves.

That preflight is the kind of thing that does not exist until someone builds it,
and its absence is invisible until the week after a departure, when a scheduled
job stops working and nobody can say why.

## What the audit trail has to answer

The question that arrives after an unexpected change is narrow: which account
did the agent write to. Not which server, and not which user was logged in.
Which of the two Notion workspaces.

Elaichi writes one entry per tool-call attempt, succeeded or failed, and both
name the account. The recorded connection is the one actually reached, taken
from the execution rather than from the intent, which is the distinction that
makes the record usable. Recorded alongside it: the operation and tool, the
classification, whether it was approved, the outcome, and an error code.
Argument names and counts are logged. Argument values never are.

`actor_kind` is a field rather than an inference, and `ai_assistant` is one of
its values. Whether an action was taken by an AI is written at the point of
action, not guessed later from a user agent string. Self-hosted servers can
produce logs, and often good ones, but they produce them per server in whatever
shape each one emits, and correlating those by eye is the work you are actually
signing up for.

## When running your own is the right call

There are cases where the honest answer is that you do not need a control plane.

If one team uses two systems, shares a service account for each, and nobody
outside that team will ever connect, a self-hosted server is less machinery for
the same result. If a system has no per-user concept at all, adding per-user
governance in front of it buys nothing. If your constraint is that a particular
workload cannot leave hardware you own, that constraint outranks convenience and
should win.

The mixed shape is also legitimate. Route the systems that need per-person rules
and a defensible trail through the governed endpoint, and leave a single-account
internal tool where it is. That is a smaller change than a migration and it puts
the effort where the risk is.

## The trade-off, stated plainly

A control plane centralizes. That is the benefit and the cost in the same
sentence. One endpoint means one place to reason about access, and it also means
one dependency in the path between a client and the systems it reaches.

What you get for that is a single answer to who may reach what, a revocation
story with numbers attached, and one record shape that answers what happened
without correlating two systems by hand. What you give up is the ability to read
every line of the process in the path.

One more limit worth naming, because it is easy to assume otherwise: an MCP
server never sees a user prompt, so no endpoint of this kind can be protected
against prompt injection, and Elaichi's is not. What does hold on the endpoint is
role-based access per operation, the forbidden classification, output redaction,
OAuth scope limits and full audit logging. That is a real set of controls, and it
is not the same thing as making a model trustworthy.

If you are weighing the two, the useful question is not which is more secure in
the abstract. It is whether you can currently produce, in under a minute, the
list of every system one departing person's agent could reach. If you can, stay
where you are. If you cannot, that is the gap, and more servers will not close
it.

For the shape of a first rollout, see
[a team-by-team playbook](/blog/roll-out-cursor-to-an-engineering-team/).
Browse [what connects today](/connectors/) or read how the
[access model is layered](/product/).

## FAQ

### Is it cheaper to run our own MCP servers?

For one team and a few systems, usually yes. The cost of self-hosting is not the compute, it is the per-person work of issuing credentials, removing them when somebody leaves, and answering questions about what an agent did. That work scales with people and systems multiplied together, while the server count scales only with systems.

### Can I keep some systems self-hosted and route the rest through a control plane?

Yes, and it is a common shape. The systems that need per-person access rules and an audit trail go through the governed endpoint. A system with one service account and no per-user distinction can stay where it is. Splitting on that line is more honest than moving everything at once.

### What breaks first when a self-hosted setup grows?

Offboarding. Credentials issued per person across several servers have no single list, so removing somebody becomes a manual sweep that is easy to do incompletely. The failure is silent, because a credential that was missed keeps working and nothing reports it.

### Does a control plane mean giving up direct access to a system?

No. The systems keep their own interfaces and their own admin consoles. What changes is the path an AI client takes to reach them, and that path is the one that needs rules, because it is the one a model drives.

## Read next

- [Running an MCP server with SSO for a whole company](/blog/mcp-server-with-sso/) — An MCP server with SSO answers who is calling. It does not answer what they may reach, and the gap between those two questions is where most rollouts get stuck.
- [How to roll out Cursor to an engineering team](/blog/roll-out-cursor-to-an-engineering-team/) — Roll out Cursor to an engineering team in an order that stays reversible: scope first, one role, narrow rules, then the systems people ask for.
- [Connect Notion to ChatGPT without oversharing](/blog/connect-notion-to-chatgpt-privately/) — To connect Notion to ChatGPT without exposing private pages, the connection has to carry each person's own access rather than a shared workspace credential.
