TypeScriptADK-TS

Tool Output Filter Plugin

Shrink large tool outputs with intelligent JQ filtering to save tokens

When a tool returns a huge JSON response (like a full API payload or database result), it eats up your token budget and can confuse the model. The Tool Output Filter Plugin automatically detects oversized outputs and uses an LLM to generate JQ filters that extract only the relevant data — shrinking the output while preserving what matters.

Quick start

import { AgentBuilder, LLMRegistry, ToolOutputFilterPlugin } from "@iqai/adk";

const { runner } = await AgentBuilder.withModel("gemini-1.5-pro")
  .withDescription("Data analysis assistant")
  .withPlugins(
    new ToolOutputFilterPlugin({
      filterModel: LLMRegistry.newLLM("gemini-1.5-flash"),
    }),
  )
  .build();

That's it. Any tool output over 8KB or 50 JSON keys will be automatically filtered down to ~4KB.

Requires JQ

This plugin shells out to the jq command-line tool. Install it before using: brew install jq (macOS), apt-get install jq (Ubuntu), or choco install jq (Windows).

How it works

  1. A tool returns a large JSON response
  2. The plugin checks if it exceeds the size/key thresholds
  3. If so, it asks a fast LLM (like Gemini Flash) to generate a JQ filter expression (e.g., {id, name, summary})
  4. It runs jq on the data to extract just the relevant fields
  5. If the result is still too large, it repeats (up to maxIterations)
  6. The filtered output replaces the original, with metadata showing what changed

If anything goes wrong (JQ error, timeout, dangerous pattern), the plugin falls back to the original output — it never breaks your agent.

Usage

import { AgentBuilder, LLMRegistry, ToolOutputFilterPlugin } from "@iqai/adk";

const { runner } = await AgentBuilder.withModel("gemini-1.5-pro")
  .withDescription("Data analysis assistant")
  .withPlugins(
    new ToolOutputFilterPlugin({
      filterModel: LLMRegistry.newLLM("gemini-1.5-flash"),
      enabledTools: ["api_tool", "database_tool"], // only filter these
    }),
  )
  .build();
import { LlmAgent, LLMRegistry, ToolOutputFilterPlugin } from "@iqai/adk";

const agent = new LlmAgent({
  name: "data_assistant",
  description: "Analyzes large datasets",
  model: "gemini-1.5-pro",
  plugins: [
    new ToolOutputFilterPlugin({
      filterModel: LLMRegistry.newLLM("gemini-1.5-flash"),
    }),
  ],
});
import { AgentBuilder, LLMRegistry, ToolOutputFilterPlugin } from "@iqai/adk";

const { runner } = await AgentBuilder.withModel("gemini-1.5-pro")
  .withDescription("Data analysis assistant")
  .withPlugins(
    new ToolOutputFilterPlugin({
      filterModel: LLMRegistry.newLLM("gemini-1.5-flash"),
      config: {
        sizeThreshold: 12000,  // trigger at 12KB (default: 8000)
        keyThreshold: 75,      // trigger at 75 keys (default: 50)
        targetSize: 6000,      // aim for 6KB output (default: 4000)
        maxIterations: 5,      // up to 5 filter passes (default: 3)
        debug: true,           // log filtering details
      },
      enabledTools: ["api_fetcher", "database_query"],
    }),
  )
  .build();

Configuration

OptionTypeDefaultDescription
filterModelBaseLlmRequiredLLM to generate JQ filters. Use LLMRegistry.newLLM("model-name")
sizeThresholdnumber8000Character count to trigger filtering
keyThresholdnumber50JSON key count to trigger filtering
targetSizenumber4000Target output size after filtering
maxIterationsnumber3Max filter-and-check cycles
maxSchemaDepthnumber4Depth for schema extraction
debugbooleanfalseLog filtering details
enabledToolsstring[]undefinedOnly filter these tools (undefined = all)
disabledToolsstring[]undefinedSkip filtering for these tools

Use a fast, cheap model for filterModel (like Gemini Flash) — filter generation is lightweight and doesn't need a powerful model.

What the filtered output looks like

When filtering is applied, the output includes metadata:

{
  "_mcp_filtered": true,
  "_original_size": 15000,
  "_filtered_size": 4500,
  "_reduction_percent": 70.0,
  "_jq_filters": ["{id, name, summary}"],
  "_iterations": 2,
  "id": "123",
  "name": "Example Data",
  "summary": "Filtered content..."
}

When to use it

  • Large API responses (REST APIs returning verbose data)
  • Database queries with nested relationships
  • File processing (large JSON/XML files)
  • Third-party integrations with verbose outputs

When not to use it

  • Small outputs (unnecessary overhead)
  • Binary/non-JSON data (plugin only processes JSON)
  • Critical data where every field matters
  • Real-time requirements where filtering latency is unacceptable

Debugging

new ToolOutputFilterPlugin({
  filterModel: LLMRegistry.newLLM("gemini-1.5-flash"),
  config: { debug: true },
});
// Logs: response size/key count, generated JQ filters, iteration progress

Troubleshooting

IssueFix
Not filtering outputsCheck output exceeds thresholds. Verify tool isn't in disabledTools.
Filtering too slowReduce maxIterations. Increase targetSize. Use faster filter model.
Critical data filtered outIncrease targetSize. Adjust sizeThreshold higher.
JQ errorsVerify JQ is installed (jq --version). Check for malformed JSON. Enable debug: true.

Next steps