The reason to build an MCP server is not the reason you think

The reason to build an MCP server is not the reason you think

An MCP server across 7,698 VCF operations bought me four things. Three are nice. The fourth is the only one that matters for agents on real infrastructure.

Up until this week I had been running my lab with agents the hard way. No MCP, just raw APIs.

Every time I asked Claude to do something, it had to work out how. Find the right endpoint. Guess the field names. Send a body, read the validation error, add whatever field it named, send it again. One missing field at a time, because the vSphere API tells you exactly one thing that is wrong per call and nothing about what it wants next.

Then, on the advice of Oren Penso, I had Claude build an MCP server for the whole VCF API. It runs alongside Claude Code on my agents box, registered per-project, covering six appliances and 7,698 operations. The code is on GitHub at NiranEC77/vcf-mcp if you want to see how it is put together or point it at your own lab.

This matters beyond my basement. If you believe agents are going to be doing real operational work in a data centre, and I do, then the question of what an agent needs in order to act safely against infrastructure is the whole ballgame. This is the clearest answer I have found so far.

Four things changed. Three of them are the ones everyone talks about, and they are all real. The fourth is hardly mentioned, and it is the one I would build this for.

1. Access, and this one surprised me

I expected this to be the boring part, because Claude already had credentials. It could reach every one of those operations with curl before the MCP existed, and did, for weeks.

What I had not counted was the cost of how differently each appliance wants to be talked to. SDDC Manager takes a bearer token. NSX has a management-plane API and a policy API that use different object ids for the same transport zone. Avi refuses basic auth entirely and needs a session flow with a CSRF token, a Referer header and an explicit API version header, with an admin credential you have to go and read out of SDDC Manager because it is not the appliance password.

Every one of those is a thing the agent had to rediscover, get wrong once, and work around. The MCP normalising six auth models into one interface removed a whole category of busywork I had stopped noticing because it had always been there.

From an AIOps point of view: your estate is not one API, it is six or sixteen, each with its own auth quirk. Consolidation is real value even before anything clever happens.

2. Speed:

The clearest measurement I have. POST /v1/alb-clusters kept returning exactly one error, ECM_ALB_NSX_ID_EMPTY, with every other field accepted. So Claude guessed spellings: nsxId, nsxtId, nsxtClusterId, nsxClusterId, nsxTClusterId. Then nested forms. Then query parameters. Then it went digging for the actual DTO inside vcf-domain-manager.jar, found it was a Spring Boot fat jar whose classes live in nested jars that a streaming grep cannot see into, and discovered the file was unreadable as that user anyway.

An hour. Then it gave up and told me to use the UI wizard 🙂

The schema, read afterwards as an ordinary file:

AlbControllerClusterSpec required:
  [adminPassword, bundleId, clusterFqdn, clusterName, formFactor, nsxIds]

nsxIds, and it takes an array. Every guess had been a singular value with a slightly different name. The error did not mean “you forgot a string,” it meant “your array is empty.”

An hour against a schema that was readable the entire time.

3. Tokens, and the design decision behind them

The savings are real and they come from one specific choice, which is the part I would take to work.

Those 7,698 operations are exposed as seven tools: search, describe, call, follow the task. One tool per endpoint is the obvious design and it collapses immediately. The tool descriptions alone would exhaust the context window before the first question got asked, and tool selection degrades badly once you are past a few dozen options.

What we did is index the specs once and then we let the agent look things up the way a person would. Cost stays flat as the API grows, and adding an appliance means adding a spec file rather than adding tools.

In my vcf-mcp architecture: Claude Code talks to one MCP server exposing seven tools over indexed OpenAPI specs, which fronts six appliances

The mechanism behind the token savings is worth being precise about, because it is not what it looks like. The agent did not become more concise. It stopped burning entire context windows on failed guesses and one-field-at-a-time error messages. The tokens were never going into the work. They were going into the flailing.

From an AIops point of view: if you are planning to point an agent at a real estate, the tool-per-endpoint design will not survive contact with a real API surface. Index it.

4. Correctness, and this is the whole point

The three above are efficiency. This one is a different category, and it is why I would build an MCP server even if it made everything slower.

Working an unfamiliar API without a schema looks like this: send a body, get told one field is missing, add it, send again. That loop works and that is exactly what makes it dangerous. It is fast and bounded and it converges, so every call gets a bit further than the last and it feels like progress the whole way through.

And it can only ever produce one kind of evidence: this call was accepted. It can never produce the other kind: this call was correct.

Here is what that cost me, or nearly did.

Claude was enabling the Kubernetes Supervisor, iterating against POST /namespace-management/clusters/{cluster}?action=enable. Empty body, read the error, fill the field it named, repeat. It was working. Each call got further. It was converging.

Then the schema:

network_provider possible values:
  - NSXT_CONTAINER_PLUGIN
  - VSPHERE_NETWORK

No VPC option. That is the vSphere 7.0-era API, and it structurally cannot express VPC mode. My lab runs NSX VPC networking, so that endpoint could never have produced what I needed.

But it would have kept accepting input right to the end. Claude would have supplied all seven required fields, received a success, and built a Supervisor on the wrong network model. That is the exact failure class that has already cost this lab two full teardowns.

Nothing would have errored. It would have looked like progress the entire way.

