The Agent Is a New Kind of Client
Why a multi-protocol gateway built for event-driven systems turned out to be the right control point for AI agents, and how Zilla evolved to govern them.



Modern application architecture has always been a secure data distribution problem. Get the right data to the right consumer, with the right identity attached, fast enough to feel live and durable enough that nothing important gets lost. For two decades the consumer was a browser, then a mobile client, then a microservice, then an IoT device. Each one changed the protocols and stressed different parts of the architecture, but the underlying problem stayed the same.
The AI agent is the next consumer. It is the first one that decides what to do at runtime.
A browser follows a path someone designed. A microservice calls what its code names. An agent decides at inference time which data it needs, which systems to reach and which actions to take, often on behalf of a person whose identity must flow with each call. The same prompt at 9:00 and 9:01 can produce different sequences of tool calls. A traditional client has a known call graph and a reviewed blast radius. An agent's blast radius is whatever it can reach.
The industry is now putting this consumer in front of every system of record it has. The question is what infrastructure has to look like to make that safe. I do not think the answer is a slightly modified API gateway. This post explains why Zilla, a gateway we built for event-driven systems, turned out to be the right starting point, and how it has evolved for agents.
We built the control point before the use case arrived
Every shift in the enterprise client created a new infrastructure layer. Browsers drove web infrastructure. Mobile drove APIs. Microservices created service meshes. Streaming created its own requirements around real-time data.
Zilla came out of that last shift. Event-driven architectures kept colliding with request-response clients, and someone had to bridge them without writing another microservice every time. So we built a multi-protocol mediation engine: HTTP to Kafka, MQTT to Kafka, gRPC to Kafka, SSE to Kafka. Bidirectional, schema-aware, identity-aware, declared in YAML, with no application code. Each protocol is a stream type, and bindings compose them into a pipeline.
That design gave Zilla three properties. It sits inline, directly between clients and enterprise systems. It is protocol-native, so it understands the operation rather than inspecting generic traffic. And it is client-independent: nothing runs in the client, so there is no SDK to adopt and no framework to depend on.
Then agents arrived. Architecturally, an agent was just another client and MCP just another protocol. But those three properties are exactly what governing an agent requires. A control point that sits outside the agent, understands what it is asking for and enforces policy before the request reaches a business system is not a nice-to-have when the client decides its own call graph. It is the only place governance can live once there are thousands of agents built by different teams on different frameworks. The agent cannot be its own enforcement boundary.
MCP became a stream type, not a new product
When MCP arrived, Zilla did not need a new product. It needed a new stream type. The engine already treated http, kafka, grpc, mqtt, ws, tls and tcp as first-class streams. We added mcp as a peer, with three bindings at its core:
Because MCP is a stream like any other, everything Zilla already did applies to it: TLS and mTLS on the way in, guards for auth, catalogs and models for schemas, telemetry on every binding. A minimal gateway fronting two MCP servers is a few lines of zilla.yaml:
north_mcp_proxy:
type: mcp
kind: proxy
options:
cache:
store: cache
ttl: PT5M
routes:
- exit: bluesky_mcp_client
when:
- toolkit: bluesky
- exit: payments_mcp_client
when:
- toolkit: payments
The mcp.proxy example runs a full gateway with two upstreams, toolkit routing, a five-minute listing cache and Prometheus metrics from a single docker compose up.
After we shipped native MCP, the Zilla project started reaching developers well outside our Kafka audience. The platform had not changed. The workload had.
Tools from what you already have
Now consider what an agent has to reach into. Systems of record built over thirty years speak everything those years produced. Customer data sits behind a REST API. Order events flow through Kafka. Schemas live in a registry. Connectors move data between systems. An agent that helps a relationship manager research a client position may need four of those in one request.
The dominant pattern is one MCP server per system: a Python service wrapping the REST API, another wrapping the Kafka consumer, another for the registry. Each is a process to deploy, monitor, secure and version. Within months you have a wrapper farm, collectively responsible for one agent's view of the enterprise. That is not pervasive access. It is a maintenance liability.
Zilla removes the wrapper. A set of backend bindings maps MCP onto systems the engine already speaks, with no MCP server upstream:
An OpenAPI read can map to an MCP resource instead of a tool, static or templated, so not every GET becomes a tool the model has to reason about. Overlays patch a third-party OpenAPI document with security requirements, servers or MCP metadata without forking it. And because the gateway calls the backend itself, it sees the real HTTP status or Kafka offset rather than a wrapper's summary of what happened.
The agent does not know, and should not know, that one tool came from REST and another from a topic. They are all MCP tools, governed identically.
One endpoint for every upstream
An agent should not need to know how many servers sit behind it. Zilla presents a single Streamable HTTP endpoint as a virtual server. It merges tool, prompt and resource listings from every upstream into one response, and routes each call to the right upstream by the toolkit prefix in the tool's name. Adding an upstream means adding a route.
Listings are cached at the gateway with a TTL, so upstream servers are queried only when the cache expires, not on every tools/list.
Scale creates a second problem: context. An agent offered hundreds of tool definitions spends its context window reading them, and gets worse at choosing. Zilla splits tools into eager and cold. Eager tools are listed by default. Cold tools stay out of the listing but remain reachable: the agent can search for one by keyword, resolve its schema and call it, without that tool ever crowding the default list.
Identity is enforced at the gateway
Pervasive access without identity is a different kind of liability. Most gateways validate the agent's token on the way in, then call the downstream system with whatever credential they have configured, usually a service account. That works for the demo. It does not work in production.
Consider an agent helping a relationship manager at a bank look up a client portfolio. She has access to her own client book and nothing else. If the agent calls the portfolio API with the platform team's service account, the audit log shows the platform team, the bank's row-level security is bypassed, and the next manager's call looks identical. The gateway has lost the user.
Zilla handles identity on both sides of the gateway:
- Inbound. A JWT bearer guard validates the agent's token against a JWKS endpoint before any tool call reaches an upstream. TLS and mutual TLS secure the connection itself.
- Role-based tool access. One policy at the gateway gates a whole toolkit, a single tool inside it, or both, instead of auth logic duplicated in every upstream.
- Outbound. Token exchange swaps the agent's inbound token for the credential each upstream expects, so the downstream system sees the user and the agent never holds that credential. Client credentials cover upstreams that need the gateway to authenticate as itself.
Some decisions need a person. MCP elicitation lets a tool call pause and ask the human behind the agent, and Zilla relays it rather than requiring a callback service. In form mode, an upstream's request for a missing field or a confirmation passes back to the client unmodified, in the same round trip. In URL mode, an out-of-band step such as an OAuth consent screen is relayed, and the call resumes when the user finishes it in a browser.
Guardrails reuse data governance
Pattern-based guardrails operate on unstructured text. They cannot tell that one JSON field is sensitive and another is metadata. Tool calls are not unstructured. Their arguments and results have schemas, and Zilla has governed schema-bound data on Kafka for years.
So guardrails in Zilla are not a bespoke filtering layer. They reuse the same catalog and model primitives that govern Kafka message data. A catalog says where a schema lives: inline in zilla.yaml, on the local filesystem or in a remote schema registry. A model says what a payload is checked against, from primitives like string and int32 to Avro, JSON Schema and Protobuf.
- Validate rejects a tool call or upstream response that does not conform, before it reaches the upstream or the agent. The
mcp · proxybinding validatestools/callarguments;mcp-http · proxyvalidates both a tool's arguments and the HTTP response. - Transform changes a payload's representation, JSON to Avro or Protobuf and back, so the agent and the backend never have to agree on a wire format.
- Semantic models cover free text that no schema can constrain. A configured embedding, local GloVe vectors or a hosted model from OpenAI, AWS Bedrock or IBM watsonx.ai, rejects a tool call or response by meaning, using a cosine-similarity threshold.
The data plane is not a database
Most MCP gateways are stateful applications. You run a database and a cache next to the proxy, back them up, fail them over and debug them during outages. Streaming infrastructure succeeded by inverting that assumption: the broker stopped being a database, and that is what let it scale.
Zilla takes the same position. It reads its configuration declaratively and executes against it without accumulating per-request state. Tokens are exchanged at request time, not stored. The listing cache and auth state live in a store: in memory for a single instance, or shared across replicas through Redis or Hazelcast, so a load balancer needs no sticky sessions.
Telemetry leaves as events, not rows. Every MCP method gets counters and histograms, dimensioned by tool and outcome, exported through Prometheus or OTLP into the observability stack you already run. Prompts, tool payloads and results stay inside the boundary where the gateway runs.
The result is one runtime for MCP, HTTP and Kafka, from one configuration file, instead of a separate service, database and cache per protocol.
What comes next: from access to authority
Tools are one leg of what an agent does. In a single request, an agent calls a model, the model asks for a tool, the agent calls the tool, and the model may delegate to another agent that makes its own model and tool calls. Traffic crosses protocol boundaries four to ten times. Most of the market treats MCP, LLM and A2A as three products with three gateways, three identity models and three telemetry pipelines, and the audit trail fragments along the seams. We made the same mistake with API gateways and event brokers a decade ago. Protocol divisions do not justify product divisions. Model and agent traffic belong in the same data plane as tools, and that is where Zilla is going.
One data plane makes a harder question answerable. Identity tells you who an agent is acting for and which systems that person can reach. It does not tell you what the agent may do for this task. When an agent settles an insurance claim, IAM knows who is involved, the agent framework knows what it attempted, the claims system knows what changed and the payment system knows what was paid. None of them can say whether this payment, for this task, on this person's behalf, was within the limits it should have been. Access is not authority.
Answering that requires three things the category has not solved:
- Task-scoped authority. A credential bound to one call, one task and one limit, not to a session or a service account.
- Recorded decisions. When a person approves an action, a record of what they were shown and what then executed, not just that someone clicked.
- Proof. One record from the person's request through every model turn, tool call and delegation to the business outcome, rather than four logs correlated by hand.
All three depend on a control point that is inline, protocol-native and independent of the agent. That is the control point Zilla already is.
A decade of mediation, now pointed at agents
Zilla is not a Kafka proxy with AI features, and not an HTTP proxy that learned to speak MCP. It is a multi-protocol mediation engine that already spoke the languages enterprise systems use, with MCP added as a peer. Tools come from specs and topics you already have. Identity flows through the gateway to the systems behind it. Guardrails are the same schema governance that protects your event streams. The data plane is configuration, not a database.
The agent is a new kind of client. It deserves infrastructure built for what it is, sitting outside it, on the path to the systems it acts on.
Related Resources


Ready to Get Started?
Get started on your own or request a demo with one of our data management experts.
Explore pricing
Straightforward, usage-based pricing with no per-connection surprises — start free and scale when you are ready.
Join the Community
Trade notes with the engineers running Zilla in production, and get help from the team in Slack or Discord.





