Skip to content

9 min read

Five servers in a quarter is a symptom, not a scale problem

On this page

The handover doc listed five MCP servers, all built in the previous quarter, all in production, all consumed by the same four internal agents.

text
mcp-pg-metrics        1 method: run_weekly_active(product, from, to)
                      wraps one parameterised SQL query. 210 LOC.
mcp-review-conventions 1 method: get_conventions() -> string
                      returns a 1,900-token markdown document. 90 LOC.
mcp-kube-ops          1 method: kubectl(args: string)
                      shells out. kubeconfig with cluster-admin. 140 LOC.
mcp-slack-post        3 methods: post, thread_reply, react. 380 LOC.
mcp-github-pr         4 methods: list, diff, comment, checks. 520 LOC.

deploy time, each: ~22 min (image build, staging soak, prod rollout)
mcp-pg-metrics: 2 schema versions behind two of its four consumers
mcp-review-conventions: last content update 4 months ago

The team's own framing was that this had become a maintenance load and they wanted to consolidate. The obvious move — one well-maintained server behind which all the capabilities live — would take the operational surface from five deploys to one and put every consumer on a single version. It is the answer that fits in a slide, and it treats the count as the problem when the count is a consequence.

Three of those five had no business being servers, and one of the three is dangerous for reasons that consolidation makes worse rather than better. Getting that right first changes what the quarter should be spent on.

Classifying what each one should have been

Run the gap test from chapter 1 over each, and the answers do not require knowing anything about the code.

ServerGap it addressesWhat it should beWhat the server bought
mcp-review-conventionsKnowledge: standing procedural text, nothing leaves the processSkill, loaded when the request is about code reviewA deploy pipeline, a token and a version number for a markdown file
mcp-pg-metricsAction, but four consumers on one release train in one languageShared client library, registered as an in-process toolSharing that a package would have given, plus an availability dependency
mcp-kube-opsAction, credential the agent hosts must not holdA server — with a bounded schema, which this does not haveA privileged daemon with an unbounded action surface

mcp-review-conventions is the clearest and the most instructive, because it is a runtime whose entire job is to return a constant. Every turn, the four agents carry its schema; when called, it opens a connection, authenticates, and returns 1,900 tokens of markdown that has not changed in four months. The version-skew risk is real and pointless, the availability risk is real and pointless, and the same text as a skill would cost about 30 resident tokens for its description and load only in the sessions that are about code review. The 22-minute deploy to change a sentence is the reason it has not been updated since June.

mcp-pg-metrics looks more defensible because it has four consumers, and chapter 7's analysis of the pricing lookup applies directly: four consumers make duplication unacceptable and duplication has two cures. All four agents are Python services on the same release train, so the library cure is available, and the server has taken on an availability number and a skew surface — it is already two schema versions ahead of two of its consumers — to deliver sharing a package version would have delivered without either.

mcp-kube-ops is the one that inverts. It should be a server, and for a reason the team never stated: the agents run in a tier that must not hold a kubeconfig, and a server that authenticates callers and performs cluster reads on their behalf is exactly the mechanism for removing a credential from a host that should not have it. What is wrong with it is the schema. A single method taking a free-text args string means the action surface is everything kubectl can do, executed with cluster-admin, initiated by a model reading text that may have come from a ticket, a log line or a pull request comment. The narrowing that would make it safe — list_pods(namespace), get_pod_logs(namespace, pod, since_seconds), describe_deployment(namespace, name) — is a schema change inside a server that already exists, which is a week of work and no new infrastructure.

Why consolidation makes the worst of these worse

Merging five servers into one keeps every capability and changes two properties, both in the wrong direction.

The permissions of the merged server are the union of the permissions of everything it fronts. Today mcp-pg-metrics holds a read-only Postgres role and nothing else, and mcp-slack-post holds a bot token and nothing else. A single server fronting all five holds the Postgres role, the Slack token, the GitHub token and the cluster-admin kubeconfig in one process, and any path that reaches one method reaches a process holding all four credentials.

Flowchart: Ticket text (attacker-controlled) → Support agent; Support agent → Merged server pg role + slack token github token + cluster-admin (read_ticket); Support agent → Merged server pg role + slack token github token + cluster-admin (model acts on injected text); Merged server pg role + slack token github token + cluster-admin → kubectl exec; Merged server pg role + slack token github token + cluster-admin → GitHub comment; Merged server pg role + slack token github token + cluster-admin → Slack post; Merged server pg role + slack token github token + cluster-admin → Postgres.

The path in that diagram exists today only if the support agent is given the kubectl tool, and the merge is what makes giving it one tool equivalent to giving it all of them. Chapter 5's point about what a reply can cause is the same point seen from the other side: consolidating capabilities behind a single identity means the blast radius of any one of them is the union of all of them.