The correct operation is POST /namespace-management/supervisors?action=enable_on_zones, which does support network_type: NSX_VPC. Claude had actually probed /namespace-management/supervisors earlier and gotten a 404, because it omitted the ?action= verb.

Guessing cannot tell you that you are guessing at the wrong thing. A legacy endpoint that happily accepts a well-formed body is indistinguishable, from inside the loop, from the correct one. The deciding fact was a single enum buried in a description string.

And this is the part worth the most. The MCP did not catch this by being smarter. It caught it because it changed the order of operations. Without a schema the loop is call, read the error, adjust, call again, and every step of that loop is downstream of a choice nobody ever examined: which endpoint are we even talking to. With the index, the loop starts one step earlier. Search for the intent, read the schema of what comes back, then call. That describe step is a checkpoint that simply did not exist before, and it costs one round trip. The enum that saved me a teardown was sitting in the output of a command that, in the old workflow, there was no reason to ever run.

Guess loop versus index loop: without a schema you pick an endpoint nobody examined and loop on one-field errors, with an index you search, describe, and catch the wrong endpoint before any call is made

There was a smaller one the same day, same shape. The MCP found that a vSphere zone created the day before had no cluster associated with it. Claude had tried to finish that, guessed /zones/{zone}/clusters and /zones/cluster/{cluster}?action=associate, gotten 404s, and assumed zone creation was enough. The real path is /zones/cluster/{zone}/associations with a body that is a bare array of cluster ids. Supervisor enablement would have failed without it, and nothing had flagged it.

one blocker solved, one wrong path prevented, one silent prior error surfaced. That is not a speed improvement.

From AIOps point of view: an agent operating your infrastructure with only an endpoint and an error message can verify that it is making progress. It cannot verify that it is making progress toward the right thing. In a lab that costs you a rebuild. In production that is a change ticket that succeeded and left you in a state nobody designed.

When you actually want one

When the surface is too large to describe. 7,698 operations is not something you put in a prompt. If your API is a dozen endpoints your agent already knows cold, you are building plumbing for nothing.

When the model has no prior. Public, popular APIs are already in the training data and the agent has a decent instinct. Vendor APIs, internal APIs, anything behind a support portal: no instinct at all, and no instinct means guessing.

When a wrong-but-accepted call is expensive. This is the real test. If the worst case is a 400 and a retry, guessing is fine and cheap. If the worst case is a Supervisor on the wrong network model and a day of teardown, you want the schema. The value scales with the cost of being confidently wrong, not with the size of the API.

That last one is the sizing rule for AIOps generally. Read-only telemetry work does not need this. Anything that changes state does.

When it is not the answer

I am not going to pretend this thing is an oracle. Three limits, all hit within a day.

When the schema and the running appliance disagree. enable_on_zones rejected zones as missing while it was plainly present in the body. The OpenAPI file declares an array of strings. Claude tried strings, objects, a singular key, an alternate field name, a query parameter. It then sent the identical payload with plain urllib, bypassing the MCP entirely, and got the identical error, which at least proved the server was faithful and the appliance itself was refusing. Twelve calls in, it stopped.

The MCP made the schema legible. It could not make the schema true. And when spec and appliance disagree, probing cannot tell you which one is wrong, because every error you get is internally consistent and none of them is informative.

When the answer is in documentation, not a schema. The way out of that one was reading the actual docs, which said plainly that enable_on_compute_cluster is the operation for a single cluster and takes a singular optional zone rather than an array. The problem dissolved rather than getting solved. A schema tells you what fields exist. It does not tell you which operation you are supposed to be using

Sometimes what you need is a snapshot, not a specification. This is the one I would most want people to take away.

The most valuable debugging artifact in my lab repo is not the MCP server. It is a JSON dump of my Avi load balancer’s configuration, taken while it was working.

Twice in two days that file answered a question no schema could. Avi complained that the Service Engine management network was configured for DHCP, and my instinct was to go add a DHCP range to my router. The snapshot showed the working estate had dhcp_enabled: false and an Avi static pool instead, handing out addresses itself. It stopped me from vandalising a deliberate design.

A schema tells you the shape of the world. A snapshot tells you which shape you had. Different questions, and you want both. If you are building toward agentic operations, snapshot your systems while they are healthy. That artefact is worth more than any amount of documentation when something breaks at 2am.

And the plumbing has plumbing problems. The Avi target started returning CSRF token missing or incorrect under sustained use and we fell back to a direct client. The MCP approval dialog turned out to be a second gate beyond the workspace-trust one, and it hangs a non-interactive session identically. The approval does not persist either, so it prompts on every launch. Budget for that.

The short version

The speed is real, the token savings are real, and the consolidation across six auth models is real. Take all three, they are free once you have built the thing.

But none of them is the reason. The reason is that an MCP server is the only artifact in the loop that can tell your agent the difference between a call that worked and a call that was right.

Build one when your agent works against an API it has no prior for, too large to describe in a prompt, where a call that gets accepted while being wrong costs you something real. Build it as a searchable index rather than a tool per endpoint, or it will collapse under its own context cost.

And do not expect it to save you when the documentation is right and the schema is stale, or when the question you actually have is not “what is possible here” but “what was true when this last worked.”

The server itself is here: github.com/NiranEC77/vcf-mcp.

I’d rather learn this in a lab than in production. That’s what the lab is for.