jq Tutorial 2026: JSON Processing for AI Agents (+ Fix jq Input Errors)
Master jq 1.8.2 for AI agent pipelines. Learn essential filters, the jq MCP server, Jaiqu transforms, and how to fix malformed JSON before jq errors out.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →Every AI agent pipeline eventually hits the same wall: a model returns JSON, you pipe it into jq, and the terminal screams parse error (Invalid numeric literal at EOF at line 1, column 4).
jq has no repair capability. It expects valid JSON — full stop. When LLMs hallucinate a trailing comma or truncate a response mid-object, the whole pipeline breaks. This guide covers both sides of that problem: getting fluent with jq 1.8.2 for real AI agent workflows, and knowing how to fix the malformed JSON that trips jq up.
What is jq?
jq is a lightweight command-line JSON processor. Think of it as sed/awk for JSON: you pass a filter expression, and jq slices, transforms, and reshapes your data.
echo '{"user": {"name": "Alice", "role": "admin"}}' | jq '.user.name'
"Alice"
jq 1.8.2 (released June 20, 2026) is the current stable release. It's a security and bug-fix patch over 1.8.1, with notable improvements:
- Max print depth increased from 256 → 10,000 — critical for deeply nested LLM tool call responses
- Fixed raw input (
-R) corrupting multi-byte UTF-8 characters — important for multilingual JSON from LLMs - Patched heap buffer overflow and stack overflow vulnerabilities
- Fixed
rtrimstr("")infinite loop edge case - Fixed
@uriand@uridmulti-byte encoding bugs
Install it:
# macOS
brew install jq
Ubuntu / Debian
sudo apt-get install jq
Windows (winget)
winget install jqlang.jq
Essential jq Filters for AI Agent Output
AI agents return structured JSON. Here are the jq patterns you'll use most.
1. Extract a field
echo '{"role": "assistant", "content": "Hello"}' | jq '.content'
"Hello"
Without quotes
echo '{"name": "Alice"}' | jq -r '.name'
Alice
2. Navigate nested objects
cat response.json | jq '.choices[0].message.content'
This is the standard pattern for OpenAI-compatible API responses.
3. Array operations
# Map over array
echo '[{"id": 1, "val": 10}, {"id": 2, "val": 20}]' | jq '[.[] | .val]'
[10, 20]
Filter array
echo '[{"score": 0.9}, {"score": 0.4}, {"score": 0.7}]' | jq '[.[] | select(.score > 0.6)]'
[{"score": 0.9}, {"score": 0.7}]
Get array length
echo '[1, 2, 3, 4, 5]' | jq 'length'
5
4. Reshape / reconstruct
# Extract only specific fields from a list
cat tool_calls.json | jq '[.[] | {name: .function.name, args: .function.arguments}]'
5. String interpolation
echo '{"model": "claude-sonnet-4-6", "tokens": 1240}' | jq '"Model: \(.model), used \(.tokens) tokens"'
"Model: claude-sonnet-4-6, used 1240 tokens"
6. Handle null safely
# Default value when field is missing
echo '{"name": "Alice"}' | jq '.email // "not provided"'
"not provided"
7. Recursive descent
# Find all "error" fields anywhere in a deep response
cat llm_response.json | jq '.. | .error? // empty'
The increased max print depth in jq 1.8.2 (10,000) makes recursive descent on large agentic responses reliable without hitting depth limits.
The jq MCP Server: Let AI Agents Run jq
The Berry Dev jq MCP Server wraps jq as an MCP tool, letting Claude and other agents run arbitrary jq filters against JSON files.
query_json— run a jq filter against a JSON file pathget_jsonschema— retrieve a JSON Schema for a file (helps the LLM understand structure)
The 247arjun/mcp-jq project and the Zapier JQ MCP Server offer hosted alternatives.
Install Berry Dev's server:npm install -g @berrydev-ai/jq-mcp-server
Claude Desktop config (~/.claude/config.json):
{
"mcpServers": {
"jq": {
"command": "npx",
"args": ["-y", "@berrydev-ai/jq-mcp-server"]
}
}
}
Why this matters for AI agents: Before the jq MCP server, an agent had to ask the user to run a jq query manually and paste the result. Now Claude can directly query large JSON files — API responses, database exports, log dumps — without reading the entire file into the context window.
> Critical: The jq MCP server requires valid JSON input. If the JSON file is malformed, query_json will fail. Repair it first before passing it to the server.
Jaiqu: AI-Powered jq Query Generation
Jaiqu (AgentOps-AI) flips the model: instead of writing jq filters manually, you describe the transformation in plain English and Jaiqu generates the jq query.from jaiqu import translate_schema
source = {"user": {"name": "Alice", "meta": {"created": "2026-01-01"}}}
target_schema = {"properties": {"username": {"type": "string"}, "joined": {"type": "string"}}}
jq_filter = translate_schema(source, target_schema)
print(jq_filter)
'.user.name as $name | .user.meta.created as $date | {"username": $name, "joined": $date}'
This is particularly useful in agentic pipelines where the output schema varies by LLM call — Jaiqu auto-generates the transform instead of hardcoding it.
When jq Fails: Diagnosing the Error
jq's error messages are terse. Here's a translation guide:
| jq Error | What it means | Common cause |
|---|---|---|
parse error (Invalid numeric literal at EOF) | JSON truncated mid-number | LLM hit max token limit |
parse error (Unexpected token) | Illegal character | Trailing comma, single quotes, unquoted key |
null (null) and null (null) cannot be added | Chaining fails on nulls | Missing field in JSON |
Cannot index null with string "foo" | Field doesn't exist | Schema mismatch between expected and actual |
parse error: ...at line 1, column 1 | Empty input | Upstream command returned nothing |
The first two errors — truncation and illegal tokens — are the jq input problems. The last three are filter logic issues.
Fixing Malformed JSON Before jq Runs
When jq refuses your JSON, the problem is always upstream. LLMs produce broken JSON in predictable ways:
{"items": [{"id": 1, "name": "Widget"}, {"id": 2, "name":
2. Trailing commas
{"a": 1, "b": 2,}
3. Single-quoted strings
{'name': 'Alice', 'role': 'admin'}
4. Unquoted keys
{name: "Alice", role: "admin"}
5. Markdown-wrapped JSON
json
{"key": "value"}
None of these will pass through jq. The fix is to repair the JSON before processing. Paste it into AI JSONMedic or run the repair programmatically:
from json_repair import repair_json
import subprocess
raw_llm_output = """{'name': 'Alice', 'role': 'admin',}"""
Repair first
repaired = repair_json(raw_llm_output)
'{"name": "Alice", "role": "admin"}'
Now safe to pipe through jq
result = subprocess.run(
["jq", ".name"],
input=repaired,
capture_output=True,
text=True
)
print(result.stdout) # "Alice"
The json_repair library (Python) handles all five patterns above. For Node.js use jsonrepair. For one-off fixes, AI JSONMedic's repair tool handles truncated, malformed, and escaped JSON online without sending data to any server.
A Reliable AI Agent jq Pipeline
Here's a production pattern that handles LLM output correctly:
import json
import subprocess
from json_repair import repair_json
def run_jq_on_llm_output(llm_response: str, jq_filter: str) -> dict:
"""
Safely apply a jq filter to LLM output.
Repairs JSON before processing, surfaces parse failures clearly.
"""
# 1. Strip markdown fences if present
text = llm_response.strip()
if text.startswith("
"):
lines = text.split("\n")
text = "\n".join(lines[1:-1]) # remove first/last fence lines
# 2. Attempt parse — fast path for valid JSON
try:
json.loads(text)
clean_json = text
except json.JSONDecodeError:
# 3. Repair path
clean_json = repair_json(text)
if not clean_json:
raise ValueError(f"Could not repair LLM output: {text[:200]}")
# 4. Run jq
result = subprocess.run(
["jq", "-r", jq_filter],
input=clean_json,
capture_output=True,
text=True
)
if result.returncode != 0:
raise ValueError(f"jq error: {result.stderr.strip()}")
return result.stdout.strip()
Usage
llm_output = "{'items': [{'id': 1, 'name': 'Widget',}]}"
names = run_jq_on_llm_output(llm_output, "[.items[].name]")
print(names) # ["Widget"]
This pattern:
- Handles markdown-fenced code blocks
- Fast-paths valid JSON (no repair overhead)
- Repairs silently when needed
- Surfaces jq errors clearly (not silently swallowed)
jq for MCP Tool Call Processing
When debugging MCP tool calls, jq is invaluable. Claude often returns tool calls as JSON inside tool_use content blocks:
bash
Extract all tool names from a Claude API response
cat claude_response.json | jq '[.content[] | select(.type == "tool_use") | .name]'
Get arguments for a specific tool call
cat claude_response.json | jq '.content[] | select(.type == "tool_use" and .name == "query_db") | .input'
Check if any tool call has invalid input schema
cat mcp_response.json | jq '.tools[] | select(.inputSchema | has("anyOf") or has("oneOf")) | .name'
The last filter is directly relevant post-MCP July 28 RC: anyOf and oneOf are now valid in tool schemas under JSON Schema 2020-12. Use jq to audit your schemas before the RC lands on July 28.
jq Cheat Sheet for AI Developers
bash
--- BASIC ---
. # Identity (pass through)
.field # Get field
.field.nested # Nested field
.array[0] # First element
.array[-1] # Last element
.array[2:5] # Slice (index 2 to 4)
--- PIPES ---
.choices[0] | .message | .content # Chain
--- ARRAYS ---
.[] # Iterate all elements
[.[] | .field] # Map: extract field from each
[.[] | select(.x > 5)] # Filter
[.[] | .x] | add # Sum
[.[] | .x] | length # Count
unique_by(.field) # Deduplicate
sort_by(.score) | reverse # Sort descending
--- OBJECTS ---
keys # Array of keys
values # Array of values
to_entries # [{key, value}]
from_entries # Reverse
with_entries(.value += 1) # Map over entries
--- CONDITIONALS ---
if .score > 0.8 then "high" else "low" end
.field // "default" # Null coalescing
.field? # Suppress errors
--- TYPES ---
type # "null", "boolean", "number", "string", "array", "object"
strings, numbers, booleans, arrays, objects, nulls # Type filters
--- OUTPUT ---
-r # Raw (no quotes on strings)
-c # Compact (one line)
-e # Exit code 1 if last output is false/null
--arg k v # Pass string variable: jq --arg name "Alice" '.greet = "Hello " + $name'
--argjson k v # Pass JSON variable
```
Frequently Asked Questions
Does jq work with JSON5 or YAML?No. jq requires strict JSON — no comments, no trailing commas, no single quotes. If you have JSON5 or YAML, convert first: yq e -o json file.yaml | jq '.'. If you have malformed JSON from an LLM, repair it first before passing to jq.
jq '.' fail on a file that looks valid?
The most common cause is a BOM (byte-order mark) at the start of the file. Run file yourfile.json to check, or cat -v yourfile.json | head -1 to see hidden characters. jq 1.8.2 also fixed some multi-byte UTF-8 edge cases — upgrade if you're on 1.7.x.
Use set -e or check $? after each jq call. Better: use jq -e which returns exit code 1 when output is false or null. For production pipelines, prefer the Python pattern with explicit error handling shown above.
The jq MCP server lets Claude query large JSON files (API exports, log files, database dumps) without reading the full file into context. This saves tokens and keeps the context window clean. The agent writes the jq filter, the server executes it, and returns only the slice you need.
Can jq repair JSON?No. jq's parser is strict — it's a feature, not a limitation. If you want repair-then-query as a single step, use the Python pipeline pattern above, or paste into AI JSONMedic's online tool which handles repair before you query.
What changed in jq 1.8.2?jq 1.8.2 (June 20, 2026) is a security and stability patch. Key changes: max print depth raised from 256 to 10,000 (helpful for deeply nested LLM responses), multi-byte UTF-8 fixes for raw input and @uri, heap/stack overflow security patches, and the rtrimstr("") infinite loop fix.
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles