Model Context Protocol in OCaml

Update ocaml_eval_sdk to use consistent content creation API

Fix the OCaml eval SDK to use Mcp.make_text_content instead of direct
TextContent constructor for consistent API usage.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+143 -180
bin
+143 -180
bin/ocaml_eval_sdk.ml
···
-
open Mcp
open Mcp_sdk
open Mcp_message
-
(* Create a server *)
-
let server = create_server
-
~name:"OCaml MCP Structured API Demo"
-
~version:"0.1.0" () |>
-
fun server ->
-
(* Set default capabilities *)
-
configure_server server ~with_tools:true ~with_resources:true ~with_prompts:true ()
+
(* Set up the formatter for capturing evaluation output *)
+
let capture_output f =
+
let buffer = Buffer.create 1024 in
+
let fmt = Format.formatter_of_buffer buffer in
+
let result = f fmt in
+
Format.pp_print_flush fmt ();
+
(result, Buffer.contents buffer)
(* Helper for extracting string value from JSON *)
let get_string_param json name =
···
| `Assoc fields ->
(match List.assoc_opt name fields with
| Some (`String value) -> value
-
| _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name)))
-
| _ -> raise (Failure "Expected JSON object")
+
| _ -> failwith (Printf.sprintf "Missing or invalid parameter: %s" name))
+
| _ -> failwith "Expected JSON object"
+
+
(* Initialize the OCaml toploop with standard libraries *)
+
let initialize_toploop () =
+
(* Initialize the toplevel environment *)
+
Toploop.initialize_toplevel_env ();
+
+
(* Set up the toplevel as if using the standard OCaml REPL *)
+
Clflags.nopervasives := false;
+
Clflags.real_paths := true;
+
Clflags.recursive_types := false;
+
Clflags.strict_sequence := false;
+
Clflags.applicative_functors := true;
+
+
(* Return success message *)
+
"OCaml evaluation environment initialized"
-
(* Register a ping tool that demonstrates the typed API *)
+
(* Evaluate an OCaml toplevel phrase *)
+
let evaluate_phrase phrase =
+
(* Parse the input text as a toplevel phrase *)
+
let lexbuf = Lexing.from_string phrase in
+
+
(* Capture both success/failure status and output *)
+
try
+
let parsed_phrase = !Toploop.parse_toplevel_phrase lexbuf in
+
let (success, output) = capture_output (fun fmt ->
+
Toploop.execute_phrase true fmt parsed_phrase
+
) in
+
+
(* Return structured result with status and captured output *)
+
if success then
+
`Assoc [
+
("success", `Bool true);
+
("output", `String output);
+
]
+
else
+
`Assoc [
+
("success", `Bool false);
+
("error", `String "Execution failed");
+
("output", `String output);
+
]
+
with e ->
+
(* Handle parsing or other errors with more detailed messages *)
+
let error_msg = match e with
+
| Syntaxerr.Error err ->
+
let msg = match err with
+
| Syntaxerr.Unclosed _ -> "Syntax error: Unclosed delimiter"
+
| Syntaxerr.Expecting _ -> "Syntax error: Expecting a different token"
+
| Syntaxerr.Not_expecting _ -> "Syntax error: Unexpected token"
+
| Syntaxerr.Applicative_path _ -> "Syntax error: Invalid applicative path"
+
| Syntaxerr.Variable_in_scope _ -> "Syntax error: Variable in scope"
+
| Syntaxerr.Other _ -> "Syntax error"
+
| _ -> "Syntax error (unknown kind)"
+
in
+
msg
+
+
| Lexer.Error (err, _) ->
+
let msg = match err with
+
| Lexer.Illegal_character _ -> "Lexer error: Illegal character"
+
| Lexer.Illegal_escape _ -> "Lexer error: Illegal escape sequence"
+
| Lexer.Unterminated_comment _ -> "Lexer error: Unterminated comment"
+
| Lexer.Unterminated_string -> "Lexer error: Unterminated string"
+
| Lexer.Unterminated_string_in_comment _ -> "Lexer error: Unterminated string in comment"
+
| Lexer.Invalid_literal _ -> "Lexer error: Invalid literal"
+
| _ -> "Lexer error (unknown kind)"
+
in
+
msg
+
| _ -> Printexc.to_string e
+
in
+
`Assoc [
+
("success", `Bool false);
+
("error", `String error_msg);
+
]
+
+
(* Create evaluation server *)
+
let server = create_server
+
~name:"OCaml Evaluation Server"
+
~version:"0.1.0" () |>
+
fun server ->
+
(* Set default capabilities *)
+
configure_server server ~with_tools:true ()
+
+
(* Toplevel environment state management *)
+
let toplevel_initialized = ref false
+
+
(* Initialize OCaml toplevel on first use *)
+
let ensure_toploop_initialized () =
+
if not !toplevel_initialized then begin
+
let _ = initialize_toploop () in
+
toplevel_initialized := true;
+
end
+
+
(* Register eval tool *)
let _ = add_tool server
-
~name:"ping"
-
~description:"A simple ping tool that demonstrates the structured API"
+
~name:"ocaml_eval"
+
~description:"Evaluates OCaml toplevel phrases and returns the result"
~schema_properties:[
-
("message", "string", "The message to echo back")
+
("code", "string", "OCaml code to evaluate")
]
-
~schema_required:["message"]
+
~schema_required:["code"]
(fun args ->
+
ensure_toploop_initialized ();
+
try
-
let message = get_string_param args "message" in
-
(* Create a typed tool response using the new API *)
-
let content = [
-
ToolsCall.ToolContent.Text (TextContent.{
-
text = Printf.sprintf "Pong: %s" message;
-
annotations = None
-
})
-
] in
-
(* Convert to JSON for the response *)
-
ToolsCall.Response.yojson_of_t {
-
content;
-
is_error = false
-
}
+
(* Extract code parameter *)
+
let code = get_string_param args "code" in
+
+
(* Execute the code *)
+
let result = evaluate_phrase code in
+
+
(* Return formatted result *)
+
let success = match result with
+
| `Assoc fields -> (
+
match List.assoc_opt "success" fields with
+
| Some (`Bool true) -> true
+
| _ -> false
+
)
+
| _ -> false
+
in
+
+
let output = match result with
+
| `Assoc fields -> (
+
match List.assoc_opt "output" fields with
+
| Some (`String s) -> s
+
| _ -> (
+
match List.assoc_opt "error" fields with
+
| Some (`String s) -> s
+
| _ -> "Unknown result"
+
)
+
)
+
| _ -> "Unknown result"
+
in
+
+
(* Create a tool result with colorized output *)
+
create_tool_result [
+
Mcp.make_text_content output
+
] ~is_error:(not success)
+
with
| Failure msg ->
-
Log.error (Printf.sprintf "Error in ping tool: %s" msg);
-
ToolsCall.Response.yojson_of_t {
-
content = [
-
ToolsCall.ToolContent.Text (TextContent.{
-
text = Printf.sprintf "Error: %s" msg;
-
annotations = None
-
})
-
];
-
is_error = true
-
}
+
Log.error (Printf.sprintf "Error in OCaml eval tool: %s" msg);
+
create_tool_result [Mcp.make_text_content (Printf.sprintf "Error: %s" msg)] ~is_error:true
)
-
(* Register a timestamp resource that uses the typed API *)
-
let _ = add_resource server
-
~uri_template:"timestamp://{format}"
-
~description:"Get the current timestamp in the specified format"
-
~mime_type:"text/plain"
-
(fun params ->
-
let format =
-
match params with
-
| [format] -> format
-
| _ -> "iso" (* default format *)
-
in
-
let timestamp =
-
match format with
-
| "unix" -> string_of_float (Unix.time ())
-
| "iso" | _ ->
-
let tm = Unix.gmtime (Unix.time ()) in
-
Printf.sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ"
-
(tm.tm_year + 1900) (tm.tm_mon + 1) tm.tm_mday
-
tm.tm_hour tm.tm_min tm.tm_sec
-
in
-
timestamp
-
)
-
-
(* Register a structured prompt that uses the typed API *)
-
let _ = add_prompt server
-
~name:"greet"
-
~description:"A prompt to greet someone using the structured API"
-
~arguments:[
-
("name", Some "The name of the person to greet", true);
-
("formal", Some "Whether to use formal greetings", false)
-
]
-
(fun args ->
-
let name = try List.assoc "name" args with Not_found -> "friend" in
-
let formal = try List.assoc "formal" args = "true" with Not_found -> false in
-
-
let greeting =
-
if formal then
-
Printf.sprintf "Greetings, %s. It's a pleasure to make your acquaintance." name
-
else
-
Printf.sprintf "Hey %s! Nice to meet you!" name
-
in
-
-
[
-
PromptMessage.{
-
role = `User;
-
content = make_text_content "I'd like to meet someone new."
-
};
-
PromptMessage.{
-
role = `Assistant;
-
content = make_text_content greeting
-
};
-
PromptMessage.{
-
role = `User;
-
content = make_text_content "What should I say next?"
-
}
-
]
-
)
-
-
(* Process a resources/list request using the typed API *)
-
let handle_resources_list_request request_id =
-
let resources = [
-
ResourcesList.Resource.{
-
uri = "timestamp://iso";
-
name = "ISO Timestamp";
-
description = Some "Current time in ISO format";
-
mime_type = Some "text/plain";
-
size = None;
-
};
-
ResourcesList.Resource.{
-
uri = "timestamp://unix";
-
name = "Unix Timestamp";
-
description = Some "Current time as Unix epoch";
-
mime_type = Some "text/plain";
-
size = None;
-
}
-
] in
-
-
(* Create a typed response *)
-
ResourcesList.create_response ~id:request_id ~resources ()
-
-
(* Process a tools/list request using the typed API *)
-
let handle_tools_list_request request_id =
-
let tools = [
-
ToolsList.Tool.{
-
name = "ping";
-
description = Some "A simple ping tool that demonstrates the structured API";
-
input_schema = `Assoc [
-
("type", `String "object");
-
("properties", `Assoc [
-
("message", `Assoc [
-
("type", `String "string");
-
("description", `String "The message to echo back")
-
])
-
]);
-
("required", `List [`String "message"])
-
];
-
annotations = None;
-
}
-
] in
-
-
(* Create a typed response *)
-
ToolsList.create_response ~id:request_id ~tools ()
-
-
(* Process a prompts/list request using the typed API *)
-
let handle_prompts_list_request request_id =
-
let prompts = [
-
PromptsList.Prompt.{
-
name = "greet";
-
description = Some "A prompt to greet someone using the structured API";
-
arguments = [
-
PromptsList.PromptArgument.{
-
name = "name";
-
description = Some "The name of the person to greet";
-
required = true;
-
};
-
PromptsList.PromptArgument.{
-
name = "formal";
-
description = Some "Whether to use formal greetings";
-
required = false;
-
}
-
];
-
}
-
] in
-
-
(* Create a typed response *)
-
PromptsList.create_response ~id:request_id ~prompts ()
-
-
(* Run the server *)
+
(* Run the server with the default scheduler *)
let () =
-
(* Example of creating a structured message directly - not actually used in the server *)
-
let example_structured_request =
-
ToolsCall.create_request
-
~name:"ping"
-
~arguments:(`Assoc [("message", `String "hello")])
-
~id:(`Int 12345)
-
()
-
in
-
-
(* Log the example request for demonstration *)
-
let json_str = Yojson.Safe.to_string (JSONRPCMessage.yojson_of_t example_structured_request) in
-
Log.info (Printf.sprintf "Example structured request: %s" json_str);
-
-
(* Run the server with the default scheduler *)
Eio_main.run @@ fun env->
Mcp_server.run_server env server