ClaudeAgentSDK.ControlProtocol.Protocol (claude_agent_sdk v0.6.9)
View SourceControl protocol message encoding and decoding.
Handles bidirectional communication with Claude CLI via control messages:
- Initialize requests with hooks configuration
- Hook callback requests/responses
- Control requests/responses
Messages are exchanged as JSON over stdin/stdout.
Message Types
SDK → CLI
control_requestwithinitializesubtypecontrol_responsefor hook callbacks
CLI → SDK
control_requestwithhook_callbacksubtypecontrol_responsefor initialize
Summary
Functions
Checks if a message is a control protocol message.
Decodes a message from CLI.
Decodes a set_model control response.
Encodes a hook callback response.
Encodes an initialize request with hooks configuration and SDK MCP servers.
Encodes an interrupt control request.
Encodes a rewind_files control request.
Encodes a set_model control request.
Encodes a set_permission_mode control request.
Generates a unique request ID.
Types
@type message_type() ::
:control_request
| :control_response
| :control_cancel_request
| :sdk_message
| :stream_event
Message type classifier.
@type request_id() :: String.t()
Request ID for tracking control protocol requests.
Functions
Checks if a message is a control protocol message.
Parameters
message- Decoded message map
Returns
true if control message, false otherwise
Examples
iex> Protocol.control_message?(%{"type" => "control_request"})
true
iex> Protocol.control_message?(%{"type" => "assistant"})
false
@spec decode_message(String.t()) :: {:ok, {message_type(), map()}} | {:error, term()}
Decodes a message from CLI.
Parses JSON and classifies message type.
Parameters
json_string- JSON message from CLI
Returns
{:ok, {message_type, data}}- Successfully decoded{:error, reason}- Failed to decode
Examples
iex> json = ~s({"type":"control_request","request_id":"req_1","request":{}})
iex> {:ok, {type, _data}} = Protocol.decode_message(json)
iex> type
:control_request
Decodes a set_model control response.
@spec encode_hook_response(request_id(), map() | String.t(), :success | :error) :: String.t()
Encodes a hook callback response.
Sends the result of a hook callback execution back to CLI.
Parameters
request_id- Request ID from CLI's hook_callback requestoutput_or_error- Hook output map or error stringstatus-:successor:error
Returns
JSON string ready to send to CLI
Examples
# Success
output = %{hookSpecificOutput: %{permissionDecision: "allow"}}
json = Protocol.encode_hook_response("req_123", output, :success)
# Error
json = Protocol.encode_hook_response("req_456", "Timeout", :error)
@spec encode_initialize_request(map() | nil, map() | nil, request_id() | nil) :: {request_id(), String.t()}
Encodes an initialize request with hooks configuration and SDK MCP servers.
Sends hooks configuration and SDK MCP server info to CLI during initialization so it knows which callbacks to invoke and which SDK servers are available.
Parameters
hooks_config- Hooks configuration map (from build_hooks_config)sdk_mcp_servers- Map of server_name => server_info for SDK servers (optional)request_id- Optional request ID (generated if nil)
Returns
{request_id, json_string} tuple
Examples
hooks = %{
"PreToolUse" => [
%{"matcher" => "Bash", "hookCallbackIds" => ["hook_0"]}
]
}
sdk_servers = %{
"math-tools" => %{"name" => "math-tools", "version" => "1.0.0"}
}
{id, json} = Protocol.encode_initialize_request(hooks, sdk_servers, nil)
@spec encode_interrupt_request(request_id() | nil) :: {request_id(), String.t()}
Encodes an interrupt control request.
@spec encode_rewind_files_request(String.t(), request_id() | nil) :: {request_id(), String.t()}
Encodes a rewind_files control request.
Returns {request_id, json}.
@spec encode_set_model_request(String.t(), request_id() | nil) :: {request_id(), String.t()}
Encodes a set_model control request.
Returns {request_id, json}.
@spec encode_set_permission_mode_request(String.t(), request_id() | nil) :: {request_id(), String.t()}
Encodes a set_permission_mode control request.
Returns {request_id, json}.
@spec generate_request_id() :: request_id()
Generates a unique request ID.
Format: req_{counter}_{random_hex}
Examples
iex> id = Protocol.generate_request_id()
iex> String.starts_with?(id, "req_")
true