Skip to content

Runtime communication protocols

This page uses the reverse-engineered cli.renamed.js bundle to answer the cross-cutting protocol question: how do runtime modules communicate, how do agents/subagents coordinate, and how does Claude Code talk to remote servers or hosts?

The short answer is that there is no single universal protocol. Inside the bundled runtime, most module boundaries are ordinary JavaScript calls, async queues, stores, and event emitters. Protocols appear at integration boundaries: MCP uses JSON-RPC-shaped messages, the local background daemon uses newline-delimited JSON over a local socket, IDE/Chrome/Remote Control paths use typed envelopes over persistent transports, task/subagent coordination uses built-in tools plus task-store/inbox events, and model/provider calls use HTTP(S) request/streaming responses.

Source anchors

Semantic aliasAnchorMeaning
BridgeToolCallFrametype:"tool_call"Chrome/IDE bridge sends JSON tool-call envelopes over a WebSocket-style bridge.
BridgePairingRequesttype:"pairing_request"Bridge pairing and device selection use explicit JSON envelope types.
BridgePermissionRequestpermission_requestBridge receives permission prompts as typed JSON messages.
BridgePermissionResponsepermission_responseBridge sends permission decisions back as typed JSON messages.
McpResourcesListMethodresources/listMCP resources are represented as JSON-RPC method schemas.
McpPromptsGetMethodprompts/getMCP prompts use the same method-schema layer.
McpToolsListMethodtools/listMCP tools are listed through a JSON-RPC method.
McpListChangeRetentionkeeping previous tools, failed fields passed as undefinedA failed list-change refresh preserves the prior discovered field instead of replacing it with an empty value.
TaskGetMethodmethod:"tasks/get"Task result/status protocol is method-based and JSON-RPC-shaped.
TaskCancelMethodmethod:"tasks/cancel"Task cancellation uses the same task method family.
ProviderRequestLog[API REQUEST]Provider/API request logging surface for outbound HTTP calls.
ProviderEventStreamDetectiontext/event-streamStreaming HTTP responses are detected as event streams.
ProviderRequestIdHeaderx-client-request-idProvider/API requests carry request IDs through HTTP headers.
RemoteSessionTransportsendMessage, cancelRequest, disconnect, sendControlRequestRemote-session wrapper exposes a message/control transport API to the app.
TaskToolProtocolSurfaceTaskCreate, TaskGet, TaskList, TaskUpdateAgent/task coordination is surfaced as tool/action constants.
AgentPeerMessageActionSendMessagePeer/agent message sending is a first-class tool/action constant.
IdeBridgeTransportsws://, http://.../sseIDE integration accepts WebSocket or HTTP SSE endpoints.
ControlRequestFramecontrol_requestHeadless/SDK/Remote Control ask path is a typed control frame.
SandboxPermissionFramesandbox_permission_requestSandbox network/file approvals are typed remote/control envelopes.
InboxPermissionBoundary[InboxPoller] Dropping team_permission_update message: permission rules are never accepted from the inboxInbox transport is not trusted to mutate permission rules.
PlanApprovalRequestFrameplan_approval_requestPlan approval is another explicit control-envelope subtype.
WebSocketAuthFdCLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTORRemote/bridge ingress can read WebSocket auth from an inherited file descriptor.
ExternalProtocolTransportLabelsstdio, sse, httpTransport labels for external protocol adapters.
RemoteSessionConfigInjectionremoteSessionConfigRemote-session configuration is injected into the interactive app.
BridgeStateStreamFramebridge_stateHeadless stream projects bridge state as a first-class frame.
DaemonControlRequestcontrolRequest(e, t), De(e) + "\n"Sends one newline-delimited JSON request and resolves the first response line; default timeout is 5 seconds.
DaemonProtocolVersionBG_PROTO = 1, BG_PROTO_MIN = 1, EPROTOLocal daemon client/server compatibility is explicitly versioned and fail-fast.
DaemonControlAuthdaemon control key checks for dispatch, reply, attach, and permission-responseSensitive local operations require more than a valid request shape.

Protocol matrix

BoundaryPrimary mechanismData shapeNotes
In-bundle modulesJavaScript calls, async functions, stores, event emitters, queuesRuntime objectsThe bundled cli.renamed.js is one process and one large module graph; logical modules are semantic boundaries, not wire protocols.
Built-in toolsTool definitions plus permission boundaryTool-use input/output objectsModel tool calls become validated tool inputs, then cross the permission/execution boundary.
MCP serversJSON-RPC 2.0-style requests, responses, notificationsmethod, params, id, jsonrpc, result/errorConfirmed by tools/list, tools/call, prompts/list, prompts/get, resources/list, task methods, and JSON-RPC error codes.
Local background daemonNewline-delimited JSON over a local Unix/domain socket{ proto, op, ... } request and { ok, op, ... } response/event objectsOne-shot clients consume the first response line; leases/subscriptions keep the connection open. Protocol version, request size, peer identity where available, and control-key checks constrain the boundary.
Agents/tasks/subagentsBuilt-in task/message tools, task store, hooks, typed notificationsTool inputs plus task records/eventsTaskCreate/TaskGet/TaskList/TaskUpdate and SendMessage are the model-facing protocol surface; hooks expose lifecycle events.
Agent Teams inboxLocked, atomically rewritten per-recipient JSON-array fileOuter {from,text,timestamp,...} envelopes; structured frames serialized inside textLives at ~/.claude/teams/<team>/inboxes/<agent>.json. It is not JSONL or a general authority channel: team_permission_update is always dropped, and accepted control-like messages undergo sender/role validation.
IDE bridgeWebSocket or HTTP SSE endpointJSON framesEndpoint detection accepts ws://... and http://.../sse; bridge frames use explicit type fields.
Chrome/browser bridgeWebSocket-style bridgeJSON frames (connect, tool_call, tool_result, permission_request, permission_response, pairing messages)The bridge has pairing, routing, tool call, result, permission, ping/pong, and device-selection messages.
SDK/headless outputStream-JSON/event projectionTyped framescontrol_request, permission_denied, session_state_changed, transcript_mirror, bridge_state, task_notification, and final result frames share a projection channel.
Remote Control / remote sessionsRemote message/control wrapper + bridge JSON envelopesMessage/control requests, permission responses, session configremoteSessionConfig, sendMessage, sendControlRequest, token/env anchors, and permission envelopes show bidirectional control.
Provider/model APIHTTP(S) plus streaming responsesHTTP headers, JSON bodies, SSE/event-stream chunkstext/event-stream, x-client-request-id, and API request logging confirm streaming HTTP boundaries; Bedrock can use Amazon event streams.

In-process module communication

Most named modules in the wiki — runtime lifecycle, context assembler, tool boundary, session store, agents, ops — are logical seams inside one bundled artifact. Their communication is direct and object-based:

flowchart TD
Commander[Commander/root action] --> Settings[settings and policy objects]
Commander --> Session[session envelope/store]
Commander --> Loop[interactive or headless loop]
Loop --> Context[context assembler]
Context --> Provider[provider request]
Loop --> Tools[tool execution boundary]
Tools --> Session
Loop --> Frames[UI / stream-json / remote frames]

This matters because a string such as TaskCreate or control_request is not evidence of a separate daemon by itself. It becomes a protocol only when it crosses an external boundary or is serialized into the headless/bridge stream.

MCP protocol boundary

MCP is the clearest protocol layer in the bundle. The source schemas include JSON-RPC error codes and method names such as:

  • tools/list, tools/call
  • prompts/list, prompts/get
  • resources/list, resources/read, resources/templates/list
  • tasks/get, tasks/list, tasks/result, tasks/cancel
  • cancellation notifications such as notifications/cancelled

The runtime therefore treats MCP as a method-oriented request/response/notification protocol. Transports can vary — the bundle contains references to stdio, sse, and http — but the semantic layer remains JSON-RPC-shaped.

List-change notifications use a last-good-state rule rather than “clear on error”:

  • A failed tools refresh logs that it is keeping previous tools and submits tools: undefined; the state update layer treats undefined as “leave this field unchanged.”
  • A failed prompts refresh likewise retains the previous command projection.
  • Resource refresh treats resources, resource templates, and commands independently, so successful fields can update while only the failed fields retain their prior values.
  • If the outer refresh itself throws, the error is logged and the previous connection state remains in place.

This distinction matters operationally: a transient discovery failure does not make an already-known server appear to have deliberately removed all capabilities.

Local daemon control protocol

Background clients and the supervisor communicate over the local control socket using one JSON object per line:

sequenceDiagram
participant Client as CLI/background client
participant Socket as local control socket
participant Daemon as V1p / FX_ handler
Client->>Socket: JSON({proto: 1, op, ...}) + newline
Socket->>Daemon: one framed request
Daemon->>Daemon: peer, size, proto, schema, auth checks
Daemon-->>Socket: JSON({ok, op, ...}) + newline
Socket-->>Client: first response line (one-shot)