The second property is availability. Five servers at 99.9% each, consumed selectively, means an agent that uses two of them is exposed to two. One server carrying all five capabilities means every agent depends on it for everything, so a single bad deploy takes out ticket lookup, metrics, Slack and cluster reads at once for four products. The blast radius of a bad change went from one capability to all of them, which is the opposite of what the consolidation was meant to achieve.

And the merge does not address the actual defect in three of the five. The conventions document is still a runtime. The metrics query is still a network hop between two things in the same release train. The kubectl surface is still unbounded, now with more credentials in the process next to it.

The sentence that explains all five

The team's stated reason was that servers are how they share capabilities across their four agents. That sentence is worth taking at face value, because it is accurate about their environment and wrong about the general case, and the gap between those two is the thing to fix.

In their org, a server genuinely was the only mechanism that worked for sharing. There was no internal package index the agent repos could pull from, so a library was not a real option; there was no directory or distribution path for skills, so a skill would have meant copying a markdown file into four repositories with no way to keep them in step; and there was no shared configuration layer. Every alternative to a server required infrastructure that did not exist, and a server required infrastructure that did — a base image, a deploy pipeline and an auth pattern the platform group had already blessed. Given that, five servers is a rational response to the incentives, which is why it is a symptom.

There is a second reason, less flattering and harder to remove. A server is legible as engineering work. It has a repository, a deploy, a dashboard and a version number, and it appears in a quarterly review as a thing that was built. A skill is a markdown file in a directory, and moving standing knowledge into one reads as documentation rather than as a system. Teams under pressure to show output will reach for the mechanism that produces an artefact, and chapter 7's absence is the enabling condition: with no written thresholds, each individual proposal is arguable on its own terms, and the heaviest option is the one that sounds most like engineering.

What one quarter should buy

The temptation with an inherited mess is to fix all of it. Most of the value here is in two of the five, and two others should be left alone deliberately rather than by omission.

ItemActionEffortWhat it buys
mcp-review-conventionsConvert to a skill, add owner and reviewed frontmatter, delete the server3 daysRemoves a deploy, a token and a version surface; makes editing the text a PR rather than a release
mcp-kube-opsReplace the args method with three read-only methods on an allow-list; keep the server1 weekBounds the action surface; keeps the credential off the agent hosts
mcp-pg-metricsPublish the query as a package, register in-process in all four agents, retire the server3 weeks (blocked on a package index)Removes an availability dependency and the existing two-version skew
mcp-slack-postLeave alone—Works, narrow schema, one owner; a rewrite buys nothing
mcp-github-prLeave alone—Genuine multi-consumer integration with a credential story
Decision docWrite the five-column matrix and the thresholds, and add "which gap is this" to the design-review template2 daysStops the sixth server
Package indexFund it, or accept servers as the sharing mechanismNot this team's to fundRemoves the incentive that produced all five

The two "leave alone" rows are decisions, and saying so in the handover matters. mcp-slack-post and mcp-github-pr are the two that pass the test from chapter 3 — external actions, multiple consumers, credentials that belong outside the agent process, narrow schemas — and rewriting working software because its neighbours were misclassified is churn with a real cost and no benefit.

The last two rows are the ones that determine whether the quarter holds. Converting the conventions server to a skill is a three-day win that reverses in a month if the next capability request goes through the same review with the same absent criteria. The package index is not this team's to build, and if it does not get funded, the honest position is that servers remain the org's only sharing mechanism and the metrics migration should be deferred rather than attempted against the grain.

What this is not an argument for

None of this is a case against MCP servers. Two of the five are correct, one more is correct once its schema is bounded, and chapter 3's three-consumers-three-teams-one-operator test admits real cases regularly. The failure here is that a mechanism with a deploy target, an authentication story and a version number was applied to a markdown file and to a SQL query, and the count of five is only how the misclassification became visible.

It is also not a claim that the team was careless. They chose the only sharing mechanism their environment offered, in the absence of any written criteria, under a review process that never asked which of the three gaps a request was. Every one of those conditions is fixable, and two of them are fixable in a fortnight.

The material of this topic is now complete: the three gaps and what each mechanism hands the model, residency against routing, ownership and operation, the true cost of delegation, the asymmetry between loud and silent failure, the five-column matrix, and the thresholds that make a recommendation defensible. What remains is to run it over something real. Take the capability inventory of an agent you actually own, classify each entry by gap, fill the five columns, and mark the ones where the mechanism does not match the gap — then write the threshold that would flip each of the mismatches, because that is the sentence that makes the next decision take eleven minutes instead of six weeks.

The extensibility landscape — read the current version at https://daviddominguez.dev/learn/en/extensibility-landscape/five-servers-in-a-quarter-is-a-symptom-not-a-scale-problem

Back to top