Repository navigation
Added
- Swift 6.1 or later. The MCP Swift SDK 0.12 does not compile with Swift 6.0.
- Package skeleton: the
AgentBinderandAgentBinderNativeproducts. - The Claude driver: stream-json with the control channel (initialize,
can_use_tool, interrupt,set_model), steer, a model probe and rate-limit windows. ToolStatus.cancelled: a tool that was open when the turn or the process ended.- The Codex driver:
codex app-serverover JSON-RPC (threads, turns, steer, interrupt, approvals), a paged model list and a limits read. - The ACP drivers for Copilot, Cursor and Gemini: the handshake with the session load and the authentication, prompts, cancel, permissions, the client file system and the session models.
- The pi driver:
pi --mode rpcwith prompts, steer, abort, permissions,set_modeland a model probe. - The OpenCode drivers:
opencode serveover HTTP and server-sent events, for the 1.x wire (opencode) and the 2.x wire (opencode2). Sessions with the same binary, environment and arguments share one server. The pool stops an idle server after 60 s. - The native loop (
AgentBinderNative):NativeDriverruns a model loop in process with theModelprotocol, tool sets (FileTools,MCPToolSet,FuncTool), approvals, skills, a message store (FileStore,MemoryStore) and compaction (SummaryCompactor). - The native providers:
AnthropicModel(Messages API) andOpenAIModel(Chat Completions API and compatible gateways), onURLSessionwith server-sent events. They retry before the first byte of the response, and a failure is aProviderErrorwith a stablecode. - The live tier (
scripts/test-live.sh): it runs real turns on the local CLI logins and writes a report to.build/live-report.md. Theagentbinder-mcp-helperexecutable is its stdio MCP server. It is not a product.
Known behavior
- Claude: the
initevent has no model name. The model is in the raw frame of the event. - Claude: the pending permission inputs stay after a result and after the exit, as in the vendor wire.
- Codex: an error reply to any request gives an
rpc_errorevent, also when a caller got the error. - Codex: a session that saw one
turn/completedgives no classified error when the process ends in a later turn. - ACP:
usage_updateputs the used context ininputand the context size inoutput. - ACP: a one-shot turn closes the standard input after the prompt.
- OpenCode: an
opencoderequest on a 2.x binary uses the 2.x wire, and its session reportsopencode2. - OpenCode: the result of a turn has no text and no usage. The
usageevents carry the usage. - OpenCode: a stopped or dead server ends each of its sessions with the error code
server_exited. - OpenCode 2.x: an interrupt ends the turn with an
interruptedresult. The aborted step gives no error event. - OpenCode 2.x: the instructions go to the
/experimentalroute of 2.0.26. On a 404 the driver uses the older route. - ACP: each stdio MCP server has
argsandenv, and each HTTP server hasheaders, also when they are empty. Gemini rejects a server withoutenv. - ACP:
authenticatewaits at most 15 s. A stale Cursor login never answers it, so the start fails with the vendor auth error, not a handshake timeout. - OpenCode: the permission mode goes to the
permissionblock ofOPENCODE_CONFIG_CONTENT. Without it OpenCode allows edits and commands with no question. Theinheritmode with no MCP servers sets no config. - ACP: a model list with a repeated id keeps the first entry. Copilot 1.0.71 lists
autotwice. - ACP: an
agent_message_chunkgives an assistant event withdelta: true. The chunks are parts of one message. - Copilot: the MCP servers go in a temp file for
--additional-mcp-config. Copilot 1.0.98 ignores the stdio servers of ACPsession/new, so the driver sends an empty list. - OpenCode 2.x: a permission answer sends
decisionfor 2.0.26 andreplyfor older 2.x builds. - Native: the built-in tools work on the disk.
Features.fileSystemis false, andStartRequest.fileSystemhas no effect. - Native: the
allowed-toolskey of a skill is read, but it does not limit the tools. - Native: an MCP tool call always needs approval, also when the server marks the tool read-only.
Changed on purpose (from the reference behavior)
- Codex: a turn that ends before the
turn/startreply arrives does not stay active. - Codex: an
errornotification with no message takes the error text or its JSON. - Codex: a JSON-RPC error of the model or limits probe names its method, for example
codex model/list: .... - ACP: a resume that the agent refuses throws
resumeUnsupported. It does not open a new session. - ACP: an attachment error or a write error does not leave the turn active.
- ACP: the waiting requests fail at once when the process ends.
- ACP: an idle session that ends with code 0 gives no error.
- ACP: the config key
auth_methodis an alias ofacp_auth_method. - OpenCode: the launch arguments go to
opencode serve, and they are part of the server fingerprint. - OpenCode: the ledger row of a server has the key
<harness>:serve-<pid>. Its sessions do not add rows. - OpenCode: an event stream that does not connect fails the start of the session.
- OpenCode: the stream reconnect wait doubles up to 10 s, and a good connection resets it.
- OpenCode 1.x: the
initevent comes from the driver, not fromsession.created. - OpenCode 1.x: a part text that gets shorter replaces the text. A text of the same length gives no event.
- OpenCode 1.x: a
session.idleevent when no turn runs gives no event. - OpenCode 1.x: a
usageevent comes only when the message has a cost or a completion time. - OpenCode: a close sends an interrupt only when a turn runs.
- OpenCode: a stopped server gives an
errorevent only when a turn runs. - Native: a
FuncToolwith aStringoutput gives the text as it is, not as a JSON string. - Native:
Decision.updatedInputreplaces the input of the tool call. - Native: a permission request gives the status
waiting, and the answer gives the statusrunning. - Native: an MCP tool name has only
[a-zA-Z0-9_-]and is at most 64 characters. A duplicate name gets a_Nsuffix. - Native: the
usageof a result is the sum of all model calls of the turn. - Native: a symlink in the workspace that leads out of the root is refused.
- Native:
promptreturns when the turn starts. It does not wait for the result. - Native: a start with no session id makes a new id.
- Native: a turn that reaches the call limit gives the result
error_max_turnswithisError. - Native:
activate_skillis in the tool list only when the session has skills. - Native: an Anthropic effort sends
thinking: {type: "adaptive"}andoutput_config.effort, not a thinking budget. The current models refusebudget_tokens. The effortxhighis accepted. - Native: the default Anthropic
max_tokensis 64000, not 8192. - Native: Anthropic web search uses the tool
web_search_20260209. - Native: Anthropic prompt caching is on by default (top-level
cache_control). - Native: on the first-party Anthropic endpoint, refusal fallback is on by default for the models that take it (
fallbacks: "default"and the beta header). Afallbackstream part removes the reasoning and the tool calls of the declined attempt. - Native: on the first-party Anthropic endpoint, client tools send
eager_input_streaming: true. - Native: OpenAI sends
max_completion_tokenson api.openai.com andmax_tokenson a gateway. - Native:
OpenAIModelneeds a model name. There is no default. - Native: an Anthropic reasoning part with no signature is not sent back.
Fixed on purpose (from the reference behavior)
- Native: the reasoning keeps its provider signature in the history, so the next call can send it back.
- Native: the tool list keeps the insertion order, so the request bytes stay stable for prompt caching.
- Native: a cancel between tool calls gives each open call a
cancelledresult, so the next prompt works. - Native: compaction does not split a tool call from its result.
- Native: an empty model answer adds no message to the history.
- Native:
bashgets the full environment of the session,PATHtoo. - Native: the glob segment
**matches zero or more segments. - Native:
MCPToolSetfollowsnextCursor, and an HTTP server gets the headers on each request. - Native: a
bashtimeout kills the process group, so a background child cannot hold the output open. - Native: a permission event has the tool call id in
toolUseID. - Native: Anthropic
signature_deltais kept, so the thinking block can go back on the next call. - Native: the assistant message keeps the arrival order of reasoning, text and tool calls.
- Native: consecutive Anthropic messages of the same role merge into one message.
- Native: an Anthropic stream that ends with no stop reason is a failure.
- Native: OpenAI sends
stream_options.include_usage, so the stream reports the usage. - Native: OpenAI sends
tool_choiceonly with tools. - Native: an OpenAI tool call with an empty input sends
"{}". - Native: the providers never read the environment (base URLs, tokens, organization ids).
Live test report
Date: 2026-10-16. macOS 15.7.3. Gemini, pi and Cursor are not run.
| Harness | Version | Case | Result |
|---|---|---|---|
| claude | 2.1.302 (Claude Code) | interrupt | pass |
| claude | 2.1.302 (Claude Code) | limits | pass |
| claude | 2.1.302 (Claude Code) | mcp | pass |
| claude | 2.1.302 (Claude Code) | models | pass |
| claude | 2.1.302 (Claude Code) | one turn | pass |
| claude | 2.1.302 (Claude Code) | permission | pass |
| claude | 2.1.302 (Claude Code) | resume | pass |
| claude | 2.1.302 (Claude Code) | skills | pass |
| claude | 2.1.302 (Claude Code) | steer | pass |
| codex | codex-cli 0.163.0 | interrupt | pass |
| codex | codex-cli 0.163.0 | limits | fail: agentbinder: vendor error -32603: codex account/rateLimits/read: failed to fetch codex rate limits: GET https://chatgpt.com/backend-api/wham/usage failed:… |
| codex | codex-cli 0.163.0 | mcp | fail: check at MCPLiveTests.swift:21 |
| codex | codex-cli 0.163.0 | models | pass |
| codex | codex-cli 0.163.0 | one turn | pass |
| codex | codex-cli 0.163.0 | permission | pass |
| codex | codex-cli 0.163.0 | resume | pass |
| codex | codex-cli 0.163.0 | skills | pass |
| codex | codex-cli 0.163.0 | steer | pass |
| copilot | GitHub Copilot CLI 1.0.98. | interrupt | pass |
| copilot | GitHub Copilot CLI 1.0.98. | mcp | pass |
| copilot | GitHub Copilot CLI 1.0.98. | models | pass |
| copilot | GitHub Copilot CLI 1.0.98. | one turn | pass |
| copilot | GitHub Copilot CLI 1.0.98. | permission | pass |
| copilot | GitHub Copilot CLI 1.0.98. | resume | pass |
| copilot | GitHub Copilot CLI 1.0.98. | skills | pass |
| opencode | opencode v2.0.26 | interrupt | pass |
| opencode | opencode v2.0.26 | mcp | pass |
| opencode | opencode v2.0.26 | models | pass |
| opencode | opencode v2.0.26 | one turn | pass |
| opencode | opencode v2.0.26 | permission | pass |
| opencode | opencode v2.0.26 | resume | pass |
| opencode | opencode v2.0.26 | skills | pass |
| opencode2 | opencode v2.0.26 | interrupt | pass |
| opencode2 | opencode v2.0.26 | mcp | pass |
| opencode2 | opencode v2.0.26 | models | pass |
| opencode2 | opencode v2.0.26 | one turn | pass |
| opencode2 | opencode v2.0.26 | permission | pass |
| opencode2 | opencode v2.0.26 | resume | pass |
| opencode2 | opencode v2.0.26 | skills | pass |
Notes
- Codex
limits: the ChatGPT usage endpoint rejects the local token (401). Turns work. - Codex
mcp: Codex loads the injected server (codex mcp listshows it). The local model (ZAI glm-5.3-Flash) replies with no tool call, also in a plaincodex exec. - Gemini, pi and Cursor: not run for 0.1.0.
See docs/compat.md for the verified versions.