Skip to content

Read tools should return plain text, not JSON-encoded strings #221

@shellicar

Description

@shellicar

The problem

When I use ReadFile, Find, Grep, or SearchFiles, the content comes back as JSON — string values inside a JSON structure. That means every piece of content I read has been through JSON string encoding before I see it.

This causes real problems. When a file contains \.ts$ (a regex with a literal backslash), JSON encodes that backslash as \\, so I see \\.ts$ in the tool output. I reason about the wrong thing: I think the file has two backslashes when it only has one. This is exactly what happened when reviewing Find.ts — I read the input_examples field, saw \\.ts$, concluded the file was correct, and missed that it was actually \.ts$ (one backslash, wrong escape) in the source.

The double-escape problem is a symptom of a deeper issue: I am not reading the file. I am reading a JSON representation of the file.

Where JSON earns its keep

JSON is the right format when output contains multiple distinct fields the system needs to act on differently — a patchId to pass to EditFile, an error.code to branch on, an approval state to act on. Named fields with typed values.

For text and search tools, there are no such fields. The content is the entire output. JSON adds an encoding layer with no payoff.

Design direction: pipe-aware rendering

The right mental model is Unix stdout: while data is in a pipe it carries structure, but once it hits the screen it’s flat text. The structure is an implementation detail of the pipe — it was never meant to land in Claude’s context window.

  • In a pipe (Pipe chaining FindGrep etc.): tools pass typed output to the next step directly. No serialisation in between.
  • At the display boundary (the result that enters Claude’s context): output is rendered to flat readable text.

defineTool could take an optional render(output) => string alongside handler. The framework calls render for display and passes raw typed output to the next pipe step. Pipe skips render on intermediate steps and only calls it on the final output.

Affected tools

Tool Current Better
ReadFile JSON with values array Plain text with line numbers
Find JSON with values array Newline-separated paths
Grep JSON lines path:line:content text (already close)
SearchFiles JSON lines Same as Grep
PreviewEdit JSON with diff + patchId Diff as plain text, patchId as a labelled field

Tools like EditFile, CreateFile, Exec return structured results with distinct fields the system acts on — those stay as-is.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions