My agentic slop goes here. Not intended for anyone else!

more

Changed files
+223
claudeio
+3
claudeio/examples/dune
···
···
+
(executable
+
(name mcp_server_example)
+
(libraries mcp eio_main))
+220
claudeio/examples/mcp_server_example.ml
···
···
+
(** Example MCP server using the Server_session API.
+
+
This example demonstrates how to create a simple MCP server that provides:
+
- A few example tools (add, echo)
+
- A simple resource (example://greeting)
+
- Basic ping support
+
+
Note: This is a template/demonstration. A real MCP server would typically:
+
1. Read JSON-RPC messages from stdin
+
2. Write JSON-RPC responses to stdout
+
3. Use proper error handling and logging
+
+
To use this as a real server, you would need to:
+
- Create a custom Transport that reads/writes to stdio
+
- Handle process lifecycle properly
+
- Add comprehensive error handling
+
*)
+
+
open Mcp
+
+
(* Helper to find field in Jsont.Object *)
+
let find_field name fields =
+
List.find_map (fun ((n, _), value) ->
+
if n = name then Some value else None
+
) fields
+
+
(* Example tool: Add two numbers *)
+
let handle_add_tool ~name:_ ~arguments =
+
let open Messages.Tools in
+
match arguments with
+
| Some (Jsont.Object (fields, _)) ->
+
let a = match find_field "a" fields with
+
| Some (Jsont.Number (n, _)) -> n
+
| _ -> 0.0
+
in
+
let b = match find_field "b" fields with
+
| Some (Jsont.Number (n, _)) -> n
+
| _ -> 0.0
+
in
+
let result = a +. b in
+
let content = [
+
Content.text (Printf.sprintf "Result: %.2f" result)
+
] in
+
make_call_result ~content ()
+
| _ ->
+
make_call_result
+
~content:[Content.text "Invalid arguments"]
+
~is_error:true
+
()
+
+
(* Example tool: Echo a message *)
+
let handle_echo_tool ~name:_ ~arguments =
+
let open Messages.Tools in
+
match arguments with
+
| Some (Jsont.Object (fields, _)) ->
+
(match find_field "message" fields with
+
| Some (Jsont.String (msg, _)) ->
+
let content = [Content.text msg] in
+
make_call_result ~content ()
+
| _ ->
+
make_call_result
+
~content:[Content.text "No message provided"]
+
~is_error:true
+
())
+
| _ ->
+
make_call_result
+
~content:[Content.text "Invalid arguments"]
+
~is_error:true
+
()
+
+
(* Tool dispatcher *)
+
let call_tool ~name ~arguments =
+
match name with
+
| "add" -> handle_add_tool ~name ~arguments
+
| "echo" -> handle_echo_tool ~name ~arguments
+
| _ ->
+
Messages.Tools.make_call_result
+
~content:[Content.text (Printf.sprintf "Unknown tool: %s" name)]
+
~is_error:true
+
()
+
+
(* List available tools *)
+
let list_tools ~cursor:_ =
+
let open Messages.Tools in
+
let meta = Jsont.Meta.none in
+
let tools = [
+
make_tool
+
~name:"add"
+
~description:"Add two numbers together"
+
~input_schema:(Jsont.Object ([
+
(("type", meta), Jsont.String ("object", meta));
+
(("properties", meta), Jsont.Object ([
+
(("a", meta), Jsont.Object ([(("type", meta), Jsont.String ("number", meta))], meta));
+
(("b", meta), Jsont.Object ([(("type", meta), Jsont.String ("number", meta))], meta));
+
], meta));
+
(("required", meta), Jsont.Array ([Jsont.String ("a", meta); Jsont.String ("b", meta)], meta));
+
], meta))
+
();
+
make_tool
+
~name:"echo"
+
~description:"Echo a message back"
+
~input_schema:(Jsont.Object ([
+
(("type", meta), Jsont.String ("object", meta));
+
(("properties", meta), Jsont.Object ([
+
(("message", meta), Jsont.Object ([(("type", meta), Jsont.String ("string", meta))], meta));
+
], meta));
+
(("required", meta), Jsont.Array ([Jsont.String ("message", meta)], meta));
+
], meta))
+
();
+
] in
+
make_list_result ~tools ()
+
+
(* Example resource *)
+
let read_resource ~uri =
+
let open Messages.Resources in
+
match uri with
+
| "example://greeting" ->
+
let contents = [
+
make_text_contents
+
~uri
+
~text:"Hello from the MCP server!"
+
~mime_type:"text/plain"
+
()
+
] in
+
make_read_result ~contents
+
| _ ->
+
failwith (Printf.sprintf "Unknown resource: %s" uri)
+
+
let list_resources ~cursor:_ =
+
let open Messages.Resources in
+
let resources = [
+
make_resource
+
~uri:"example://greeting"
+
~name:"Greeting"
+
~description:"A simple greeting message"
+
~mime_type:"text/plain"
+
()
+
] in
+
make_list_result ~resources ()
+
+
(* Ping handler *)
+
let ping () = ()
+
+
(* Main server function *)
+
let run_server env =
+
let open Eio in
+
+
Switch.run @@ fun sw ->
+
(* Create stdio transport for the server *)
+
(* Note: For a real server, you would typically use a transport that
+
reads from stdin and writes to stdout. Here we create a simple
+
in-process transport for demonstration. *)
+
let params = {
+
Transport_stdio.command = "cat"; (* Echo back for demo *)
+
args = [];
+
env = None;
+
max_buffer_size = None;
+
} in
+
let transport = Transport_stdio.create
+
~sw
+
~process_mgr:(Eio.Stdenv.process_mgr env)
+
params
+
in
+
+
(* Configure server *)
+
let config = {
+
Server_session.server_info = Capabilities.Implementation.make
+
~name:"example-mcp-server"
+
~version:"1.0.0";
+
server_capabilities = Capabilities.Server.make
+
~tools:(Capabilities.Tools.make ())
+
~resources:(Capabilities.Resources.make ())
+
()
+
;
+
instructions = Some "Example MCP server with basic tools and resources";
+
} in
+
+
(* Set up handlers *)
+
let handlers = {
+
Server_session.list_resources = Some list_resources;
+
list_resource_templates = None;
+
read_resource = Some read_resource;
+
subscribe_resource = None;
+
unsubscribe_resource = None;
+
list_tools = Some list_tools;
+
call_tool = Some call_tool;
+
list_prompts = None;
+
get_prompt = None;
+
complete = None;
+
ping = Some ping;
+
} in
+
+
(* Create and run server *)
+
let server = Server_session.create
+
~sw
+
~transport
+
config
+
handlers
+
in
+
+
(* Log server startup *)
+
let client_info = Server_session.client_info server in
+
Eio.traceln "MCP server started, connected to client: %s v%s"
+
client_info.Capabilities.Implementation.name
+
client_info.Capabilities.Implementation.version;
+
+
(* The server runs in the background via the Session's receive loop *)
+
(* The switch will keep the server alive until it's explicitly closed or an error occurs *)
+
(* For this example, we just let it run until the process is terminated *)
+
()
+
(* Note: In a real server, you would wait for some termination signal or condition *)
+
+
let () =
+
Eio_main.run @@ fun env ->
+
try
+
run_server env
+
with
+
| exn ->
+
Printf.eprintf "Server error: %s\n%!" (Printexc.to_string exn);
+
exit 1