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