Content Mediation
Everything an agent does outside its sandbox passes through AgentVisor — LLM requests, MCP tool calls, messages to other agents, writes to its store and checkpoints — and so does each reply. The Policy Engine decides whether each call is allowed. Content mediation looks at what the call carries, in both directions, and decides whether it may go through.
Content mediation is the capability. Content middleware is what
you configure to carry it out: an ordered chain: of checks and the raw_request: /
raw_response: hooks, all under content_middleware:. Some checks are built into AgentVisor; for
the rest, you point AgentVisor at a service of your own and it calls that service for each
decision. Each check permits the content, denies it, or — where a rewrite is possible — modifies
it, and every decision is recorded.
| Policy Engine | Content mediation | |
|---|---|---|
| Asks | may this call be made? | may what it carries go through? |
| Looks at | who is calling, the operation, the resource, and with enrichment an MCP tool call's arguments or an A2A task's metadata | the payload: prompts, tool arguments, results, messages |
| Decides from | PolicyDomain rules you write | content middleware you configure |
| Can | allow or deny | permit, deny, or (on HTTP) modify |
| Covers | the agent's calls, and starting a run | both directions: what goes out and what comes back |
The Content Mediation tutorial module walks you through how to redact sensitive content from agent traffic and read the audit trail, hands-on, with no model-provider key.
What it is for
- Stop data leaving. Redact or refuse what must not leave, on whichever path it would take.
- Shape LLM traffic. Restrict which models an agent may call, and lower the token ceiling it asked for.
- Govern what comes back. Inspect a provider's reply, a tool's result or another agent's answer before the agent sees it.
- Record token usage and decisions. Meter tokens per principal, and keep an audit record of every decision. The record says what was decided, not what the agent sent: AgentVisor does not copy a prompt, a tool argument or a reply into it.
Where it sits
Content mediation sits on both directions of the sandbox boundary, at every step of a turn. Each turn of an interaction is its own run — a resume after an interrupt included — and each run invokes the agent once.
| Step of a turn | Policy Engine | Content mediation |
|---|---|---|
| 1. The run's input arrives from the caller | ✓ when the run is created | ✓ when the run is created |
| 2. The agent makes its calls | ✓ each call¹ | ✓ each call |
| 3. Replies come back to the agent | — | ✓ |
| 4. The agent answers its caller | — | ✓ each streamed chunk, and the result |
¹ Except checkpoint saves and telemetry, which only content mediation checks.
1. The run's input arrives from the caller. What a caller asked the agent to do passes through content mediation before the agent is given it. The Policy Engine decided this caller may start a run; content mediation decides whether the agent is invoked with this input. A denial refuses the request, so the run never starts and nothing it would have done — no model call, no tool call — happens. The input is checked once, however many times the run is retried or resumed. It is also the only point that sees the request whole, which is what lets a record tie a tool call to what was asked.
2. The agent makes its calls. Every call out of the sandbox — to a model, a tool, another agent, its store — passes through these layers in order:
| Order | Layer | Decides |
|---|---|---|
| 1 | Policy Engine | whether this principal may make this call at all |
| 2 | Content mediation | whether what the call carries may go through, and in what form |
| 3 | Credential brokering | which real secret replaces the agent's symbolic token (HTTP) |
| 4 | Egress controls | whether the resolved address may be reached (HTTP) |
A call the Policy Engine refuses never reaches content inspection. And on HTTP, content middleware
runs before credentials are substituted, so no middleware — including a third-party service you
configure — is handed a real API key. It sees the symbolic token instead (for example mav-tok-…).
3. Replies come back to the agent. A provider's response, a tool's result, another agent's answer, a value read back from the store — each passes through content mediation before the agent sees it. The call was authorized on the way out, so the Policy Engine is not consulted again; content mediation is the check on what came back.
4. The agent answers its caller. What the agent streams to its caller is inspected chunk by chunk as it goes, and the run's result is inspected before the caller is given it. A denial withholds the result — though not chunks already streamed; see Current limitations.
Two kinds of middleware
| The chain | Raw hooks | |
|---|---|---|
| Configured as | chain: — an ordered list of entries | raw_request: and raw_response: |
| Sees | an LLM request's conversational text, lifted out as spans; any other HTTP body, if text or JSON, as one span, or two for JSON (as sent, and with escapes decoded) | the payload exactly as it was sent or received |
| Runs on | every HTTP request the agent makes (not a WebSocket upgrade) | every hooked path |
| Can | permit or deny any HTTP request; edit an LLM request's text, and lower the generation parameters of any JSON request; on the reply, only record what it finds — it cannot block or change it | permit, deny; rewrite only an HTTP call or its reply |
| Written by | AgentVisor's built-ins, or your own http checker | your own service |
The chain inspects the agent's HTTP calls, and understands LLM requests in particular. An extractor recognizes the provider's payload shape and hands each middleware the text in it, so one rule finds the same SSN in an OpenAI request and an Anthropic one without knowing which it is reading. Any other HTTP body it sees whole, as one span it can permit or deny but not rewrite, or two for a JSON body: as sent, and with escapes decoded. Off HTTP there is nothing for an extractor to model, so the chain does not run there at all.
A raw hook does not parse the payload, so it works on every path. It hands your service the bytes, or the RPC message, as they were sent or received — and on every path but HTTP it is the only middleware there is.
Use the chain for HTTP traffic, and raw hooks everywhere else.
What it reaches
Content mediation hooks the agent's paths out of the sandbox, each in the direction that carries content:
| Family | Operations | Notes |
|---|---|---|
| HTTP | agentvisor:http:request, agentvisor:http:upgrade | http:request is the only operation the chain runs on |
| MCP | agentvisor:mcp:tool:*, agentvisor:mcp:resource:* | arguments going out, results coming back |
| A2A | agentvisor:a2a:* | tasks sent to other agents, and their answers |
| Run boundary | content:run:input, content:run:output | the run's own input and its result |
| Streamed output | content:stream:chunk | the agent's reply to its caller, a chunk at a time |
| Persistence | agentvisor:store:*, agentvisor:thread-kv:*, content:checkpoint:* | the agent's hot loop — a checkpoint is saved on every graph step |
| Telemetry | content:telemetry:spans, content:telemetry:log | where a tracer can leak a prompt that was redacted elsewhere |
An agentvisor: operation is one the Policy Engine also evaluates. A content: operation
exists for content mediation alone: no PolicyDomain rule reaches it, and on those paths content
middleware is the only governance there is. The
Content Middleware Reference lists every
operation and the phase it is hooked on.
Current limitations
Content mediation does not yet cover the following:
-
What is sent over a WebSocket. Opening a WebSocket is hooked: the upgrade request (
agentvisor:http:upgrade) is inspected, and a deny refuses the connection. An upgrade request that is not aGETwith no body is refused. Once it is open, the messages exchanged over it are not inspected. -
The terminal of an
agentvisor execsession — what you type and what the tool prints. The tool's own outbound calls are inspected as usual. -
AgentVisor's own logs. Only the agent's logs are hooked.
-
Streamed replies, as a whole. Each streamed chunk is inspected on its own and, if permitted, delivered at once. A deny ends the run, but chunks already delivered stay delivered. Content split across chunks — an SSN arriving a few characters at a time on a token-mode stream — matches no per-chunk check. The run's result is inspected only when the run ends, after every chunk has been delivered, so a deny there withholds the result but cannot take back what the caller already has.
Judge the reply before it is streamedHook the content where it enters the agent instead.
raw_responsewithdelivery: wholeon the LLM call sees the complete model reply before the agent does, so a deny there means there is nothing to stream. A tool result the agent might repeat can be refused the same way, on the tool call's response.
Securing your middleware
Your middleware sees the content itself, which on most paths means real data:
- On an HTTP call (chain or raw hook), it sees the agent's symbolic credential, never the real one.
- On every other path — MCP, A2A, persistence, telemetry, the run's input and result — only raw hooks run, and nothing is substituted. A store value, a tool result or a run's input reaches the hook verbatim, secrets included.
The built-in checks run inside AgentVisor and need no setup. If you bring your own service instead, AgentVisor calls it over HTTP, in one of two ways:
-
As an
httpchain entry — a check in the chain that is sent the text of each HTTP call as spans. AgentVisor can authenticate itself to your service and verify the service's certificate against a private CA, so the service can run anywhere. -
As a raw hook — your service is sent the payload exactly as it was sent or received, on any hooked path, and returns a verdict.
Run a raw hook on loopbackRaw hooks have no
tls:or header settings yet, so AgentVisor can neither prove who it is nor verify the service it calls, and the service's reply is trusted as-is. Run the service on loopback, or behindhttpswith a publicly-trusted certificate. A private CA, mTLS and request authentication are not supported for raw hooks.
Every decision is recorded
Each verdict becomes an OTLP log record, sent to the collector you configure under
telemetry.audit and from there to your log store or SIEM. It is a log rather than a trace span
because spans can be sampled, but every decision must be recorded. It says who, what and the
decision. AgentVisor never copies message text or tool-argument values into it: a finding names a
location, a type and a score. Two things it does record come from the agent: a finding's location
names the tool-argument keys above the value, which the agent chose, and model_policy's refusal
quotes the model the agent asked for, cut to 64 characters. What your own checker or raw hook
returns, such as a deny reason, is recorded as you send it. Keep matched text out of it, and the
audit stream will not become a copy of the data it governs.
What operators use it for:
- Evidence. Every permit, deny and rewrite, with the principal, agent, run and the check that decided it — the record a compliance review asks for.
- Investigation.
trace_id,thread_idandrun_idtie a decision to the run and the trace around it, so a refused call can be followed back to the request that caused it. - Tuning checks. Findings are recorded on permits as well as denies, so you can see what a
rule matches before making it block. A check on the
asynclane records what it would have decided (enforced: false) without blocking anything — a way to trial a new rule on live traffic. - Spend. Token counts from
token_meterare recorded per principal, which is how usage per person or per agent is answered. - Coverage. A payload that passed uninspected — a binary body, say — is recorded with the
reason, so gaps show up in the stream rather than staying silent. A check that failed and was
let through by
on_error: allowis named infailed_open.
Every field is listed in the Content Middleware Reference.
Next steps
- Content Middleware guides — configure your first chain and hook
- Content Middleware Reference — operations, wire contracts, audit fields
- Configuration Reference — the section's own
settings;
chainentries and raw hooks are configured as the guides show