OCaml client library for Claude Code
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-claude: switch all .mli examples to open Claude + idiomatic short paths

The deeply prefixed `Claude.Module.X` style across every line was
hard to read at a glance, especially in the multi-block client.mli
and claude.mli files. Switched to a single `open Claude` at the top
of each {[ ... ]} block and short call sites underneath:

open Claude
let cfg =
Options.default
|> Options.with_model `Sonnet_4_5
|> Options.with_permission_mode Permissions.Mode.Accept_edits

Touches all six claude .mli files I'd added or amended for mdx
coverage (claude.mli, client.mli, options.mli, handler.mli, hooks.mli,
tool.mli, mcp_server.mli). Wired options.mli into the (mdx ...) stanza
since the new examples now compile.

+129 -98
+7 -5
lib/claude.mli
··· 40 40 {1 Quick Start} 41 41 42 42 {[ 43 + open Claude 44 + 43 45 let run () = 44 46 Eio_main.run @@ fun env -> 45 47 let process_mgr = Eio.Stdenv.process_mgr env in 46 48 let clock = Eio.Stdenv.clock env in 47 49 Eio.Switch.run @@ fun sw -> 48 - let client = Claude.Client.v ~sw ~process_mgr ~clock () in 49 - Claude.Client.query client "What is 2+2?"; 50 + let client = Client.v ~sw ~process_mgr ~clock () in 51 + Client.query client "What is 2+2?"; 50 52 let handler = 51 53 object 52 - inherit Claude.Handler.default 53 - method! on_text t = print_endline (Claude.Response.Text.content t) 54 + inherit Handler.default 55 + method! on_text t = print_endline (Response.Text.content t) 54 56 end 55 57 in 56 - Claude.Client.run client ~handler 58 + Client.run client ~handler 57 59 ]} 58 60 59 61 {1 Response Handling}
+42 -33
lib/client.mli
··· 12 12 {2 Basic Usage} 13 13 14 14 {[ 15 + open Claude 16 + 15 17 let run () = 16 18 Eio_main.run @@ fun env -> 17 19 let process_mgr = Eio.Stdenv.process_mgr env in 18 20 let clock = Eio.Stdenv.clock env in 19 21 Eio.Switch.run @@ fun sw -> 20 - let client = Claude.Client.v ~sw ~process_mgr ~clock () in 21 - Claude.Client.query client "What is 2+2?"; 22 - let messages = Claude.Client.receive_all client in 22 + let client = Client.v ~sw ~process_mgr ~clock () in 23 + Client.query client "What is 2+2?"; 24 + let messages = Client.receive_all client in 23 25 List.iter 24 26 (function 25 - | Claude.Response.Text t -> 26 - Fmt.pr "Claude: %s@." (Claude.Response.Text.content t) 27 + | Response.Text t -> 28 + Fmt.pr "Claude: %s@." (Response.Text.content t) 27 29 | _ -> ()) 28 30 messages 29 31 ]} ··· 109 111 110 112 Example: 111 113 {[ 114 + open Claude 115 + 112 116 let respond client = 113 - Claude.Client.respond_to_tools client 117 + Client.respond_to_tools client 114 118 [ 115 119 ("tool_use_123", Json.Value.string "Success", None); 116 120 ("tool_use_456", Json.Value.string "Error occurred", Some true); ··· 135 139 136 140 Example: 137 141 {[ 142 + open Claude 143 + 138 144 let with_handler client = 139 145 let my_handler = 140 146 object 141 - inherit Claude.Handler.default 142 - method! on_text t = print_endline (Claude.Response.Text.content t) 147 + inherit Handler.default 148 + method! on_text t = print_endline (Response.Text.content t) 143 149 144 150 method! on_complete c = 145 151 Fmt.pr "Cost: $%.4f@." 146 152 (Option.value ~default:0.0 147 - (Claude.Response.Complete.total_cost_usd c)) 153 + (Response.Complete.total_cost_usd c)) 148 154 end 149 155 in 150 - Claude.Client.query client "Hello"; 151 - Claude.Client.run client ~handler:my_handler 156 + Client.query client "Hello"; 157 + Client.run client ~handler:my_handler 152 158 ]} *) 153 159 154 160 val receive : t -> Response.t Seq.t ··· 192 198 {2 Example: Adaptive Permission Control} 193 199 194 200 {[ 201 + open Claude 202 + 195 203 let with_adaptive_permissions ~sw ~process_mgr ~clock = 196 204 (* Start with strict permissions. *) 197 205 let client = 198 - Claude.Client.v ~sw ~process_mgr ~clock 206 + Client.v ~sw ~process_mgr ~clock 199 207 ~options: 200 - Claude.Options.( 201 - default |> with_permission_mode Claude.Permissions.Mode.Default) 208 + Options.(default |> with_permission_mode Permissions.Mode.Default) 202 209 () 203 210 in 204 - Claude.Client.query client "Analyze this code"; 205 - let analysis = Claude.Client.receive_all client in 211 + Client.query client "Analyze this code"; 212 + let analysis = Client.receive_all client in 206 213 Fmt.pr "analysis: %d events@." (List.length analysis); 207 214 (* User approves, switch to auto-accept edits. *) 208 - Claude.Client.set_permission_mode client 209 - Claude.Permissions.Mode.Accept_edits; 210 - Claude.Client.query client "Now refactor it"; 211 - let refactor = Claude.Client.receive_all client in 215 + Client.set_permission_mode client Permissions.Mode.Accept_edits; 216 + Client.query client "Now refactor it"; 217 + let refactor = Client.receive_all client in 212 218 Fmt.pr "refactor: %d events@." (List.length refactor) 213 219 ]} 214 220 215 221 {2 Example: Model Switching for Efficiency} 216 222 217 223 {[ 224 + open Claude 225 + 218 226 let with_model_switch ~sw ~process_mgr ~clock = 219 - let sonnet = Claude.Model.of_string "claude-sonnet-4-5" in 220 - let haiku = Claude.Model.of_string "claude-haiku-4" in 227 + let sonnet = Model.of_string "claude-sonnet-4-5" in 228 + let haiku = Model.of_string "claude-haiku-4" in 221 229 (* Use powerful model for complex analysis. *) 222 230 let client = 223 - Claude.Client.v ~sw ~process_mgr ~clock 224 - ~options:Claude.Options.(default |> with_model sonnet) 225 - () 231 + Client.v ~sw ~process_mgr ~clock 232 + ~options:Options.(default |> with_model sonnet) () 226 233 in 227 - Claude.Client.query client "Design a new architecture for this system"; 228 - let design = Claude.Client.receive_all client in 234 + Client.query client "Design a new architecture for this system"; 235 + let design = Client.receive_all client in 229 236 Fmt.pr "design: %d events@." (List.length design); 230 237 (* Switch to faster model for simple tasks. *) 231 - Claude.Client.set_model client haiku; 232 - Claude.Client.query client "Now write a README"; 233 - let readme = Claude.Client.receive_all client in 238 + Client.set_model client haiku; 239 + Client.query client "Now write a README"; 240 + let readme = Client.receive_all client in 234 241 Fmt.pr "readme: %d events@." (List.length readme) 235 242 ]} 236 243 237 244 {2 Example: Server Introspection} 238 245 239 246 {[ 247 + open Claude 248 + 240 249 let print_server_info client = 241 - let info = Claude.Client.server_info client in 242 - Fmt.pr "Claude CLI version: %s@." (Claude.Server_info.version info); 250 + let info = Client.server_info client in 251 + Fmt.pr "Claude CLI version: %s@." (Server_info.version info); 243 252 Fmt.pr "Capabilities: %s@." 244 - (String.concat ", " (Claude.Server_info.capabilities info)) 253 + (String.concat ", " (Server_info.capabilities info)) 245 254 ]} *) 246 255 247 256 val set_permission_mode : t -> Permissions.Mode.t -> unit
+2 -1
lib/dune
··· 10 10 claude.mli 11 11 hooks.mli 12 12 handler.mli 13 - tool.mli) 13 + tool.mli 14 + options.mli) 14 15 (libraries claude nox-json logs fmt eio eio.core eio.unix eio_main))
+28 -20
lib/handler.mli
··· 16 16 methods you care about: 17 17 18 18 {[ 19 + open Claude 20 + 19 21 let my_handler = 20 22 object 21 - inherit Claude.Handler.default 22 - method! on_text t = print_endline (Claude.Response.Text.content t) 23 + inherit Handler.default 24 + method! on_text t = print_endline (Response.Text.content t) 23 25 24 26 method! on_complete c = 25 27 Fmt.pr "Done! Cost: $%.4f@." 26 - (Option.value ~default:0.0 27 - (Claude.Response.Complete.total_cost_usd c)) 28 + (Option.value ~default:0.0 (Response.Complete.total_cost_usd c)) 28 29 end 29 30 ]} 30 31 ··· 32 33 {!abstract} -- omitting any method is a type error. 33 34 34 35 {[ 36 + open Claude 37 + 35 38 let trace_handler = 36 39 object 37 - inherit Claude.Handler.abstract 38 - method on_text t = Fmt.pr "text: %s@." (Claude.Response.Text.content t) 40 + inherit Handler.abstract 41 + method on_text t = Fmt.pr "text: %s@." (Response.Text.content t) 39 42 40 43 method on_tool_use t = 41 - Fmt.pr "tool_use: %s@." (Claude.Response.Tool_use.name t) 44 + Fmt.pr "tool_use: %s@." (Response.Tool_use.name t) 42 45 43 46 method on_tool_result t = 44 47 Fmt.pr "tool_result: %s@." 45 - (Claude.Content_block.Tool_result.tool_use_id t) 48 + (Content_block.Tool_result.tool_use_id t) 46 49 47 50 method on_thinking t = 48 - Fmt.pr "thinking: %s@." (Claude.Response.Thinking.content t) 51 + Fmt.pr "thinking: %s@." (Response.Thinking.content t) 49 52 50 53 method on_init t = 51 54 Fmt.pr "init session: %s@." 52 55 (Option.value ~default:"<no session>" 53 - (Claude.Response.Init.session_id t)) 56 + (Response.Init.session_id t)) 54 57 55 58 method on_error t = 56 - Fmt.pr "error: %s@." (Claude.Response.Error.message t) 59 + Fmt.pr "error: %s@." (Response.Error.message t) 57 60 58 61 method on_complete c = 59 62 Fmt.pr "complete: $%.4f@." 60 - (Option.value ~default:0.0 61 - (Claude.Response.Complete.total_cost_usd c)) 63 + (Option.value ~default:0.0 (Response.Complete.total_cost_usd c)) 62 64 end 63 65 ]} *) 64 66 ··· 106 108 methods you need: 107 109 108 110 {[ 111 + open Claude 112 + 109 113 let handler = 110 114 object 111 - inherit Claude.Handler.default 112 - method! on_text t = Fmt.pr "Text: %s@." (Claude.Response.Text.content t) 115 + inherit Handler.default 116 + method! on_text t = Fmt.pr "Text: %s@." (Response.Text.content t) 113 117 end 114 118 ]} 115 119 ··· 155 159 156 160 Example: 157 161 {[ 162 + open Claude 163 + 158 164 let print_text response = 159 165 let handler = 160 166 object 161 - inherit Claude.Handler.default 162 - method! on_text t = print_endline (Claude.Response.Text.content t) 167 + inherit Handler.default 168 + method! on_text t = print_endline (Response.Text.content t) 163 169 end 164 170 in 165 - Claude.Handler.dispatch handler response 171 + Handler.dispatch handler response 166 172 ]} *) 167 173 168 174 val dispatch_all : #handler -> Response.t list -> unit ··· 173 179 may be more convenient: 174 180 175 181 {[ 182 + open Claude 183 + 176 184 let drain_all client handler = 177 - let responses = Claude.Client.receive_all client in 178 - Claude.Handler.dispatch_all handler responses 185 + let responses = Client.receive_all client in 186 + Handler.dispatch_all handler responses 179 187 ]} *)
+14 -12
lib/hooks.mli
··· 11 11 {1 Example Usage} 12 12 13 13 {[ 14 + open Claude 15 + 14 16 (* Block dangerous bash commands. *) 15 - let block_rm_rf (input : Claude.Hooks.Pre_tool_use.input) = 17 + let block_rm_rf (input : Hooks.Pre_tool_use.input) = 16 18 if input.tool_name = "Bash" then 17 - match Claude.Tool_input.string input.tool_input "command" with 19 + match Tool_input.string input.tool_input "command" with 18 20 | Some cmd 19 - when String.length cmd >= 5 20 - && String.sub cmd 0 5 = "rm -rf" -> 21 - Claude.Hooks.Pre_tool_use.deny ~reason:"Dangerous command" () 22 - | _ -> Claude.Hooks.Pre_tool_use.continue () 23 - else Claude.Hooks.Pre_tool_use.continue () 21 + when String.length cmd >= 5 && String.sub cmd 0 5 = "rm -rf" -> 22 + Hooks.Pre_tool_use.deny ~reason:"Dangerous command" () 23 + | _ -> Hooks.Pre_tool_use.continue () 24 + else Hooks.Pre_tool_use.continue () 24 25 25 26 let hooks = 26 - Claude.Hooks.empty 27 - |> Claude.Hooks.on_pre_tool_use ~pattern:"Bash" block_rm_rf 27 + Hooks.empty |> Hooks.on_pre_tool_use ~pattern:"Bash" block_rm_rf 28 28 ]} *) 29 29 30 30 val src : Logs.Src.t ··· 200 200 201 201 Hooks are configured using a builder pattern: 202 202 {[ 203 + open Claude 204 + 203 205 let configure ~bash_handler ~post_handler = 204 - Claude.Hooks.empty 205 - |> Claude.Hooks.on_pre_tool_use ~pattern:"Bash" bash_handler 206 - |> Claude.Hooks.on_post_tool_use post_handler 206 + Hooks.empty 207 + |> Hooks.on_pre_tool_use ~pattern:"Bash" bash_handler 208 + |> Hooks.on_post_tool_use post_handler 207 209 ]} *) 208 210 209 211 val pp : Format.formatter -> t -> unit
+11 -10
lib/mcp_server.mli
··· 12 12 {2 Basic Usage} 13 13 14 14 {[ 15 + open Claude 16 + 15 17 let greet = 16 - Claude.Tool.v ~name:"greet" ~description:"Greet a user" 18 + Tool.v ~name:"greet" ~description:"Greet a user" 17 19 ~input_schema: 18 - (Claude.Tool.schema_object 19 - [ ("name", Claude.Tool.schema_string) ] 20 + (Tool.schema_object 21 + [ ("name", Tool.schema_string) ] 20 22 ~required:[ "name" ]) 21 23 ~handler:(fun args -> 22 - match Claude.Tool_input.string args "name" with 23 - | Some name -> 24 - Ok (Claude.Tool.text_result (Printf.sprintf "Hello, %s!" name)) 24 + match Tool_input.string args "name" with 25 + | Some name -> Ok (Tool.text_result ("Hello, " ^ name ^ "!")) 25 26 | None -> Error "Missing name") 26 27 27 - let server = Claude.Mcp_server.v ~name:"my-tools" ~tools:[ greet ] () 28 + let server = Mcp_server.v ~name:"my-tools" ~tools:[ greet ] () 28 29 29 30 let options = 30 - Claude.Options.default 31 - |> Claude.Options.with_mcp_server ~name:"tools" server 32 - |> Claude.Options.with_allowed_tools [ "mcp__tools__greet" ] 31 + Options.default 32 + |> Options.with_mcp_server ~name:"tools" server 33 + |> Options.with_allowed_tools [ "mcp__tools__greet" ] 33 34 ]} 34 35 35 36 {2 Tool Naming}
+10 -1
lib/options.mli
··· 26 26 new options value with the specified field updated: 27 27 28 28 {[ 29 + open Claude 30 + 29 31 let options = 30 32 Options.default 31 33 |> Options.with_model `Sonnet_4_5 ··· 38 40 {3 CI/CD: Isolated, Reproducible Builds} 39 41 40 42 {[ 43 + open Claude 44 + 41 45 let ci_config = 42 - Options.default |> Options.with_no_settings (* Ignore user config *) 46 + Options.default 47 + |> Options.with_no_settings (* Ignore user config *) 43 48 |> Options.with_max_budget_usd 0.50 (* 50 cent limit *) 44 49 |> Options.with_permission_mode Permissions.Mode.Bypass_permissions 45 50 |> Options.with_model `Haiku_4 ··· 48 53 {3 Production: Cost Control with Fallback} 49 54 50 55 {[ 56 + open Claude 57 + 51 58 let prod_config = 52 59 Options.default 53 60 |> Options.with_model `Sonnet_4_5 ··· 59 66 {3 Development: User Settings with Overrides} 60 67 61 68 {[ 69 + open Claude 70 + 62 71 let dev_config = 63 72 Options.default 64 73 |> Options.with_max_budget_usd 1.0
+15 -16
lib/tool.mli
··· 10 10 11 11 {2 Basic Usage} 12 12 13 - Use {!schema_object} / {!schema_string} and {!text_result} so the 14 - schema and response shape stay typed instead of constructing the 15 - JSON values by hand: 13 + Use {!schema_object} / {!schema_string} and {!text_result} so the schema and 14 + response shape stay typed instead of constructing the JSON values by hand: 16 15 17 16 {[ 17 + open Claude 18 + 18 19 let greet = 19 - Claude.Tool.v ~name:"greet" ~description:"Greet a user by name" 20 + Tool.v ~name:"greet" ~description:"Greet a user by name" 20 21 ~input_schema: 21 - (Claude.Tool.schema_object 22 - [ ("name", Claude.Tool.schema_string) ] 22 + (Tool.schema_object 23 + [ ("name", Tool.schema_string) ] 23 24 ~required:[ "name" ]) 24 25 ~handler:(fun args -> 25 - match Claude.Tool_input.string args "name" with 26 - | Some name -> 27 - Ok (Claude.Tool.text_result ("Hello, " ^ name ^ "!")) 26 + match Tool_input.string args "name" with 27 + | Some name -> Ok (Tool.text_result ("Hello, " ^ name ^ "!")) 28 28 | None -> Error "Missing 'name' parameter") 29 29 ]} 30 30 31 - Tool handlers return MCP-compatible content via {!text_result} 32 - on success, or [Error message] for error responses. *) 31 + Tool handlers return MCP-compatible content via {!text_result} on success, 32 + or [Error message] for error responses. *) 33 33 34 34 type t 35 35 (** Abstract type for tool definitions. *) ··· 88 88 val schema_object : (string * Json.t) list -> required:string list -> Json.t 89 89 (** [schema_object props ~required] creates an object schema. 90 90 {[ 91 + open Claude 92 + 91 93 let schema = 92 - Claude.Tool.schema_object 93 - [ 94 - ("name", Claude.Tool.schema_string); 95 - ("age", Claude.Tool.schema_int); 96 - ] 94 + Tool.schema_object 95 + [ ("name", Tool.schema_string); ("age", Tool.schema_int) ] 97 96 ~required:[ "name" ] 98 97 ]} *) 99 98