Skip to content

14 min read

Server registration and escalation tiers

On this page

A policy of the form no third-party MCP server may be connected to any agent without prior security review is the one most organisations write first, and it has a specific failure mode that is arithmetic rather than cultural. This chapter is about the replacement: a registration requirement, meaning that every server-to-agent connection exists as an entry in a manifest the runtime reads, with a tier attached that decides which credentials and which network reach the platform issues for it.

The distinction that makes the replacement work is between two events that a single policy usually conflates. Connecting a server is frequent and mostly boring. Giving an agent a capability that can act on the world — deploy, payment, mail to external addresses, permission changes — is rare and always interesting. A policy that gates the first cannot be serviced; a policy that gates the second has maybe five or six cases a quarter, which one person can handle properly and quickly.

The arithmetic that guarantees the queue

Review demand at a forty-engineer organisation building agents runs at three to four new server requests a week and rises as more teams build them. A careful review — reading the source, checking the dependency tree, understanding the credential handling, per topic 6.14 on trusting third-party servers — takes four to six hours. That is 16 to 24 hours of demand a week against one part-time reviewer with perhaps four hours available. The queue is not a symptom of a disorganised team; it is what those numbers do. Doubling the reviewer's time still leaves a deficit, and the deficit compounds, which is what a 19-day median looks like on the way to a 40-day one.

Work the multiplication, including the choice buried inside it, because that choice separates an honest estimate from a convenient one. Requests arrive at three to four a week and a careful review takes four to six hours, so demand is a rate multiplied by a duration. Taken at the bottom of both ranges that is twelve hours a week; the figure above is computed at the top of the request range instead, which is the right end to take for a quantity the same sentence says is rising. Now set the product against the supply, which is one part-time reviewer with about four hours. The shortfall is between twelve and twenty hours every week, and a shortfall of that kind is not a backlog that clears — it is added to the queue each week whatever happened in the week before. Doubling the reviewer to eight hours halves nothing and leaves a shortfall of eight to sixteen hours a week, still accumulating. That is why the queue is arithmetic rather than culture, and it also names the only two moves available: reduce the number of items, or reduce the hours each one takes.

There is a second cost that does not show up in the queue length. When engineers learn that the sanctioned path takes three weeks, they stop asking about it at all, so the security team loses the informal channel where somebody would have mentioned in passing that the new server reads the production replica. An anonymous survey run alongside one fleet inventory found that 14 of the 22 engineers with unregistered servers knew the policy existed and had decided it did not apply to a local-only setup, and 5 had submitted a request, waited, and connected the server anyway while waiting. The policy trained people to route around it, and that training persists after the policy is fixed, which is why the replacement has to be visibly faster rather than merely faster.

Registration first, review where it is scarce

The replacement has three requirements, and the test for each is whether a machine enforces it.

The first is that every server-to-agent connection is registered. Registration is a pull request adding an entry to a manifest, and the entry is auto-merged by CI when it matches the default profile. There is no human in that path.

yaml
# platform/agents/oncall.yaml
servers:
  - name: knowledge-mcp
    image: ghcr.io/atlas/knowledge-mcp@sha256:9f3c…
    surface_sha256: b21d7c…
    owner: platform-team
    tier: read_only            # egress: internal CIDRs only, read scopes only
    expires: 2026-09-14
tools:
  - knowledge_search
  - read_page
  # trigger_deploy: not permitted in an agent holding a read_only-tier server

The unit of registration is the pair, not the server, because of the union property from chapter 2: once two servers share an agent, the union of their tools is available to text returned by either of them. The same server is a different risk in an agent that can only read and in an agent that can deploy, so one server connected to a docs agent and the same server connected to an on-call agent are two entries and potentially two decisions.

Registering the pair rather than the server carries a cost worth naming, because it is the objection somebody in the room will raise. The number of entries is servers multiplied by agents rather than servers added to agents, so a manifest grows faster than an inventory of software would. That is the correct shape all the same, since the thing being decided is a property of the combination — the union of the tools available in one session — and a property of a combination cannot be recorded as a fact about either half of it. Registering servers alone would be cheaper and would answer a question nobody is asking. What keeps the multiplication tolerable is that almost every pair takes the default tier and never reaches a person, which is the same reason the design puts the human step somewhere else entirely.

