Given a number of recent issues stemming from Claude Code using more
subagents that don't play nicely with our custom mcp server, plus some
slowly diverging behavior, we made the decision to just switch back over
to the built-in tools and do more processing on the tool input/output
There are some downsides, but also a lot of upsides, on the whole, it
feels like a win for now.
Closes#310, #279, #305, #236, #94
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
When the Read tool returns image content (e.g., reading a .png file),
the raw Claude SDK image block was passed through unconverted, causing
ACP clients to fail deserializing the tool call completion event. Route
non-text content through `toAcpContentBlock` to produce the correct ACP
image format.
This PR fixes an issue with `claude-code-acp` where incoming tool
results from the Anthropic SDK would, in several cases, get mapped to
invalid ACP `ContentBlock`s inside of the `toAcpContentUpdate` method.
This prevents ACP clients from being able to properly render tool calls.
Per the [ACP
spec](https://agentclientprotocol.com/protocol/schema#contentblock)
`ContentBlock` can only have a `type` field with a value of: `text |
image | audio | resource | resource_link`. However, several of Claude
Code's tools return results with different `type`'s .
This PR adds a `toAcpContentBlock` method that maps tool call results to
valid ACP `ContentBlock`. It attempts to be defensive and will map
unknown types to a text content block. This avoids breakages if
Anthropic changes the types upstream, or introduces new tools.
Removes the problematic file cache and instead builds the diff out of
the unified diff that gets returned from the edit tool already.
This should solve several error cases and provide a more reliable diff
## Summary
Follow-up to #121. The same issue (invalid locations when `file_path` is
undefined) existed in the `Read` tool case as well.
## Problem
The ACP spec requires `path` as a required field in `ToolCallLocation`.
PR #121 fixed this for `mcp__acp__Read`, but the same issue remained for
the `Read` tool case.
When Claude generates a Read tool call without `file_path`, the current
code produces an invalid notification:
```json
{
"locations": [
{
"line": 0
}
]
}
```
This causes the following error in the client:
```
Error handling notification
{code: -32602, message: 'Invalid params', data: {…}}
```
## Solution
Return an empty `locations` array when `input.file_path` is undefined,
consistent with the fix in #121.
Use the `_meta` field for tool calls to forward:
- the original tool name (ie `Bash` etc)
- the structured output of the tool call
The output is received by using the `PostToolUse` hook from the SDK. The
`tool_call_update` for the tool result is now only sent when both the
hook callback and the original tool result (plain text) have been
received.
Claude Code describes [in its documentation
](https://docs.claude.com/en/api/agent-sdk/typescript#tool-output-types)the
expected structure of the output for each tools. Unfortunately those are
not part of the SDK, and [empirically I've found that there was some
differences](58c3100a36/local-server/src/server/endpoints/sendMessage/acp/clients/claudeCode/types.ts (L265)).
Still I've found that having this structured output was useful for my
client to provide a richer UX. Maybe Zed will too.
I was not sure how to add tests for this change, given the current setup
The ACP spec has the path as a required field for tool call locations,
but I've seen Claude generate read calls without an input path, which
would generate:
```json
{
"content": [],
"kind": "read",
"locations": [
{
"line": 0
}
],
"rawInput": {},
"sessionUpdate": "tool_call",
"status": "pending",
"title": "Read File",
"toolCallId": "toolu_012PzeR5whUjWN4ij2HBubJw"
}
```
And then fail to parse as SessionNotification in the SDK.
This looks more reasonable now, because the code is now rendered in a
monospace font:
<img width="475" height="450" alt="Screenshot 2025-09-15 at 10 58 20 AM"
src="https://github.com/user-attachments/assets/9870436b-ee8f-44cd-b814-b22ad9261ebe"
/>
That said, this definitely would benefit from a horizontal scroll bar on
the code blocks. You can't really read what it's saying without copying
the text out of the block.
Of course, in this particular case the relevant info is all in the
beginning of the error message (namely, that it failed to find the
`old_string`), and this does look overall better than before (where it
was just this big jumble of non-monospaced code), so I think it's worth
merging as-is and then revisiting horizontal scrolling later.
When reporting the edit tool uses, we were accessing a field with a typo
(we should start using Zod, btw), and that caused locations not to be
reported correctly. We also now report all line numbers.
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
- Return a unified diff after applying changes
- Fail if the `old_string` isn't found
- Fail if the `old_string` is empty
- Throw instead of returning succesful content on error
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
When the `terminal` client capability is available, we'll now replace
Claude Code's bash tools with our own that go over ACP. This includes
support for background terminals.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet <bennet@zed.dev>