OCaml Claude SDK using Eio and Jsont
at main 155 lines 5.0 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6module J = Jsont.Json 7 8type t = { 9 name : string; 10 version : string; 11 tools : Tool.t list; 12 tool_map : (string, Tool.t) Hashtbl.t; 13} 14 15let create ~name ?(version = "1.0.0") ~tools () = 16 let tool_map = Hashtbl.create (List.length tools) in 17 List.iter (fun tool -> Hashtbl.add tool_map (Tool.name tool) tool) tools; 18 { name; version; tools; tool_map } 19 20let name t = t.name 21let version t = t.version 22let tools t = t.tools 23 24(* JSONRPC helpers using Jsont.Json builders *) 25 26let jsonrpc_success ~id result = 27 J.object' 28 [ 29 J.mem (J.name "jsonrpc") (J.string "2.0"); 30 J.mem (J.name "id") id; 31 J.mem (J.name "result") result; 32 ] 33 34let jsonrpc_error ~id ~code ~message = 35 J.object' 36 [ 37 J.mem (J.name "jsonrpc") (J.string "2.0"); 38 J.mem (J.name "id") id; 39 J.mem (J.name "error") 40 (J.object' 41 [ 42 J.mem (J.name "code") (J.number (Float.of_int code)); 43 J.mem (J.name "message") (J.string message); 44 ]); 45 ] 46 47(* Extract string from JSON *) 48let get_string key (obj : Jsont.json) = 49 match obj with 50 | Jsont.Object (mems, _) -> ( 51 match J.find_mem key mems with 52 | Some (_, Jsont.String (s, _)) -> Some s 53 | _ -> None) 54 | _ -> None 55 56(* Extract object from JSON *) 57let get_object key (obj : Jsont.json) : Jsont.json option = 58 match obj with 59 | Jsont.Object (mems, _) -> ( 60 match J.find_mem key mems with 61 | Some (_, (Jsont.Object _ as o)) -> Some o 62 | _ -> None) 63 | _ -> None 64 65(* Get ID from JSON message *) 66let get_id (msg : Jsont.json) : Jsont.json = 67 match msg with 68 | Jsont.Object (mems, _) -> ( 69 match J.find_mem "id" mems with Some (_, id) -> id | None -> J.null ()) 70 | _ -> J.null () 71 72(* Handle initialize request *) 73let handle_initialize t ~id = 74 jsonrpc_success ~id 75 (J.object' 76 [ 77 J.mem (J.name "protocolVersion") (J.string "2024-11-05"); 78 J.mem (J.name "capabilities") 79 (J.object' [ J.mem (J.name "tools") (J.object' []) ]); 80 J.mem (J.name "serverInfo") 81 (J.object' 82 [ 83 J.mem (J.name "name") (J.string t.name); 84 J.mem (J.name "version") (J.string t.version); 85 ]); 86 ]) 87 88(* Handle tools/list request *) 89let handle_tools_list t ~id = 90 let tools_json = 91 List.map 92 (fun tool -> 93 J.object' 94 [ 95 J.mem (J.name "name") (J.string (Tool.name tool)); 96 J.mem (J.name "description") (J.string (Tool.description tool)); 97 J.mem (J.name "inputSchema") (Tool.input_schema tool); 98 ]) 99 t.tools 100 in 101 jsonrpc_success ~id (J.object' [ J.mem (J.name "tools") (J.list tools_json) ]) 102 103(* Handle tools/call request *) 104let handle_tools_call t ~id ~params = 105 match get_string "name" params with 106 | None -> jsonrpc_error ~id ~code:(-32602) ~message:"Missing 'name' parameter" 107 | Some tool_name -> ( 108 match Hashtbl.find_opt t.tool_map tool_name with 109 | None -> 110 jsonrpc_error ~id ~code:(-32601) 111 ~message:(Printf.sprintf "Tool '%s' not found" tool_name) 112 | Some tool -> ( 113 let arguments = 114 match get_object "arguments" params with 115 | Some args -> args 116 | None -> J.object' [] 117 in 118 let input = Tool_input.of_json arguments in 119 match Tool.call tool input with 120 | Ok content -> 121 jsonrpc_success ~id 122 (J.object' [ J.mem (J.name "content") content ]) 123 | Error msg -> 124 (* Return error as content with is_error flag *) 125 jsonrpc_success ~id 126 (J.object' 127 [ 128 J.mem (J.name "content") 129 (J.list 130 [ 131 J.object' 132 [ 133 J.mem (J.name "type") (J.string "text"); 134 J.mem (J.name "text") (J.string msg); 135 ]; 136 ]); 137 J.mem (J.name "isError") (J.bool true); 138 ]))) 139 140let handle_request t ~method_ ~params ~id = 141 match method_ with 142 | "initialize" -> handle_initialize t ~id 143 | "tools/list" -> handle_tools_list t ~id 144 | "tools/call" -> handle_tools_call t ~id ~params 145 | _ -> 146 jsonrpc_error ~id ~code:(-32601) 147 ~message:(Printf.sprintf "Method '%s' not found" method_) 148 149let handle_json_message t (msg : Jsont.json) = 150 let method_ = match get_string "method" msg with Some m -> m | None -> "" in 151 let params = 152 match get_object "params" msg with Some p -> p | None -> J.object' [] 153 in 154 let id = get_id msg in 155 handle_request t ~method_ ~params ~id