controlRequest() defaults to a 5,000 ms timeout and classifies timeout/connection failures separately from an operation response. The server caps a request at 1 MiB and checks the peer UID where the platform exposes it. Only protocol version 1 is accepted in this build; unsupported versions receive EPROTO and restart guidance so an old daemon is not silently driven with a new schema.

Health/lifecycle operations include ping, nudge, yield, lease, leases, and shutdown; manager operations include list, has, await-ack, dispatch, reply, kill, respawn-stale, resize, attach, ensure-spare, permission-response, and subscribe. Sensitive operations validate the daemon control key. openDaemonLease() and subscribeControl() deliberately keep their sockets open: a lease pins transient-daemon liveness and reconnects after closure, while a subscription consumes a stream of newline-delimited events.

Agent and subagent communication

Agents do not use one universal peer-chat protocol. The visible coordination surface is composed from:

  1. Tools/actions: SendMessage, TaskCreate, TaskGet, TaskList, TaskUpdate.
  2. Task store methods: tasks/get, tasks/result, tasks/list, tasks/cancel.
  3. Hooks and events: SubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle.
  4. Agent Teams inbox envelopes: locked per-recipient files carrying routed messages, validated permission responses/mode changes, plan-approval frames, and mailbox/team-context reminders.
sequenceDiagram
autonumber
participant Main as Main agent
participant Tools as Task/message tools
participant Store as Task store / queues
participant Sub as Subagent runtime
participant Hooks as Hook/event stream
Main->>Tools: TaskCreate / SendMessage
Tools->>Store: create task or enqueue message
Store->>Sub: assign work / provide context
Sub->>Hooks: SubagentStart
Sub->>Store: progress, task result, notifications
Store-->>Main: TaskGet / TaskList / task_notification
Sub->>Hooks: SubagentStop / TaskCompleted

The implication is that “agent-to-agent communication” is tool/state/event mediated. Ordinary live subagents receive process-local pending-message attachments. Experimental teammates receive locked file-mailbox entries and share separate task JSON files. Main uses the priority queue. The dispatcher also retains local/cloud peer transport cases, although their candidate providers return empty lists in this exact artifact. Neither Agent path is just a peer socket; each worker is a runtime context with transcript-backed state, task metadata, and hook-visible lifecycle. Agent messaging and communication follows the complete resolve → transport → receive → reply lifecycle and records that exact-build caveat.

The inbox is also a trust boundary. Its poller unconditionally drops team_permission_update with the explicit reason that permission rules are never accepted from the inbox. Other control-like messages, including permission responses and mode changes, are routed only after sender/role checks; unknown or unrouted protocol frames are dropped. Therefore the existence of a serialized team message type does not prove that the receiver treats it as an authorized state mutation.

Remote, bridge, and provider communication

Remote/server communication splits into several channels:

Provider/model APIs

The model request path is HTTP(S)-based and can stream responses. Anchors include [API REQUEST], x-client-request-id, text/event-stream, and vnd.amazon.eventstream. This supports the model/provider documentation: Anthropic-style streaming uses event-stream responses; Bedrock can expose Amazon event-stream content.

IDE and browser bridges

IDE and Chrome/browser bridges use persistent transport and JSON frames:

  • IDE endpoint discovery accepts ws:// and HTTP .../sse URLs.
  • Browser bridge frames include tool_call, tool_result, permission_request, permission_response, pairing_request, pairing_response, ping, and pong.
  • Permission decisions are sent back with request IDs, so approvals are correlated with pending tool calls.

Remote sessions and Remote Control

Remote sessions inject remoteSessionConfig into the interactive app. Remote Control/bridge paths expose functions with semantic names like sendMessage, cancelRequest, disconnect, and sendControlRequest, and they read auth/token material through anchors such as CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR and CLAUDE_CODE_SESSION_ACCESS_TOKEN.

Remote Control is bidirectional: it can observe output frames, send permission responses, and issue control changes such as interrupt, model/thinking updates, mode changes, and plan approval responses.

Caveats

  • Bundled vendor libraries include gRPC/WebSocket/AWS Smithy/EventStream code. This page treats those as dependency support unless a string is connected to a Claude Code runtime path.
  • Approximate line numbers shift easily because cli.renamed.js is bundled and has very long lines. Use exact strings plus byte offsets for lookup.
  • Some schemas prove protocol shape; they do not prove which transport is selected for a particular user configuration.
  • Local socket access alone should not be described as the complete daemon authorization model: peer-UID checks are platform-dependent and sensitive operations also validate a control key.

Created and maintained by Yingting Huang.