# MCP agent filters

Let agents request only the part of a response they need, with a JSONata expression per call.

**Allow agent filters** adds an optional `response_filter` parameter to every tool - the agent sends a [JSONata](https://jsonata.org) expression with the call, the expression runs on the server and the agent receives only what it asked for.

A tool that lists invoices returns a many-row document - with a filter, the agent receives only the ids or the total it needs. The expression runs on the server, so the model spends no tokens on the rest.

## Automatic discovery {#automatic-discovery}

Agent filters require no changes to services or clients:

- With the toggle on, `tools/list` advertises `response_filter` on every tool's input schema, described as an optional JSONata expression applied to the response before it is returned - agents discover the parameter from the schema alone.
- The major models already know JSONata and write the expressions themselves - an agent asked for totals alone sends a matching filter with the call.
- Services stay unchanged and never see the parameter - it belongs to the gateway and is taken out of the arguments before [argument validation](https://zato.io/docs/ai/mcp/argument-validation.html) runs.
- Clients stay unchanged too - the parameter is never required, and a client that ignores it keeps receiving full responses as before.

## Enable agent filters {#enable-agent-filters}

1. Go to **AI** > **MCP gateways** and click **Edit** on the gateway
2. Check **Allow agent filters**
3. Click **OK**

In [enmasse](https://zato.io/docs/ai/enmasse.html), the gateway attribute is `allow_agent_filters`.

## The expressions {#the-expressions}

JSONata is a query and transformation language for JSON. Given this tool response:

```json
{
  "count": 5,
  "invoices": [
    {"invoice_id": "INV-2026-0001", "total": 101},
    {"invoice_id": "INV-2026-0002", "total": 102},
    {"invoice_id": "INV-2026-0003", "total": 103},
    {"invoice_id": "INV-2026-0004", "total": 104},
    {"invoice_id": "INV-2026-0005", "total": 105}
  ]
}
```

Each `response_filter` below runs against this document and produces the result next to it:

| Expression | What the agent receives |
| --- | --- |
| `invoices.total` | `[101, 102, 103, 104, 105]` |
| `invoices[total > 103].invoice_id` | `["INV-2026-0004", "INV-2026-0005"]` |
| `$sum(invoices.total)` | `515` |
| `{"first": invoices[0].invoice_id, "how_many": count}` | `{"first": "INV-2026-0001", "how_many": 5}` |

A projection, a predicate, an aggregation and an object the agent composed itself - each call of one session can include a different expression, and a call without one receives the full response. Everything JSONata offers is available, [jsonata.org](https://jsonata.org) documents the full language.

## Order of application {#order-of-application}

Filters run after the [response controls](https://zato.io/docs/ai/mcp/response-controls.html) safeguards, so an expression runs on cleaned data - anything PII removal or the prompt-injection defenses took out is gone before the expression runs. Filters run before the token cap, so the cap measures the size of the filtered response. With the [audit log](https://zato.io/docs/ai/mcp/audit-log.html) on, each applied expression is recorded verbatim in the event's data, under the `agent_filter` key.

## Errors and edge cases {#errors-and-edge-cases}

- An expression with a syntax error, or one that fails against this document, is refused with an invalid-params error naming `response_filter` - agents correct the expression or retry without it.
- A filter that matches nothing returns a JSON `null`, the defined result when nothing matches.
- Expressions are capped at 10,000 characters.
- Errors are never filtered - a result with `isError` passes through untouched, exactly as the tool produced it.
- A gateway with **Allow agent filters** off rejects the parameter as unknown, the way any other undeclared argument is rejected.

## See also {#see-also}

- [Response controls](https://zato.io/docs/ai/mcp/response-controls.html) - The safeguards that clean data before a filter runs
- [Argument validation](https://zato.io/docs/ai/mcp/argument-validation.html) - How undeclared arguments are rejected
- [Audit log](https://zato.io/docs/ai/mcp/audit-log.html) - Where each applied expression is recorded
- [MCP gateways](https://zato.io/docs/ai/mcp/index.html) - Configuration, endpoint behavior and the governance controls

## Learn more {#learn-more}

- [MCP tutorial](https://zato.io/tutorials/mcp/01.html) - Expose Python services as AI tools and connect Claude Code in minutes
- [AI Integrations overview](https://zato.io/docs/ai/) - Both directions of AI traffic in one platform, and where to start
- [MCP gateway reference](https://zato.io/docs/ai/mcp/) - Configuration, endpoint behavior and the governance controls
- [Tool schemas](https://zato.io/docs/ai/mcp/tool-schemas.html) - How a service's docstring and declared I/O become its tool definition
- [MCP gateway security](https://zato.io/docs/ai/mcp/security.html) - API keys, Basic Auth and bearer tokens for AI agents
- [MCP response controls](https://zato.io/docs/ai/mcp/response-controls.html) - PII removal, prompt-injection safeguards and token-denominated size caps
- [MCP audit log](https://zato.io/docs/ai/mcp/audit-log.html) - What each agent did, when and with what outcome - payloads never recorded
- [MCP alerts](https://zato.io/docs/ai/mcp/alerts.html) - Failing backends, agents stuck in a loop and gateways with too many tools
- [Cost and limits](https://zato.io/docs/ai/cost-and-limits.html) - Every setting that caps AI cost and traffic, with worked examples
- [GitOps for AI integrations](https://zato.io/docs/ai/enmasse.html) - LLM connections and MCP gateways as YAML in version control
- [AI examples](https://zato.io/docs/ai/examples/) - Complete, runnable scenarios - one real-world need per file