The second requirement is that credential scope and network reach come from the tier rather than from the request. An engineer registering at read_only does not choose scopes; the platform issues a read-scoped token from the vault and applies the default-deny egress policy from chapter 4, on what an honest relay server delivers. This is the part that makes the fast path safe enough to be the default, because the worst case of a careless registration is a server that can read what it was going to read anyway and cannot talk to the internet.

The third is that the surface is pinned and a change blocks. The CI job that merges the entry also runs the server once, hashes tools/list, resources/list and prompts/list, and writes surface_sha256. At runtime, a mismatch stops the agent from starting.

bash
$ agentctl add knowledge-mcp --agent oncall --tier read_only
✓ image resolved  ghcr.io/atlas/knowledge-mcp@sha256:9f3c…
✓ surface hashed  4 tools, 1 resource, 0 prompts → b21d7c…
✓ token issued    read:issue read:project read:comment  (30d, auto-rotate)
✓ egress policy   10.42.7.0/24:443
✓ PR #2291 opened and auto-merged (default profile)
  connected in 41s

Forty-one seconds against 19 days is the entire argument for why this gets used. A policy competes with the local config file on convenience, and if it loses on convenience it will be routed around by people acting in good faith.

Divide the two, because the ratio is what makes that unanswerable rather than merely appealing. Nineteen days is a little over one and a half million seconds, and forty-one seconds goes into it roughly forty thousand times. No quantity of goodwill, training or policy language competes with a factor of forty thousand. What the ratio actually buys is narrower and more useful than compliance in the abstract: the sanctioned path is now faster than the unsanctioned one, because editing a config file by hand and restarting the client takes minutes rather than seconds. Once that inequality flips, the policy stops depending on anyone's discipline, and the argument about culture turns into an argument about tooling, which is a solvable kind.

Where a human is required

Flowchart: agentctl add → Default profile?; Default profile? → Auto-merge ~40s (read scopes, internal egress only); Default profile? → Named approver + 6.14 vetting (write scopes, external egress, or acting tool in agent); Named approver + 6.14 vetting → Approved?; Approved? → Merged with tier override (yes); Approved? → Rejected, reason recorded (no); Auto-merge ~40s → Runtime: surface hash check; Merged with tier override → Runtime: surface hash check; Runtime: surface hash check → Refuse to start (mismatch).

Three conditions send a registration to a person: the server needs write scopes on any upstream system, it needs egress beyond the internal CIDRs, or the agent it is joining holds a tool in the acting class. Those are the cases from chapters 3 and 4 where the blast radius stops being bounded by the schema, and they are rare enough that the reviewer can do the job properly. In the first quarter after one such change the organisation saw 27 registrations, of which 5 escalated. Five reviews in a quarter is a workload; forty is a queue.

Both halves of that comparison repay a moment's arithmetic. Five out of twenty-seven is about one registration in five reaching a person, and that ratio is the thing to watch after the change: if it climbs, the tiers are drawn too narrowly and the escalation path has become the normal path, and if it falls towards nothing the triggers have stopped catching anything. The forty on the other side of the sentence is not hypothetical either. Connections arrive at three to four a week, so a quarter's worth is on the order of forty, which is precisely what a policy gating every connection would have to absorb. The two figures describe the same organisation over the same period under two policies, and the only structural difference between them is which event the human step was attached to.

Everything in the policy maps to something that runs.

RequirementEnforced byIf it were prose only
Every connection registeredRuntime refuses to load a server absent from the manifestUnknown inventory, as before
Scoped read-only credentials by defaultVault issues from tier; no engineer-supplied tokensLong-lived personal tokens in config files
No egress beyond internal rangesNetworkPolicy on the server namespaceServer fetches whatever a payload names
Acting tools not co-located with third-party read serversManifest lint, fails CIThe union problem, undetected
Server surface unchanged since approvalStartup hash check, refuse on mismatchSilent capability growth on upgrade
Registration expires after 180 daysScheduled job disconnects and notifies the ownerAbandoned servers accumulate

That right-hand column is the same test applied row by row, and it is worth reading as one statement. Take the old policy and ask what its text says when six servers are running, then ask what it says when thirty-one are. It says the same thing. The sentence no third-party MCP server may be connected to any agent without prior security review is exactly as true, exactly as prominent and exactly as unenforced in both worlds, because it has no way to observe a file in somebody's home directory and no way to answer one. That is the failure worth naming precisely: the policy was not too strict. Its text was constant across the entire range of the thing it was meant to bound, which is the signature of a rule that is not in the path. The column enumerates what each requirement decays into once nothing reads it, and every entry in it is something that has actually happened somewhere.

