Request an evaluation(opens in a new tab)

November 11, 2026 / 9 min read

A Company Card for Your Agents: Cost, Access and Observability with an AI Gateway

Agents reach models and tools through one gateway, which lets three calls through and refuses one.

A coding agent estimated that a run cost $0.044. The model was free.

That contradiction points at a bigger problem. A software factory runs many AI agents on tickets, and each brings its own credentials, its own way of calling models and tools, and its own report of what happened. You lose control over what they may do and visibility into what they did.

A gateway is meant to fix that. It sits between the agents and everything they call, and routes each request while it applies access rules, limits and logging. Think of it as a company card for each agent: a spending limit, a list of what it may use, and a statement of every charge. How much does it actually give you? We ran three coding agents through four gateways to find out.

Why agents need a control and observability layer

A coding agent works in a loop: it asks a model what to do, runs a command or calls a tool, and feeds the result back. Each step can cost money or have side effects. Agents need the same foundations as any application, such as access control, logging and cost attribution, but they decide for themselves which models and tools to use.

An LLM gateway sits between agents and model providers, and an MCP gateway does the same for tools. MCP, the Model Context Protocol, is a standard way for agents to reach tools such as Jira. OpenTelemetry (OTel) is a standard way to collect telemetry, and its protocol, OTLP, carries that data between systems.

Think of this layer as a front door. Agents hold a limited key, the gateway holds the real credentials, and every request passes through one place that can apply rules and record what happened. That door has three jobs, and this post follows them:

  • Control what agents can access
  • Understand what agents are doing
  • Decide where agent data goes
Agents send model calls through an LLM gateway and tool calls through an MCP gateway. Agents and gateways send telemetry to a collector, which feeds an observability backend.
Conceptual architecture, not the exact setup of every test.

Inside the experiment

We recorded 61 runs, nine of them ending in an error, some on purpose in budget and model-block tests. Nearly all used one small task: read a file and explain a behaviour, which kept the workload comparable.

  • Agents: Claude Code 2.1.318, Codex 0.164.0, OpenCode 2.2.1
  • Gateways: Bifrost 2.4.0 and agentgateway 1.7.0 in Docker, Cloudflare AI Gateway, Vercel AI Gateway
  • Models: Claude Haiku 5.5, GPT-6 Luna, free Nemotron through Vercel, Mistral Small and Qwen on Scaleway
  • Tools: MCP tests used a fake Jira server, with a scripted client and one Claude Code run
  • Telemetry: OTel Collector into Langfuse Cloud

Scope: a small sample, and not every agent ran on every gateway. We tested the control layer, not model output, and four of many gateways. Others, such as LiteLLM, Portkey and Kong, may behave differently.

Four gateways, seven real-world requirements

A gateway earns its place when something changes, so we compared the changes a platform team actually makes.

Change you need to makeBifrostagentgatewayCloudflareVercel
Add Claude Code or CodexWorksWorksWorksPaid credits only
Trust the costExact on ClaudeExact on ClaudeVerify firstVerify first
Cap a team's spendStops with 402, can overshootStops with 429, can overshootDocs: best-effort limits by model, provider or metadataDocs: budgets per team, project, key or member
Restrict a key's modelsBlocked with 400, unclear errorBlocked with 403, clear errorNot foundDocs: team-level allowlist only
Swap to an open-weight modelWorksWorksPartly, agent failed on call 2Works
Control one MCP toolHidden and refusedHidden and refusedDocs: MCP portals can turn off single toolsDocs: not in AI Gateway
Self-host itYesYesNoNo

Cells starting with "Docs:" come from vendor documentation, which we did not run. "Not found" means we found nothing there. Every other cell is a result from our runs or a product fact.

Putting limits on what agents can do

A budget is a stop, not a ceiling

The gateways we tested checked recorded spend before a call and charged it afterwards, so the call that crossed the limit still ran. Cloudflare's and Vercel's docs describe the same pattern, which we did not test. A gateway could instead reserve the worst case up front, since input tokens are known and max_tokens caps the output, then release the rest, like a card hold. The price is caution: a key near its limit may refuse calls it could have afforded.

With a limit of 0.001 dollars, Bifrost ended at 0.0024 dollars and agentgateway at 0.0011 dollars.
Final spend against a deliberately tiny test limit. The overshoot was one call.

A software factory makes this worse, because it runs many agents at once and all of them pass the check before any finishes. We did not run that case, but Cloudflare's docs describe it, and a reservation would close it too.

A factory also needs budgets in layers:

Cap perWhat it limitsWhy
Agent runOne ticket's workOne stuck ticket cannot drain the rest
ProjectAll runs for one projectKeeps each project inside its share
OrganizationAll agent spendA hard ceiling on the total

A gateway can enforce these only if every request says which run and project it belongs to, for example through a separate key or metadata per run.

