You switched from GPT-4 to Claude. Still broken.

You switched from Claude to Gemini. Still broken.

You rewrote your prompts for a week. Still broken.

The model was never the whole problem.

Agents fail at boundaries. A model can reason well and still receive the wrong context, choose among overlapping tools, or get back output too noisy to use. In a tool-heavy system, the interface around the model becomes part of the product architecture.

MCP standardizes the boundary, not the interface

MCP, or Model Context Protocol, is an open protocol for connecting AI applications to external tools, data, and services. It gives clients a common way to discover capabilities and invoke them through shared contracts.

For tools, that contract includes a name, description, input schema, and optional output metadata. This is valuable infrastructure: clients and servers can evolve independently without inventing a bespoke integration for every pairing.

But a standard boundary is not automatically a good boundary. MCP defines how capabilities are exposed; it does not decide how many capabilities the model should see or how their interfaces should be shaped.

Tool definitions spend the same budget as the task

When a client makes tools visible to a model, their names, descriptions, input schemas, and annotations occupy model-visible context or an equivalent tool-selection budget. That information arrives before the model can use the tool itself.

The exact cost depends on the client. Some clients load a full catalog; others filter tools or discover them on demand. The invariant is simpler: irrelevant tool metadata competes with the user’s instructions, the code, the incident, and the conversation the model is supposed to reason about.

Context is finiteEvery schema competes with the task.
Endpoint mirrorMany definitions loaded
Tool schemasTask + reasoning
Curated surfaceRelevant tools only
ToolsTask + reasoning

Illustrative: the actual cost depends on the client and schema size.

More schema surface also creates more near-neighbor choices. The failure mode is not only “the context window is full.” It is that the signal gets weaker: more plausible names, more overlapping descriptions, and more opportunities to pick the almost-right capability.

Tool sprawl is an interface problem

One tool per API endpoint is a natural first implementation. It is easy to generate, easy to document, and easy to map back to server code. It also makes the model inherit the shape of the API.

Combine a few broad services and the agent may need to distinguish dozens or hundreds of endpoint-shaped names. The model is now doing routing work that deterministic software already knows how to do.

The tempting fix is “just expose fewer tools.” But which capabilities should disappear: issues, pull-request reviews, deployments, secrets, alerts, docs, or messages?

You should not have to delete product capability to repair an interface.

Collapse endpoints into verbs

A small vocabulary of verbs repeats across many resource types. For example, list_issues becomes list(issue), merge_pull_request becomes execute(pull_request.merge), and run_workflow becomes execute(workflow). The long tail is mostly nouns: issue, pull request, branch, workflow, deployment, connector, service, and so on.

Representative toolsSame capability. Smaller model-visible surface.
Endpoint surfaceGrows with the API
  • list_repositories
  • get_repository
  • create_issue
  • close_issue
  • update_issue
  • get_pull_request
  • merge_pull_request
  • run_workflow

One tool per endpoint. The interface grows forever.

Registry surfaceStable vocabulary
  • list
  • get
  • create
  • update
  • delete
  • execute
  • search
  • describe
  • diagnose
  • status

Stable verbs in front. Resource routing behind the registry.

The goal is not to remove capability. It is to move resource knowledge out of the model-visible tool list and into a registry the server can validate, test, and evolve.

Two layers, one request

Consider a developer asking:

find the PR that broke the login page

Layer 1 is the small, stable interface the model sees: verbs such as search, get, list, create, update, delete, execute, describe, diagnose, and status.

Layer 2 is the server registry. It maps a resource and operation to the backing API, injects scope, validates inputs, handles pagination, and shapes the result. It can grow without turning the model’s prompt into an API catalog.

Two-layer dispatchThe model chooses intent. The registry resolves implementation.
Developer asksFind the PR that broke login
Layer 1 / modelsearchChooses one stable verb
Layer 2 / serverRegistryRoutes and validates
  • pull requests
  • commits
  • issues
  • deployments

If the model knows the resource, it can call something like get(pull_request, id=482). If it does not know where the answer lives, search("login page broke") can fan out across pull requests, commits, issues, and deployments.

Either way, the model chooses intent. The registry resolves implementation.

The complexity moved. Good.

A registry does not make complexity disappear. It moves complexity from a probabilistic decision into code, where routing, authorization, validation, and error handling can be tested.

That move creates a new responsibility. Generic verbs can become vague or stringly typed if the registry is weak. A production-quality implementation needs:

  • discoverable resource metadata and schemas;
  • strict validation before execution;
  • compact, consistent result shapes;
  • explicit authorization and confirmation for sensitive actions;
  • useful errors when a resource or operation is unsupported; and
  • traces that show what the model selected and what the registry dispatched.

The interface should be small, not mysterious.

What we learned at Harness

At Harness, our first MCP server grew past 130 active tools. The redesign collapsed that surface to 11 stable tools and moved resource-specific behavior behind registry dispatch.

As of September 2026, the open-source Harness MCP server documents more than 240 resource types behind those 11 tools. The exact count will change. The useful invariant is that capability can grow without forcing the model’s tool vocabulary to grow with it.

The redesign write-up also describes the safety controls that belong on the server side: confirmation for writes, fail-closed deletes, read-only operation, and structured dispatch.

What about servers you do not own?

Start with the cheapest control: scope what the client loads. Disable irrelevant servers or toolsets, and use lazy discovery when the client supports it.

If you cannot redesign or scope the upstream server, put a gateway in front. A gateway can retrieve the capabilities relevant to the request and expose a smaller working set instead of flooding the agent with every possible endpoint.

That gateway is not free infrastructure. It becomes responsible for routing, policy, authentication, observability, and failure behavior. Treat it as a product boundary, not a schema compressor.

A practical design checklist

Before adding another MCP tool, ask:

  1. Does this represent a new user intent, or only another API endpoint?
  2. Can the resource be a typed parameter behind an existing verb?
  3. Can the agent discover the schema only when it needs it?
  4. Is the output compact, structured, and useful for the next reasoning step?
  5. Are authorization and destructive-action confirmation enforced outside the model?
  6. Can you observe selection errors, routing errors, latency, and token cost?
  7. Have you evaluated the server beside other servers, with realistic tasks?

Takeaways

Three things are worth taking home:

  1. Context is a budget. Spend it on relevant capability, not an API catalog.
  2. Design the interface for agent intent, not endpoint parity.
  3. Let the model choose what to do; let deterministic code decide how to route it.

Stop treating a model swap as the first response to an architecture problem. Design your MCP for the agent, not for your API.

Further reading