OCaml Claude SDK using Eio and Jsont
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6open Eio.Std
7
8let src = Logs.Src.create "test_permissions" ~doc:"Permission callback test"
9
10module Log = (val Logs.src_log src : Logs.LOG)
11
12(* Simple auto-allow permission callback *)
13let auto_allow_callback ctx =
14 Log.app (fun m ->
15 m "✅ Auto-allowing tool: %s" ctx.Claude.Permissions.tool_name);
16 Claude.Permissions.Decision.allow ()
17
18let run_test ~sw ~env =
19 Log.app (fun m -> m "🧪 Testing Permission Callbacks");
20 Log.app (fun m -> m "================================");
21
22 (* Create options with custom permission callback *)
23 let options =
24 Claude.Options.default
25 |> Claude.Options.with_model (Claude.Model.of_string "sonnet")
26 |> Claude.Options.with_permission_callback auto_allow_callback
27 in
28
29 Log.app (fun m -> m "Creating client with permission callback...");
30 let client =
31 Claude.Client.create ~options ~sw ~process_mgr:env#process_mgr
32 ~clock:env#clock ()
33 in
34
35 (* Simple query that will trigger tool use *)
36 Log.app (fun m -> m "\n📤 Sending test query...");
37 Claude.Client.query client "What is 2 + 2? Just give me the number.";
38
39 (* Process response *)
40 let messages = Claude.Client.receive_all client in
41 Log.app (fun m -> m "\n📨 Received %d messages" (List.length messages));
42
43 List.iter
44 (fun resp ->
45 match resp with
46 | Claude.Response.Text text ->
47 Log.app (fun m -> m "Claude: %s" (Claude.Response.Text.content text))
48 | Claude.Response.Tool_use t ->
49 Log.app (fun m ->
50 m "🔧 Tool use: %s" (Claude.Response.Tool_use.name t))
51 | Claude.Response.Complete result ->
52 Log.app (fun m -> m "✅ Success!");
53 Log.app (fun m ->
54 m "Duration: %dms" (Claude.Response.Complete.duration_ms result))
55 | Claude.Response.Error err ->
56 Log.err (fun m -> m "❌ Error: %s" (Claude.Response.Error.message err))
57 | _ -> ())
58 messages;
59
60 Log.app (fun m -> m "\n================================");
61 Log.app (fun m -> m "✨ Test complete!")
62
63let main ~env = Switch.run @@ fun sw -> run_test ~sw ~env
64
65(* Command-line interface *)
66open Cmdliner
67
68let main_term env =
69 let setup_log style_renderer level =
70 Fmt_tty.setup_std_outputs ?style_renderer ();
71 Logs.set_level level;
72 Logs.set_reporter (Logs_fmt.reporter ());
73 if level = None then Logs.set_level (Some Logs.App);
74 match level with
75 | Some Logs.Info | Some Logs.Debug ->
76 Logs.Src.set_level Claude.Client.src (Some Logs.Info)
77 | _ -> ()
78 in
79 let run style level =
80 setup_log style level;
81 main ~env
82 in
83 Term.(const run $ Fmt_cli.style_renderer () $ Logs_cli.level ())
84
85let cmd env =
86 let doc = "Test permission callback functionality" in
87 let info = Cmd.info "test_permissions" ~version:"1.0" ~doc in
88 Cmd.v info (main_term env)
89
90let () = Eio_main.run @@ fun env -> exit (Cmd.eval (cmd env))