Test your own clients before you rely on a budget. They react differently to a refusal:

  • Claude Code stopped at once on both gateways.
  • Codex retried Bifrost's 402 five times in about seven seconds, then gave up.
  • Codex stalled on agentgateway's 429 until our five-minute timeout, in both of two runs.

From model access to tool access: MCP gateways

Agents also act on external systems through tools, and that is a separate security concern: a bad model call wastes money, a bad tool call can delete a project. An MCP gateway extends the front door to tools. The agent holds a limited key, the gateway decides which tools that key can see and call, and the real tool credential stays at the gateway.

To test it, we gave two keys different rights on a fake Jira server behind each gateway. The server rejects any call without its own token, and the client never holds it.

A dev key and an admin key both go through the MCP gateway, which adds the Jira token. The dev key can search but cannot see or call delete. The admin key can do both.
One tool, two keys. The dev key never sees the delete tool, and a direct call to it is refused.

Below is the real output through Bifrost, trimmed. The same test through agentgateway ended the same way.

$ npm run mcp -- bifrost
no key: 401

dev
  tools visible: fake_jira-jira_create_issue, fake_jira-jira_search_issues
  search: ALLOWED: jira_search_issues ran on fake Jira
  direct delete: REFUSED (200): Tool execution failed: MCP tool
    'fake_jira-jira_delete_project' is not allowed for virtual key

admin
  tools visible: fake_jira-jira_create_issue, fake_jira-jira_delete_project,
    fake_jira-jira_search_issues
  direct delete: ALLOWED: jira_delete_project ran on fake Jira

What the run shows:

  • The tool is hidden. The dev key never sees the delete tool.
  • A direct call is refused. In REFUSED (200), the HTTP request succeeded, and the gateway rejected the tool call inside the MCP response.
  • Credentials stay at the gateway. The client never holds the Jira token.
  • A real agent sees the same. Claude Code on the dev key, through agentgateway, could not see the delete tool and said so.

If you self-host, keep the admin port private. In our setup one gateway's admin interface answered without authentication, and its configuration listed the keys the agents use.

Can you trust the numbers?

Budgets only help if you know what agents did and the numbers are right. Gateways show the same traffic differently: some lead with tokens and cost, and one stored the request content.

agentgateway request log listing models, tokens, cache share and cost, with an empty prompt column.
agentgateway: tokens, cache share and cost per call. Cache share appears when the provider reports cached tokens, and cost only where the gateway knows the model's price. Prompts stayed empty in our setup.
Bifrost log rows with time, request type, message text, model and calling app.
Bifrost: calling app and stored message text, which needs a retention decision.
Vercel AI Gateway log of Nemotron calls, each at zero dollars.
Vercel: the free model at $0, as expected. The top row is GPT, refused on the free tier.
Cloudflare AI Gateway log listing Claude Haiku calls with tokens, cost and duration.
Cloudflare: failed calls sit next to successful ones, with no tokens and no cost.

Check who is reporting the cost

SourceWhat it reportedVerdict
Claude Code on ClaudeMatched both self-hosted gateways to the last digitAccurate
Claude Code on free NemotronEstimated $0.044 to $0.084 per run, against a $0 price on VercelWrong price
CodexToken counts, but no dollar figure in any signal we capturedCalculate it yourself
A hosted gateway on ClaudeAbout 4.7× the price the other sources applied, on mostly cached runs. Fits cache reads billed near the full input priceDiffered
Bifrost, agentgatewayPer-call cost including cache. Exact on Claude, and close to each other on GPTAccurate

Three numbers are easy to confuse, and they need not match:

  • Agent-reported. Claude Code estimates at list price. Codex reports only tokens.
  • Gateway-calculated. Tokens times the gateway's own price table.
  • The invoice. What you actually pay. Check the others against it before you build dashboards. We did not.

What we saw:

  • Claude Code priced a free model. All three runs we checked match $5 input, $25 output and $0.50 cache read per million, likely a default rate for unknown models. Administrators can set rates with modelPricing, which we did not test.
  • The hosted gateway likely missed the cache discount. Pricing cached tokens as full input explains about 3.9× of its 4.7× gap.
  • Codex gives no dollars. A gateway can price every call, which makes it a natural cost source for a mixed fleet.
  • Field names mislead. Anthropic's input_tokens leaves out cache reads: one call showed 2 in Claude Code and 17,268 at the gateway. Normalize fields before you mix sources.

Cache belongs to the provider, and clients decide whether you hit it

Agents resend long prompts on every call, and providers charge much less for input they have seen recently. That cache lives at Anthropic or OpenAI, not in the gateway: Bifrost and Cloudflare, using one key, shared it seconds apart. Both misses we saw came from the client.

Claude Code changes its request with the base URL. Moving from direct to a gateway gave a cold first call. A proxy that forwards requests untouched went cold too, on the same key, so the request changed on the client side, not in the gateway.

In our tests, Codex started every session cold. It sends a new prompt_cache_key per session, a request field that OpenAI's prompt caching docs use to group requests for caching. When our proxy pinned that key to one value, the next two runs read 10,594 tokens on their first call.

