For the complete documentation index, see llms.txt.
Skip to main content

Tools Output Filtering

As explained in Why reShapr?, reShapr can create secure MCP servers in seconds without coding, just by importing your API's existing artifacts such as OpenAPI 3.x specs, GraphQL schemas, and gRPC/Protobuf definitions. Once these artifacts are imported, the Custom Tools specification lets you redefine the input of a tool to fit a specific use case.

ToolsOutputFilters is the symmetrical capability on the output side: it lets you declaratively control what a tool returns to the model, before the response is wrapped into the JSON-RPC MCP envelope and sent back to the client.

This supports two common requirements:

  • Response scope. A GraphQL node with many scalar properties or a REST endpoint returning a deeply nested JSON tree can expose fields that are irrelevant to the task. Filtering at the gateway retains only the required response shape.
  • Stable response shape. Retaining and patching known fields gives the Agent a smaller, more predictable result independent of the underlying API protocol.

In reShapr 1.0.0, a filter-processing error returns the original Tool response. Treat ToolsOutputFilters as response shaping, not as a security or data-loss-prevention boundary. Prevent access to sensitive fields in the backend contract and authorization layer.

reShapr applies filtering universally, regardless of the source protocol (REST, GraphQL, gRPC), because filters operate on the canonical JSON response produced by reShapr's protocol converters.

A first example​

Here is a simple ToolsOutputFilters artifact that trims the response of a custom GitHub tool down to a few key fields, removes a sensitive field, and adds a value:

apiVersion: reshapr.io/v1alpha1
kind: ToolsOutputFilters
service:
name: GitHub GraphQL
version: '20250917'
filters:
get_user_with_latest_followers:
jsonRetain:
- /data/user/name
- /data/user/login
- /data/user/bio
- /data/user/avatarUrl
- /data/user/followers
jsonPatches:
- op: add
path: /data/user/location
value: "Worldwide"
- op: remove
path: /data/user/followers/nodes

A ToolsOutputFilters artifact follows some simple rules:

  • It always contains an identification section made of apiVersion and kind properties that must have the reshapr.io/v1alpha1 and ToolsOutputFilters values respectively,
  • It must be bound to a specific reShapr Service using the service.name and service.version properties whose values must match an already discovered Service,
  • The filters section then defines the filters, keyed by tool name:
    • The key (here get_user_with_latest_followers) must match an existing tool on the Service, either an imported tool or a Custom Tool previously attached to that Service,
    • A filter entry must specify at least one of jsonRetain, jsonPatches, compact, or convertToToon,
    • When both jsonRetain and jsonPatches are present, jsonRetain is always applied first, as a pre-processing step that narrows the response, before jsonPatches are applied in order,
    • compact is applied after jsonPatches, and convertToToon is applied last.

You can specify as many tool filters as you want in the same ToolsOutputFilters artifact, as long as each key targets a distinct tool of the bound Service.

The jsonRetain operation​

jsonRetain is reShapr's addition to the JSON Patch vocabulary. Standard JSON Patch (see below) only lets you describe what to remove, which is impractical when the response is a large object and you only care about a small subset of it. With jsonRetain, you declare the branches you want to keep, and everything else is dropped before patches run.

  • The value of jsonRetain must be a non-empty list of JSON Pointer paths,
  • Each listed path must resolve against the original tool response. Paths that don't resolve are silently ignored (a missing branch isn't an error, it's simply nothing to keep),
  • The order of paths in jsonRetain is not significant: jsonRetain describes a set of branches to keep,
  • If jsonRetain is omitted, the whole response is passed through to the jsonPatches step unchanged.

The jsonPatches operation​

jsonPatches is an ordered sequence of JSON Patch operations, applied in the listed order to whatever the jsonRetain step produced (or to the full response, when jsonRetain is omitted).

reShapr supports the six standard JSON Patch operations:

