My agentic slop goes here. Not intended for anyone else!
at jsont 13 kB view raw
1open Ezjsonm 2 3let src = Logs.Src.create "claude.sdk_control" ~doc:"Claude SDK control protocol" 4module Log = (val Logs.src_log src : Logs.LOG) 5 6module JU = Json_utils 7 8module Request = struct 9 type interrupt = { 10 subtype : [`Interrupt]; 11 } 12 13 type permission = { 14 subtype : [`Can_use_tool]; 15 tool_name : string; 16 input : value; 17 permission_suggestions : Permissions.Update.t list option; 18 blocked_path : string option; 19 } 20 21 type initialize = { 22 subtype : [`Initialize]; 23 hooks : (string * value) list option; 24 } 25 26 type set_permission_mode = { 27 subtype : [`Set_permission_mode]; 28 mode : Permissions.Mode.t; 29 } 30 31 type hook_callback = { 32 subtype : [`Hook_callback]; 33 callback_id : string; 34 input : value; 35 tool_use_id : string option; 36 } 37 38 type mcp_message = { 39 subtype : [`Mcp_message]; 40 server_name : string; 41 message : value; 42 } 43 44 type set_model = { 45 subtype : [`Set_model]; 46 model : string; 47 } 48 49 type get_server_info = { 50 subtype : [`Get_server_info]; 51 } 52 53 type t = 54 | Interrupt of interrupt 55 | Permission of permission 56 | Initialize of initialize 57 | Set_permission_mode of set_permission_mode 58 | Hook_callback of hook_callback 59 | Mcp_message of mcp_message 60 | Set_model of set_model 61 | Get_server_info of get_server_info 62 63 let interrupt () = Interrupt { subtype = `Interrupt } 64 65 let permission ~tool_name ~input ?permission_suggestions ?blocked_path () = 66 Permission { 67 subtype = `Can_use_tool; 68 tool_name; 69 input; 70 permission_suggestions; 71 blocked_path; 72 } 73 74 let initialize ?hooks () = 75 Initialize { subtype = `Initialize; hooks } 76 77 let set_permission_mode ~mode = 78 Set_permission_mode { subtype = `Set_permission_mode; mode } 79 80 let hook_callback ~callback_id ~input ?tool_use_id () = 81 Hook_callback { 82 subtype = `Hook_callback; 83 callback_id; 84 input; 85 tool_use_id; 86 } 87 88 let mcp_message ~server_name ~message = 89 Mcp_message { 90 subtype = `Mcp_message; 91 server_name; 92 message; 93 } 94 95 let set_model ~model = 96 Set_model { subtype = `Set_model; model } 97 98 let get_server_info () = 99 Get_server_info { subtype = `Get_server_info } 100 101 let to_json = function 102 | Interrupt _ -> 103 `O [("subtype", `String "interrupt")] 104 | Permission p -> 105 let fields = [ 106 ("subtype", `String "can_use_tool"); 107 ("tool_name", `String p.tool_name); 108 ("input", p.input); 109 ] in 110 let fields = match p.permission_suggestions with 111 | Some suggestions -> 112 ("permission_suggestions", 113 `A (List.map Permissions.Update.to_json suggestions)) :: fields 114 | None -> fields 115 in 116 let fields = match p.blocked_path with 117 | Some path -> ("blocked_path", `String path) :: fields 118 | None -> fields 119 in 120 `O fields 121 | Initialize i -> 122 let fields = [("subtype", `String "initialize")] in 123 let fields = match i.hooks with 124 | Some hooks -> 125 ("hooks", `O hooks) :: fields 126 | None -> fields 127 in 128 `O fields 129 | Set_permission_mode s -> 130 `O [ 131 ("subtype", `String "set_permission_mode"); 132 ("mode", Permissions.Mode.to_json s.mode); 133 ] 134 | Hook_callback h -> 135 let fields = [ 136 ("subtype", `String "hook_callback"); 137 ("callback_id", `String h.callback_id); 138 ("input", h.input); 139 ] in 140 let fields = match h.tool_use_id with 141 | Some id -> ("tool_use_id", `String id) :: fields 142 | None -> fields 143 in 144 `O fields 145 | Mcp_message m -> 146 `O [ 147 ("subtype", `String "mcp_message"); 148 ("server_name", `String m.server_name); 149 ("message", m.message); 150 ] 151 | Set_model s -> 152 `O [ 153 ("subtype", `String "set_model"); 154 ("model", `String s.model); 155 ] 156 | Get_server_info _ -> 157 `O [("subtype", `String "get_server_info")] 158 159 let of_json = function 160 | `O fields -> 161 let subtype = JU.assoc_string "subtype" fields in 162 (match subtype with 163 | "interrupt" -> 164 Interrupt { subtype = `Interrupt } 165 | "can_use_tool" -> 166 let tool_name = JU.assoc_string "tool_name" fields in 167 let input = List.assoc "input" fields in 168 let permission_suggestions = 169 match List.assoc_opt "permission_suggestions" fields with 170 | Some (`A lst) -> 171 Some (List.map Permissions.Update.of_json lst) 172 | _ -> None 173 in 174 let blocked_path = JU.assoc_string_opt "blocked_path" fields in 175 Permission { 176 subtype = `Can_use_tool; 177 tool_name; 178 input; 179 permission_suggestions; 180 blocked_path; 181 } 182 | "initialize" -> 183 let hooks = 184 match List.assoc_opt "hooks" fields with 185 | Some (`O hooks) -> Some hooks 186 | _ -> None 187 in 188 Initialize { subtype = `Initialize; hooks } 189 | "set_permission_mode" -> 190 let mode = List.assoc "mode" fields |> Permissions.Mode.of_json in 191 Set_permission_mode { subtype = `Set_permission_mode; mode } 192 | "hook_callback" -> 193 let callback_id = JU.assoc_string "callback_id" fields in 194 let input = List.assoc "input" fields in 195 let tool_use_id = JU.assoc_string_opt "tool_use_id" fields in 196 Hook_callback { 197 subtype = `Hook_callback; 198 callback_id; 199 input; 200 tool_use_id; 201 } 202 | "mcp_message" -> 203 let server_name = JU.assoc_string "server_name" fields in 204 let message = List.assoc "message" fields in 205 Mcp_message { 206 subtype = `Mcp_message; 207 server_name; 208 message; 209 } 210 | "set_model" -> 211 let model = JU.assoc_string "model" fields in 212 Set_model { subtype = `Set_model; model } 213 | "get_server_info" -> 214 Get_server_info { subtype = `Get_server_info } 215 | _ -> raise (Invalid_argument ("Unknown request subtype: " ^ subtype))) 216 | _ -> raise (Invalid_argument "Request.of_json: expected object") 217 218 let pp fmt = function 219 | Interrupt _ -> 220 Fmt.pf fmt "@[<2>Interrupt@]" 221 | Permission p -> 222 Fmt.pf fmt "@[<2>Permission@ { tool = %S;@ blocked_path = %a }@]" 223 p.tool_name Fmt.(option string) p.blocked_path 224 | Initialize i -> 225 Fmt.pf fmt "@[<2>Initialize@ { hooks = %s }@]" 226 (if Option.is_some i.hooks then "present" else "none") 227 | Set_permission_mode s -> 228 Fmt.pf fmt "@[<2>SetPermissionMode@ { mode = %a }@]" 229 Permissions.Mode.pp s.mode 230 | Hook_callback h -> 231 Fmt.pf fmt "@[<2>HookCallback@ { id = %S;@ tool_use_id = %a }@]" 232 h.callback_id Fmt.(option string) h.tool_use_id 233 | Mcp_message m -> 234 Fmt.pf fmt "@[<2>McpMessage@ { server = %S }@]" 235 m.server_name 236 | Set_model s -> 237 Fmt.pf fmt "@[<2>SetModel@ { model = %S }@]" s.model 238 | Get_server_info _ -> 239 Fmt.pf fmt "@[<2>GetServerInfo@]" 240end 241 242module Response = struct 243 type success = { 244 subtype : [`Success]; 245 request_id : string; 246 response : value option; 247 } 248 249 type error = { 250 subtype : [`Error]; 251 request_id : string; 252 error : string; 253 } 254 255 type t = 256 | Success of success 257 | Error of error 258 259 let success ~request_id ?response () = 260 Success { 261 subtype = `Success; 262 request_id; 263 response; 264 } 265 266 let error ~request_id ~error = 267 Error { 268 subtype = `Error; 269 request_id; 270 error; 271 } 272 273 let to_json = function 274 | Success s -> 275 let fields = [ 276 ("subtype", `String "success"); 277 ("request_id", `String s.request_id); 278 ] in 279 let fields = match s.response with 280 | Some resp -> ("response", resp) :: fields 281 | None -> fields 282 in 283 `O fields 284 | Error e -> 285 `O [ 286 ("subtype", `String "error"); 287 ("request_id", `String e.request_id); 288 ("error", `String e.error); 289 ] 290 291 let of_json = function 292 | `O fields -> 293 let subtype = JU.assoc_string "subtype" fields in 294 let request_id = JU.assoc_string "request_id" fields in 295 (match subtype with 296 | "success" -> 297 let response = List.assoc_opt "response" fields in 298 Success { 299 subtype = `Success; 300 request_id; 301 response; 302 } 303 | "error" -> 304 let error = JU.assoc_string "error" fields in 305 Error { 306 subtype = `Error; 307 request_id; 308 error; 309 } 310 | _ -> raise (Invalid_argument ("Unknown response subtype: " ^ subtype))) 311 | _ -> raise (Invalid_argument "Response.of_json: expected object") 312 313 let pp fmt = function 314 | Success s -> 315 Fmt.pf fmt "@[<2>Success@ { request_id = %S;@ response = %s }@]" 316 s.request_id (if Option.is_some s.response then "present" else "none") 317 | Error e -> 318 Fmt.pf fmt "@[<2>Error@ { request_id = %S;@ error = %S }@]" 319 e.request_id e.error 320end 321 322type control_request = { 323 type_ : [`Control_request]; 324 request_id : string; 325 request : Request.t; 326} 327 328type control_response = { 329 type_ : [`Control_response]; 330 response : Response.t; 331} 332 333type t = 334 | Request of control_request 335 | Response of control_response 336 337let create_request ~request_id ~request = 338 Request { 339 type_ = `Control_request; 340 request_id; 341 request; 342 } 343 344let create_response ~response = 345 Response { 346 type_ = `Control_response; 347 response; 348 } 349 350let to_json = function 351 | Request r -> 352 `O [ 353 ("type", `String "control_request"); 354 ("request_id", `String r.request_id); 355 ("request", Request.to_json r.request); 356 ] 357 | Response r -> 358 `O [ 359 ("type", `String "control_response"); 360 ("response", Response.to_json r.response); 361 ] 362 363let of_json = function 364 | `O fields -> 365 let type_ = JU.assoc_string "type" fields in 366 (match type_ with 367 | "control_request" -> 368 let request_id = JU.assoc_string "request_id" fields in 369 let request = List.assoc "request" fields |> Request.of_json in 370 Request { 371 type_ = `Control_request; 372 request_id; 373 request; 374 } 375 | "control_response" -> 376 let response = List.assoc "response" fields |> Response.of_json in 377 Response { 378 type_ = `Control_response; 379 response; 380 } 381 | _ -> raise (Invalid_argument ("Unknown control type: " ^ type_))) 382 | _ -> raise (Invalid_argument "of_json: expected object") 383 384let pp fmt = function 385 | Request r -> 386 Fmt.pf fmt "@[<2>ControlRequest@ { id = %S;@ request = %a }@]" 387 r.request_id Request.pp r.request 388 | Response r -> 389 Fmt.pf fmt "@[<2>ControlResponse@ { %a }@]" 390 Response.pp r.response 391 392let log_request req = 393 Log.debug (fun m -> m "SDK control request: %a" Request.pp req) 394 395let log_response resp = 396 Log.debug (fun m -> m "SDK control response: %a" Response.pp resp) 397 398(** Server information *) 399module Server_info = struct 400 type t = { 401 version : string; 402 capabilities : string list; 403 commands : string list; 404 output_styles : string list; 405 } 406 407 let create ~version ~capabilities ~commands ~output_styles = 408 { version; capabilities; commands; output_styles } 409 410 let version t = t.version 411 let capabilities t = t.capabilities 412 let commands t = t.commands 413 let output_styles t = t.output_styles 414 415 let of_json = function 416 | `O fields -> 417 let version = JU.assoc_string "version" fields in 418 let capabilities = 419 match List.assoc_opt "capabilities" fields with 420 | Some (`A lst) -> List.map Ezjsonm.get_string lst 421 | _ -> [] 422 in 423 let commands = 424 match List.assoc_opt "commands" fields with 425 | Some (`A lst) -> List.map Ezjsonm.get_string lst 426 | _ -> [] 427 in 428 let output_styles = 429 match List.assoc_opt "outputStyles" fields with 430 | Some (`A lst) -> List.map Ezjsonm.get_string lst 431 | _ -> [] 432 in 433 { version; capabilities; commands; output_styles } 434 | _ -> raise (Invalid_argument "Server_info.of_json: expected object") 435 436 let to_json t = 437 `O [ 438 ("version", `String t.version); 439 ("capabilities", `A (List.map (fun s -> `String s) t.capabilities)); 440 ("commands", `A (List.map (fun s -> `String s) t.commands)); 441 ("outputStyles", `A (List.map (fun s -> `String s) t.output_styles)); 442 ] 443 444 let pp fmt t = 445 Fmt.pf fmt "@[<2>ServerInfo@ { version = %S;@ capabilities = [%a];@ commands = [%a];@ output_styles = [%a] }@]" 446 t.version 447 Fmt.(list ~sep:(any ", ") (quote string)) t.capabilities 448 Fmt.(list ~sep:(any ", ") (quote string)) t.commands 449 Fmt.(list ~sep:(any ", ") (quote string)) t.output_styles 450end