Share of first-call input read from cache. Claude Code repeat run: nearly all. Claude Code on a new base URL: none. Codex default: none. Codex with a pinned key: nearly all.
In our tests, only the first call of a session missed. A cold first call cost about $0.002 more on Claude Haiku 5.5 and $0.0012 more on GPT-6 Luna, likely a small share of a long ticket.

Following one ticket, not one call

Gateway telemetry is per call, but a factory needs answers per ticket: what a run cost and which project it was for. Give every call a correlation ID, such as run, ticket and project, and pass the trace context from agent to gateway. In our lab a run ID and traceparent were enough to show one run as a single trace.

Your gateway doesn't determine where your data lives

EU data residency is set hop by hop

The third job is knowing where data goes. Prompts can contain source code and customer data, so many teams must keep them inside a region, which is called data residency. A request can pass through several services, and each may process or store part of it. A gateway in Frankfurt does not keep prompts in Europe: the gateway, the model host, the telemetry collector and any observability backend that receives prompt text all count.

Keep prompts out of telemetry. Make sure spans do not carry full prompts and responses. In our setup one gateway exported them by default, and tool arguments still slipped past our filter, so filter on an allowlist.

Three routes. EU gateway with a model on global routing: prompts leave the EU. EU gateway and EU model with a backend outside the EU: prompts can leave. Gateway, model and backend all in the EU: prompts stay.
A hosted gateway adds a hop. Filtering prompts before export takes the backend off the path, but a collector that does the filtering still sees them.

The model host is usually the deciding hop, and options differ by vendor. This table reflects vendor documentation as we checked it on 5 November 2026. Regional options change, so check them again before you rely on one.

RouteHow you choose the regionWatch for
Direct API
Claude API, directinference_geo: global or USNo EU value documented
OpenAI APIEU project and eu.api.openai.comNew projects only, check eligibility
Cloud infrastructure
Claude on Bedrockeu. inference profileCan process in several EU Regions
Claude on Vertex AIEU endpointsThe eu endpoint excludes the UK and Switzerland
Open-weight hosting
Open-weight on Scalewayapi.scaleway.aiStated as Paris. The only EU route we tested

A gateway makes switching easy. We added two Scaleway models to agentgateway as aliases, and OpenCode finished the task on both with no change. That shows the route works, not where the model ran.

OpenCode asks agentgateway for a model alias. The aliases eu-mistral and eu-qwen route to Scaleway, stated as Paris. The nemotron alias routes to Vercel. The agent stays the same.
The agent names an alias, and the gateway decides where it goes.

Which gateway fits your architecture?

Start from your requirements. These apply even if you pick none of the products we tested:

RequirementWhat to prioritize
Centralized credentialsVirtual keys, with provider credentials held at the gateway
Fine-grained model accessPer-key model lists, or rules on role
MCP securityPer-tool authorization enforced by the gateway
Cost managementPer-call cost that counts cache, and budgets you understand
ObservabilityOTLP support, trace propagation and configurable filtering
Data residencyControl over every service that processes or stores prompts
Infrastructure independenceSelf-hosting and configurable routing

Treat the gateway as infrastructure

A software factory runs many agents on tickets nobody watches live. A gateway gives you one place to control what they access, see what each run cost and decide where their data goes. In our tests it did not guarantee accurate cost, firm budgets, clean telemetry or data residency on its own, so verify those. The agent ecosystem will keep changing. Your front door shouldn't have to change with it.

Blog

  • How a software factory earns the right to merge: CI, narrow checks and AI review feed fixed rules set by the blast radius. When the checks pass the change is ready to merge; on a conflict a reviewer resolves it.

    November 12, 20268 min read

    How a Software Factory Earns the Right to Merge

    A coding agent can turn a ticket into a green pull request in minutes. We split the merge decision into narrow checks, let fixed rules decide, set the strictness by the blast radius of a change, and bring in a person only where the agent gets stuck.

  • A ticket's path through a software factory. Most of the steps come before the coding agent starts.

    November 6, 20266 min read

    Stopping the Wrong Work Early

    We compared a stronger model, a different agent, answers from the author and a read-only investigation before any code. On 19 fresh tickets, the investigation went ahead on 9 of 10 wanted changes and held back 7 of 9 requests an author had already declined.

  • Tasks come and go above one living spec, which stays linked to the code underneath. One link has drifted apart and waits on a question mark.

    November 5, 20266 min read

    Spec Systems Change What's Left Behind More Than the Code

    We ran OpenSpec, Spec Kit, Superpowers and Claude Code plan mode on a real ticket, then planted drift and contradictions. For one feature we couldn't separate the code, and plan mode cost about half. What differs most is the record each leaves for the next agent.

Transform your SDLC with agentsFrom copilots to a self-driving SDLC

With the team behind the first AI for code.

Backed by frontier coding agent research with ETH Zurich.