OCaml Claude SDK using Eio and Jsont
at main 250 lines 8.0 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** Control protocol wire format for SDK communication. 7 8 This module defines the wire format for the SDK control protocol used for 9 bidirectional communication between the SDK and the Claude CLI. It handles 10 JSON serialization and deserialization of control messages. 11 12 The control protocol enables: 13 - Permission requests for tool usage authorization 14 - Hook callbacks for intercepting and modifying tool execution 15 - Dynamic control for changing settings mid-conversation 16 - Server introspection for querying capabilities *) 17 18(** {1 Request Types} *) 19 20module Request : sig 21 (** SDK control request types. *) 22 23 type permission_r = private { 24 tool_name : string; 25 input : Jsont.json; 26 permission_suggestions : Permissions.Update.t list option; 27 blocked_path : string option; 28 unknown : Unknown.t; 29 } 30 31 type initialize_r = private { 32 hooks : (string * Jsont.json) list option; 33 unknown : Unknown.t; 34 } 35 36 type set_permission_mode_r = private { 37 mode : Permissions.Mode.t; 38 unknown : Unknown.t; 39 } 40 41 type hook_callback_r = private { 42 callback_id : string; 43 input : Jsont.json; 44 tool_use_id : string option; 45 unknown : Unknown.t; 46 } 47 48 type mcp_message_r = private { 49 server_name : string; 50 message : Jsont.json; 51 unknown : Unknown.t; 52 } 53 54 type set_model_r = private { model : string; unknown : Unknown.t } 55 56 type t = 57 | Interrupt 58 | Permission of permission_r 59 | Initialize of initialize_r 60 | Set_permission_mode of set_permission_mode_r 61 | Hook_callback of hook_callback_r 62 | Mcp_message of mcp_message_r 63 | Set_model of set_model_r 64 | Get_server_info 65 (** The type of SDK control requests. Wire format uses "subtype" field: 66 "interrupt", "canUseTool", "initialize", "setPermissionMode", 67 "hookCallback", "mcpMessage", "setModel", "getServerInfo". *) 68 69 val jsont : t Jsont.t 70 (** [jsont] is the Jsont codec for requests. *) 71 72 val interrupt : unit -> t 73 (** [interrupt ()] creates an interrupt request. *) 74 75 val permission : 76 tool_name:string -> 77 input:Jsont.json -> 78 ?permission_suggestions:Permissions.Update.t list -> 79 ?blocked_path:string -> 80 unit -> 81 t 82 (** [permission ~tool_name ~input ?permission_suggestions ?blocked_path ()] 83 creates a permission request. *) 84 85 val initialize : ?hooks:(string * Jsont.json) list -> unit -> t 86 (** [initialize ?hooks ()] creates an initialize request. *) 87 88 val set_permission_mode : mode:Permissions.Mode.t -> unit -> t 89 (** [set_permission_mode ~mode ()] creates a permission mode change request. 90 *) 91 92 val hook_callback : 93 callback_id:string -> input:Jsont.json -> ?tool_use_id:string -> unit -> t 94 (** [hook_callback ~callback_id ~input ?tool_use_id ()] creates a hook 95 callback request. *) 96 97 val mcp_message : server_name:string -> message:Jsont.json -> unit -> t 98 (** [mcp_message ~server_name ~message ()] creates an MCP message request. *) 99 100 val set_model : model:string -> unit -> t 101 (** [set_model ~model ()] creates a model change request. *) 102 103 val get_server_info : unit -> t 104 (** [get_server_info ()] creates a server info request. *) 105end 106 107(** {1 Response Types} *) 108 109module Response : sig 110 (** SDK control response types. *) 111 112 (** Standard JSON-RPC 2.0 error codes. 113 114 These codes follow the JSON-RPC 2.0 specification for structured error 115 responses. Using the typed codes instead of raw integers improves code 116 clarity and prevents typos. Polymorphic variants allow for easy extension. 117 *) 118 module Error_code : sig 119 type t = 120 [ `Parse_error (** -32700: Invalid JSON received *) 121 | `Invalid_request (** -32600: The request object is invalid *) 122 | `Method_not_found (** -32601: The requested method does not exist *) 123 | `Invalid_params (** -32602: Invalid method parameters *) 124 | `Internal_error (** -32603: Internal server error *) 125 | `Custom of int (** Application-specific error codes *) ] 126 127 val to_int : [< t ] -> int 128 (** [to_int t] converts an error code to its integer representation. *) 129 130 val of_int : int -> t 131 (** [of_int n] converts an integer to an error code. Standard codes are 132 mapped to their variants, others become [`Custom n]. *) 133 end 134 135 type error_detail = { 136 code : int; (** Error code for programmatic handling *) 137 message : string; (** Human-readable error message *) 138 data : Jsont.json option; (** Optional additional error data *) 139 } 140 (** Structured error detail similar to JSON-RPC. *) 141 142 val error_detail : 143 code:[< Error_code.t ] -> 144 message:string -> 145 ?data:Jsont.json -> 146 unit -> 147 error_detail 148 (** [error_detail ~code ~message ?data ()] creates a structured error detail 149 using typed error codes. 150 151 Example: 152 {[ 153 error_detail ~code:`Method_not_found ~message:"Hook callback not found" 154 () 155 ]} *) 156 157 val error_detail_jsont : error_detail Jsont.t 158 (** [error_detail_jsont] is the Jsont codec for error details. *) 159 160 type success_r = private { 161 request_id : string; 162 response : Jsont.json option; 163 unknown : Unknown.t; 164 } 165 166 type error_r = private { 167 request_id : string; 168 error : error_detail; 169 unknown : Unknown.t; 170 } 171 172 type t = 173 | Success of success_r 174 | Error of error_r 175 (** The type of SDK control responses. Wire format uses "subtype" field: 176 "success", "error". *) 177 178 val jsont : t Jsont.t 179 (** [jsont] is the Jsont codec for responses. *) 180 181 val success : request_id:string -> ?response:Jsont.json -> unit -> t 182 (** [success ~request_id ?response ()] creates a success response. *) 183 184 val error : request_id:string -> error:error_detail -> unit -> t 185 (** [error ~request_id ~error ()] creates an error response with structured 186 error detail. *) 187end 188 189(** {1 Control Envelopes} *) 190 191type request_envelope = { 192 request_id : string; 193 request : Request.t; 194 unknown : Unknown.t; 195} 196(** Control request envelope. Wire format has "type": "control_request". *) 197 198type response_envelope = { response : Response.t; unknown : Unknown.t } 199(** Control response envelope. Wire format has "type": "control_response". *) 200 201val request_envelope_jsont : request_envelope Jsont.t 202(** [request_envelope_jsont] is the Jsont codec for request envelopes. *) 203 204val response_envelope_jsont : response_envelope Jsont.t 205(** [response_envelope_jsont] is the Jsont codec for response envelopes. *) 206 207val create_request : 208 request_id:string -> request:Request.t -> unit -> request_envelope 209(** [create_request ~request_id ~request ()] creates a control request envelope. 210*) 211 212val create_response : response:Response.t -> unit -> response_envelope 213(** [create_response ~response ()] creates a control response envelope. *) 214 215(** {1 Server Information} *) 216 217module Server_info : sig 218 (** Server information and capabilities. *) 219 220 type t 221 (** Server metadata and capabilities. *) 222 223 val jsont : t Jsont.t 224 (** [jsont] is the Jsont codec for server info. *) 225 226 val create : 227 version:string -> 228 capabilities:string list -> 229 commands:string list -> 230 output_styles:string list -> 231 unit -> 232 t 233 (** [create ~version ~capabilities ~commands ~output_styles ()] creates server 234 info. *) 235 236 val version : t -> string 237 (** [version t] returns the server version. *) 238 239 val capabilities : t -> string list 240 (** [capabilities t] returns the server capabilities. *) 241 242 val commands : t -> string list 243 (** [commands t] returns available commands. *) 244 245 val output_styles : t -> string list 246 (** [output_styles t] returns available output styles. *) 247 248 val unknown : t -> Unknown.t 249 (** [unknown t] returns the unknown fields. *) 250end