Model Context Protocol in OCaml
1open Mcp_sdk 2open Mcp_rpc 3 4(* Set up the formatter for capturing evaluation output *) 5let capture_output f = 6 let buffer = Buffer.create 1024 in 7 let fmt = Format.formatter_of_buffer buffer in 8 let result = f fmt in 9 Format.pp_print_flush fmt (); 10 (result, Buffer.contents buffer) 11 12(* Helper for extracting string value from JSON *) 13let get_string_param json name = 14 match json with 15 | `Assoc fields -> 16 (match List.assoc_opt name fields with 17 | Some (`String value) -> value 18 | _ -> failwith (Printf.sprintf "Missing or invalid parameter: %s" name)) 19 | _ -> failwith "Expected JSON object" 20 21(* Initialize the OCaml toploop with standard libraries *) 22let initialize_toploop () = 23 (* Initialize the toplevel environment *) 24 Toploop.initialize_toplevel_env (); 25 26 (* Set up the toplevel as if using the standard OCaml REPL *) 27 Clflags.nopervasives := false; 28 Clflags.real_paths := true; 29 Clflags.recursive_types := false; 30 Clflags.strict_sequence := false; 31 Clflags.applicative_functors := true; 32 33 (* Return success message *) 34 "OCaml evaluation environment initialized" 35 36(* Evaluate an OCaml toplevel phrase *) 37let evaluate_phrase phrase = 38 (* Parse the input text as a toplevel phrase *) 39 let lexbuf = Lexing.from_string phrase in 40 41 (* Capture both success/failure status and output *) 42 try 43 let parsed_phrase = !Toploop.parse_toplevel_phrase lexbuf in 44 let (success, output) = capture_output (fun fmt -> 45 Toploop.execute_phrase true fmt parsed_phrase 46 ) in 47 48 (* Return structured result with status and captured output *) 49 if success then 50 `Assoc [ 51 ("success", `Bool true); 52 ("output", `String output); 53 ] 54 else 55 `Assoc [ 56 ("success", `Bool false); 57 ("error", `String "Execution failed"); 58 ("output", `String output); 59 ] 60 with e -> 61 (* Handle parsing or other errors with more detailed messages *) 62 let error_msg = match e with 63 | Syntaxerr.Error err -> 64 let msg = match err with 65 | Syntaxerr.Unclosed _ -> "Syntax error: Unclosed delimiter" 66 | Syntaxerr.Expecting _ -> "Syntax error: Expecting a different token" 67 | Syntaxerr.Not_expecting _ -> "Syntax error: Unexpected token" 68 | Syntaxerr.Applicative_path _ -> "Syntax error: Invalid applicative path" 69 | Syntaxerr.Variable_in_scope _ -> "Syntax error: Variable in scope" 70 | Syntaxerr.Other _ -> "Syntax error" 71 | _ -> "Syntax error (unknown kind)" 72 in 73 msg 74 75 | Lexer.Error (err, _) -> 76 let msg = match err with 77 | Lexer.Illegal_character _ -> "Lexer error: Illegal character" 78 | Lexer.Illegal_escape _ -> "Lexer error: Illegal escape sequence" 79 | Lexer.Unterminated_comment _ -> "Lexer error: Unterminated comment" 80 | Lexer.Unterminated_string -> "Lexer error: Unterminated string" 81 | Lexer.Unterminated_string_in_comment _ -> "Lexer error: Unterminated string in comment" 82 | Lexer.Invalid_literal _ -> "Lexer error: Invalid literal" 83 | _ -> "Lexer error (unknown kind)" 84 in 85 msg 86 | _ -> Printexc.to_string e 87 in 88 `Assoc [ 89 ("success", `Bool false); 90 ("error", `String error_msg); 91 ] 92 93(* Create evaluation server *) 94let server = create_server 95 ~name:"OCaml Evaluation Server" 96 ~version:"0.1.0" () |> 97 fun server -> 98 (* Set default capabilities *) 99 configure_server server ~with_tools:true () 100 101(* Toplevel environment state management *) 102let toplevel_initialized = ref false 103 104(* Initialize OCaml toplevel on first use *) 105let ensure_toploop_initialized () = 106 if not !toplevel_initialized then begin 107 let _ = initialize_toploop () in 108 toplevel_initialized := true; 109 end 110 111(* Register eval tool *) 112let _ = add_tool server 113 ~name:"ocaml_eval" 114 ~description:"Evaluates OCaml toplevel phrases and returns the result" 115 ~schema_properties:[ 116 ("code", "string", "OCaml code to evaluate") 117 ] 118 ~schema_required:["code"] 119 (fun args -> 120 ensure_toploop_initialized (); 121 122 try 123 (* Extract code parameter *) 124 let code = get_string_param args "code" in 125 126 (* Execute the code *) 127 let result = evaluate_phrase code in 128 129 (* Return formatted result *) 130 let success = match result with 131 | `Assoc fields -> ( 132 match List.assoc_opt "success" fields with 133 | Some (`Bool true) -> true 134 | _ -> false 135 ) 136 | _ -> false 137 in 138 139 let output = match result with 140 | `Assoc fields -> ( 141 match List.assoc_opt "output" fields with 142 | Some (`String s) -> s 143 | _ -> ( 144 match List.assoc_opt "error" fields with 145 | Some (`String s) -> s 146 | _ -> "Unknown result" 147 ) 148 ) 149 | _ -> "Unknown result" 150 in 151 152 (* Create a tool result with colorized output *) 153 Tool.create_tool_result [ 154 Mcp.make_text_content output 155 ] ~is_error:(not success) 156 157 with 158 | Failure msg -> 159 Log.error (Printf.sprintf "Error in OCaml eval tool: %s" msg); 160 Tool.create_tool_result [Mcp.make_text_content (Printf.sprintf "Error: %s" msg)] ~is_error:true 161 ) 162 163(* Run the server with the default scheduler *) 164let () = 165 Eio_main.run @@ fun env-> 166 Mcp_server.run_server env server