OperationEffect
addAdds a value at the given path. Creates the field if it doesn't exist; inserts into arrays.
replaceReplaces the value at the given path. The path must already exist.
removeRemoves the value at the given path.
copyCopies the value from from to path.
moveMoves the value from from to path (equivalent to copy + remove on the source).
testAsserts that the value at path equals value. If the test fails, the patch sequence stops.
  • The value of jsonPatches must be a non-empty list of objects, each carrying an op property whose value must be one of the six operations above,
  • path must be a JSON Pointer that resolves against the working document (the response after jsonRetain has been applied),
  • op: copy and op: move must additionally specify a from JSON Pointer,
  • op: add, replace, and test must specify a value,
  • The order of the list is significant: each operation sees the document as modified by the operations that came before it.

For the precise semantics of each operation, refer to RFC 6902: JavaScript Object Notation (JSON) Patch.

The compact operation​

Set compact: true to recursively remove sparse values from the filtered JSON response:

  • null values;
  • empty strings;
  • empty arrays;
  • empty objects.

Compaction also removes a parent array or object when pruning its children leaves it empty. It runs after jsonRetain and jsonPatches, and before convertToToon. Omit compact or set it to false when an empty value carries domain meaning that the client must preserve.

The convertToToon operation​

convertToToon converts the final filtered JSON output into Toon format, a compact representation intended for LLM consumption.

  • The value of convertToToon must be true,
  • It is applied last, after jsonRetain, jsonPatches, and compact have run,
  • It can be used alone — without any jsonRetain or jsonPatches — and it will compact the full raw tool response as-is,
  • It works regardless of the backend protocol: REST, GraphQL, and gRPC tool responses are all converted to canonical JSON before filters run, so convertToToon applies uniformly across all three.

This makes convertToToon: true the simplest possible ToolsOutputFilters entry: a single key that converts any tool response without requiring you to know its shape upfront.

apiVersion: reshapr.io/v1alpha1
kind: ToolsOutputFilters
service:
name: GitHub GraphQL
version: '20250917'
filters:
get_user_with_latest_followers:
convertToToon: true

You can also combine it with jsonRetain and jsonPatches to both shape and compact the response:

apiVersion: reshapr.io/v1alpha1
kind: ToolsOutputFilters
service:
name: GitHub GraphQL
version: '20250917'
filters:
get_user_with_latest_followers:
jsonRetain:
- /data/user/name
- /data/user/login
- /data/user/bio
- /data/user/avatarUrl
- /data/user/followers
jsonPatches:
- op: add
path: /data/user/location
value: "Worldwide"
- op: remove
path: /data/user/followers/nodes
convertToToon: true

Naming and configuration scope​

Like other complementary artifacts, a ToolsOutputFilters file is attached to a Service. A Configuration Plan controls whether that attached artifact contributes to an Exposition through its includedArtifacts selection.

  • When includedArtifacts is absent or empty, all complementary artifacts attached to the Service apply, including every attached ToolsOutputFilters artifact.
  • When includedArtifacts contains artifact names, only those attached artifacts apply to the Configuration Plan.

This selection lets two Configuration Plans expose the same Service with different output filters. For example, partner-plan can set includedArtifacts to partner-output-filters.yaml, while internal-plan selects internal-output-filters.yaml. Each Exposition then uses the filters selected by its Configuration Plan.

Where filters fit in the request lifecycle​

For a given MCP tool call, reShapr applies transformations in this order:

  1. The incoming MCP tool call is validated against the tool's input schema (the imported one, or the one defined by a Custom Tool).
  2. The call is converted to a protocol-specific request (REST, GraphQL, gRPC) and dispatched to the backend.
  3. The backend response is converted back into a canonical JSON response by reShapr's converters.
  4. If a ToolsOutputFilters artifact is attached to the Service and declares a filter for this tool, the filter is applied here: jsonRetain first, then jsonPatches, then compact, and finally convertToToon if set.
  5. The filtered response is wrapped into the JSON-RPC MCP envelope and returned to the client.

Because filtering happens after the converter step and before the MCP envelope, the same ToolsOutputFilters artifact applies uniformly whether the underlying tool is backed by REST, GraphQL, or gRPC.

To compare an unfiltered and filtered response with identical Tool arguments, follow Context Control in Practice.

Agent View