A team will propose, reasonably, that injected-instruction detection on tool results be a mandatory control in this policy. The answer from chapter 5, on what such a detector can recognise, is that it is worth running and is not a control, so mandating it as a gate imports its 0.5% precision into every workflow in the company and creates a row in the controls table that a future reader will trust. There is a version of the proposal worth accepting, which is making the detector mandatory for the escalated tier only, in monitoring mode, as a condition of the approval that let that agent hold an acting tool. At five or six such agents the alert volume stays reviewable, the population being watched is the one where a successful payload does something irreversible, and the precision number in the controls table is attached to a specific deployment rather than to the whole company.

What is worth mandating is the telemetry underneath it. Every server-authored content event — tool result, resource read, prompt fetch — must be emitted with the server name, the channel, a source_ref and a content hash, whether or not anything is flagged. That requirement is enforceable, since the platform owns the client library that produces the events, and it is what makes any future detector deployable across the fleet in a day instead of a quarter.

What will break this, and the parts to build now

The failure mode that matters is the same one that killed the old policy: engineers running servers outside the managed runtime. Nothing in the manifest reaches a config file on a laptop, and the honest position is that this is mitigated rather than solved. Three things help, and all three have to be built before the policy is announced rather than after.

Make discovery routine and non-punitive. The fleet check that found 31 servers should run weekly and report to the owning team, not to a compliance dashboard, with the first response being a message offering to migrate the server rather than a ticket. The first time discovery is used to discipline someone, it stops finding anything, because the file is easy to hide.

Run an amnesty at the switchover. Two weeks in which registering an existing local server is one command and carries no consequences, with someone from the platform team pairing on the awkward ones. The 25 unregistered servers are the current inventory, and the point of the new policy is to see them.

Budget for the expiry cliff. Registrations that expire after 180 days all expire in the same week if they were all created during the amnesty, so stagger the initial expiry dates across the window. An expiry mechanism that produces forty simultaneous disconnections on a Monday gets disabled by Tuesday, and a disabled expiry mechanism is how abandoned servers survive.

The cliff is a consequence of the amnesty rather than of the expiry period, and seeing why says how to spread it. A registration expires a fixed interval after it is created, so the distribution of expiry dates is the distribution of creation dates shifted forward by that interval and otherwise unchanged. An amnesty compresses creation into a fortnight, so the expiries arrive compressed into a fortnight half a year later, with nothing on either side of them. Nothing about the length of the interval causes that; the shape was fixed on the day everybody registered. The remedy falls out of the same sentence — assign the initial expiry dates spread across the whole window instead of counting forward from each registration — and after the first cycle the distribution is set by ordinary demand rather than by one fortnight in the organisation's history.

There is a fourth pressure that is harder to plan for. Agents are being created faster than servers, and the escalation triggers are written in terms of tool classes that somebody has to keep current. When a team ships a new tool that moves money, the class list has to gain a row, and the mechanism for that is a review of the tool rather than of the server — which means the manifest lint is only as good as the classification behind it. Review that list quarterly and treat an unclassified tool as acting-class by default, so the failure direction is an unnecessary escalation rather than a missed one.

An inventory, not a boundary

A registry is an inventory and it is not a boundary. Everything that actually contains a compromised or hostile server is in chapters 3 and 4: argument schemas that leave nothing for a payload to aim at, credential scopes, egress policy, and keeping acting tools out of sessions that read third-party text. Registration makes those things applicable by default and applied consistently, and on its own it protects nothing. A team that builds the manifest and skips the tiers has a very good list of the ways they can be attacked.

This shape also does not survive unchanged at four hundred engineers. At that size the auto-merge path needs per-team ownership rather than one platform team, the tier definitions need to differ by business unit, and the fleet check needs to be a platform capability rather than a script somebody runs. The structure is the same and the operating model is not, so the policy should carry a review date rather than pretending to be permanent.

The next server request will arrive this week, and the path is now short enough to follow rather than argue about: register the pair, take the default tier, let the surface hash pin what the server exposes, and check whether the agent on the other end holds anything in the acting class. Where it escalates, the vetting from topic 6.14 answers whether the server is honest, and the questions from chapter 4 answer what an honest one can still deliver. Neither question stops being worth asking; what changes is that asking them is now reserved for the handful of connections where the answer changes what anyone does.

MCP as an injection surface — read the current version at https://daviddominguez.dev/learn/en/mcp-as-an-injection-surface/server-registration-and-escalation-tiers

Back to top