# Running an MCP server with SSO for a whole company

> 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.

**TL;DR** Putting an MCP server with SSO in front of your company solves identity and nothing else. Sign-in tells the endpoint who is calling. Deciding what that person may reach, holding it to one role, narrowing it per connector and tool, and recording the result are four separate mechanisms that sit behind authentication rather than inside it.

## What an MCP server with SSO actually settles

Single sign-on answers one question well: who is making this call. That matters
for a [Model Context Protocol](https://modelcontextprotocol.io/) endpoint
because a single address serves the whole company. For an
endpoint that any employee can point a client at, that question has to be
answered before anything else, so getting it right first is the correct order.

Elaichi supports enterprise SAML and OIDC, both built in-house rather than
through a third-party auth vendor, plus
[SCIM v2](https://datatracker.ietf.org/doc/html/rfc7644) for users and groups. Domains
are verified with a DNS TXT record. Alongside the enterprise path there is
sign-in with Google, GitHub and Microsoft, email codes, TOTP multi-factor with
single-use recovery codes, and passkeys.

Onboarding runs four ways, and which one you pick changes how much of the rest
is automatic. Emailed single-use invite links can carry roles and teams already
assigned. Verified-domain auto-join admits anybody with a company address and
gives them a configurable default role. SCIM provisions from the directory.
Just-in-time SSO creates the account at first sign-in.

All four land a person in the organization. None of them decides what that
person's agent may do, and that is the part worth spending the rest of a rollout
on.

## Identity is not authorization

The distinction sounds pedantic until an endpoint is live. Authentication
establishes a subject. Authorization decides what that subject may reach. An
MCP endpoint that has the first and not the second hands every authenticated
employee the full reach of every connected account.

Three layers sit behind sign-in, and keeping them distinct is the single most
common way a design goes wrong.

Permissions are role-based: roughly 38 action strings such as `tool:execute`,
`restriction:manage`, `audit:view` and `connector:create`, grouped into roles.
The system roles form a strict subset chain, `Guest` inside `Member` inside
`Team Admin` inside `People Admin` inside `Org Admin` inside `Org Owner`, each
tier built by spreading the one below so the subset relation holds structurally
rather than by convention. Billing Admin and Auditor sit off that chain.

Resource sharing is a second, separate primitive: a grant of `view`, `use` or
`edit` on a resource to a user, a team, or the whole organization. A member sees
only what they own or what was explicitly shared with them, and no org-level
permission silently widens a listing. That includes org owners and admins.

Restrictions are the third layer, and they are the one that maps most directly
onto what people mean when they say governance: which connectors and which
individual tools a target may reach.

## How restriction rules resolve

Targets are `role` or `user` only. There is no organization target, and that is
a deliberate design rather than an omission. The organization default is the
absence of any rule, which means allow-all, so the way to tighten the default is
to write rules against roles rather than to configure an org-wide setting that
does not exist.

Precedence runs user override, then role rule, then org default. A user-targeted
rule replaces role rules entirely rather than layering on top of them, which is
worth internalizing before writing one: giving a single person a narrow
exception discards every role rule that would otherwise have applied to them.

Within the winning layer, allow rules union, block rules union, and blocks
always beat allows.

The behavior most likely to surprise you is the allowlist stage. It engages on
the presence of an allow rule, not on its contents. An allow rule naming nothing
therefore denies everything. That is the strictest rule expressible in the
system, and it is reachable by accident, so it is worth knowing before you meet
it in production.

> **Worth knowing**
>
> Blocks match on the tool name or the pinned operation. Allows match on the
> pinned operation only. The asymmetry is deliberate: a tool's advertised name can
> be changed by whoever edits the connector's documentation, so the name is a
> token the governed party controls. Governance binds the operation, never the
> label.

Enforcement happens at four points against the same resolver, not once at the
door: browse, connect, advertise and execute, plus a final check on the
fully-substituted outbound URL.

## What the endpoint checks on every call

`tool:execute` gates the whole endpoint ahead of every narrower scope. Without
it, `tools/list` comes back empty and a call returns an in-band error naming the
permission that was missing. Guest, Auditor and Billing Admin do not have it,
which is what makes those seats safe to hand out freely.

OAuth scopes form a second ladder: `mcp:read`, `mcp:write`, `mcp:destructive`
and `mcp:tools`. A tool classified `forbidden` is reachable under no scope at
all. `mcp:tools` does not replace the ladder, so a connected tool whose method
is a delete needs `mcp:destructive` as well as `mcp:tools`.

There is one address for every client in the organization: `POST /mcp`, standard
MCP over Streamable HTTP, JSON-RPC 2.0, stateless, behind OAuth. Everyone points
at the same endpoint and signs in. There is no per-user server to mint, list or
revoke, because the endpoint is constant and the grant is what varies.

## Propagation has a number, and it is not zero

When you change what a role may reach, the change is not instant, and planning
around the real figure is better than assuming the optimistic one.

Role membership and restrictions resolve through a 60-second cache plus edge
propagation, so a change takes effect within about two minutes, on every surface
alike: the endpoint, the console and the REST interface.

Grant revocation is different and faster. `revoked_at` is re-read from the
organization store on every single call with no cache, and removing or
suspending a member revokes every live grant in the same transaction as the
membership change. For those, the next call really is the boundary.

Two mechanisms, two timings. Quoting the wrong one during an incident is how a
team concludes that a control did not work when it simply had not landed yet.

## The discovery threshold changes what the model sees

Past a threshold of 30 tools, connected tools collapse behind two meta-tools,
`search_tools` and `execute_tool`. The threshold counts catalog operations and
connected tools together, and the control-plane catalog alone runs to dozens of
operations, so one connected app is normally enough to trip it. Collapse is the
normal case rather than an edge case, and a rollout plan that assumes a flat
tool list will be wrong on day one.

Only the connected half collapses. Control-plane operations stay listed
individually, and `search_tools` never returns one. Tools withheld by a
restriction are excluded from the count, because they were handed to nobody.

`execute_tool` is only a naming indirection. It unwraps to the same name and the
same arguments and falls through the identical gates. There is no separate
execution path in it and no privilege attached to it, which is the property that
makes the collapse safe rather than merely convenient.

## One limitation to state before you deploy

An MCP server never sees a user prompt. Prompt-injection defenses that work in
an agent window therefore cannot apply at the endpoint, and Elaichi's do not.

What does hold on `POST /mcp`: role-based access per operation, the `forbidden`
classification, output redaction, OAuth scope limits and full audit logging. Each
call writes one entry, succeeded or failed, naming the account actually reached.
Argument names and counts are recorded; argument values never are.

That is a real set of controls and it is worth deploying. It is not the same
thing as a guarantee about model behavior, and a rollout that is sold internally
as the latter will lose credibility the first time a model does something odd
within its permitted scope.

If you are still deciding whether to centralize at all, the tradeoff is set out
in [the case for and against self-hosting](/blog/running-your-own-mcp-servers/).
For a worked example, read
[how one team gets governed access](/blog/roll-out-cursor-to-an-engineering-team/).
See [what the catalog covers](/connectors/) or how the
[controls are described for reviewers](/security/).

## FAQ

### Does SSO alone make an MCP endpoint safe to expose company-wide?

No. Single sign-on authenticates the caller and can carry group membership, but it does not decide which connectors and which individual tools that caller may reach. Those are separate rules evaluated after sign-in, and an endpoint with authentication and no such rules grants everybody whatever the connected accounts can do.

### How do identity provider groups become permissions?

Through SCIM group-to-role mapping. Groups synced from the identity provider map to roles, and each member holds exactly one role. Changing somebody's group therefore changes their role rather than adding to it, which keeps the effective permission set readable.

### Can a person hold two roles at once?

No. Exactly one role per member is enforced by a unique index. Every role is built as a complete persona that includes the tier below it, so the way to give somebody more is to move them up the chain rather than to attach a second role alongside the first.

### Do read-only reviewers need a paid seat?

No. Auditor is a free seat class, as are Guest and Billing Admin. A compliance reviewer who only reads the audit trail does not consume a license, which means there is no cost argument for sharing an admin login with them.

## Read next

- [Running your own MCP servers, or not](/blog/running-your-own-mcp-servers/) — 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.
- [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.
