Problem
Every customer integration adds a tool: their CRM, their calendar, their ticket system, their stock lookup. Written ad hoc, each one arrives with its own schema conventions, its own auth handling and its own error semantics, and the agent runtime slowly turns into an integration monolith.
The Model Context Protocol offers a standard shape for that boundary. The question was whether adopting it actually removes work in a multi-tenant setting, or just relocates it.
Hypothesis
Expressing integrations as MCP servers reduces per-integration runtime work to configuration. Authorisation, tenancy and rate limiting remain the platform's responsibility and must sit in front of the protocol, not inside it.
Architecture
Agent runtime
- loop
- model calls
- tool selection
Tool broker (ours)
- tenant scoping
- authorisation
- rate limits
- audit log
- schema pinning
MCP transport
- tool discovery
- JSON schemas
- invocation
- errors
MCP servers
- CRM
- calendar
- ticketing
- internal APIs
The broker layer is the part that is ours and cannot be delegated. An agent serving tenant A must not be able to reach tenant B's calendar server, and the protocol does not have an opinion about that - correctly, since it is an application concern.
Implementation
Tools are resolved per session rather than per deployment. When a conversation starts, the broker assembles the tool list from the tenant's enabled integrations, filters it by the agent's configured scope, and pins each tool schema to a version.
// The agent never sees the full catalogue. It sees the intersection of
// what the tenant has connected and what this agent is scoped to use.
async function toolsFor(session: Session): Promise<Tool[]> {
const connected = await registry.serversFor(session.tenantId)
const tools: Tool[] = []
for (const server of connected) {
for (const t of await server.listTools()) {
if (!session.agent.scopes.allows(server.id, t.name)) continue
tools.push({
// Namespaced: two servers may both offer "search".
name: `${server.id}.${t.name}`,
schema: pin(t.inputSchema, server.schemaVersion),
invoke: (args) => broker.call(session, server, t, args),
})
}
}
return tools
}Schema pinning
Discovery is dynamic; behaviour must not be. If a server changes a tool schema between sessions, agent instructions written against the old shape silently stop working. We pin the schema version per tenant and treat an upstream change as a migration with a diff, not as an automatic upgrade.
Error semantics
The distinction that mattered most was between "the tool failed" and "the tool worked and the answer is no". A calendar with no free slot is not an error, but naive wrappers report it as one, and the agent then retries a call that will never succeed. We normalise this at the broker: transport failures are errors, empty results are results.
Result
The hypothesis broadly held. With a broker in place, adding an integration became configuration plus a server, and the runtime stopped growing per customer - which was the actual goal.
What MCP did not remove: authorisation, tenancy, rate limiting, audit logging, schema governance and cost control. That is not a criticism of the protocol - those are application concerns and it is right that they sit outside it. But a plan that assumes adopting MCP replaces an integration layer will be wrong about roughly half the work.
One practical surprise: tool *description* quality dominates tool selection accuracy far more than the model does. Two servers exposing the same capability with differently-worded descriptions produced noticeably different agent behaviour. Descriptions are prompt engineering wearing an API costume.
Limitations
What this experiment does not establish. Listed because an experiment without limitations is an advertisement.
- Exploratory. This runs in an internal environment against a small number of servers, not as a production integration surface.
- We have not stress-tested large tool catalogues. Selection accuracy with dozens of similar tools is an open question we expect to be the real scaling limit.
- Latency was not a focus. Tool discovery per session is fine at our sizes and would need caching well before it is not.
- The protocol continues to evolve; anything written here is a snapshot of our reading of it in mid-2026 and should be checked against the current specification.
Next steps
- Test selection accuracy as the catalogue grows, and find where a flat tool list stops working and needs hierarchical routing.
- Automate schema-drift detection so an upstream change surfaces as a diff before it surfaces as a broken agent.
- Evaluate whether the broker should expose itself as an MCP server in turn, so the same boundary composes.