···11open Mcp
22open Mcp_sdk
33+open Mcp_server
3445(* WAV file format helper module *)
56module Wav = struct
···307308308309(* Main function *)
309310let () =
311311+ (* Parse command line arguments *)
312312+ let transport_type = ref Mcp_server.Stdio in
313313+ let args = [
314314+ ("--http", Arg.Unit (fun () -> transport_type := Mcp_server.Http),
315315+ "Start server with HTTP transport (default is stdio)");
316316+ ] in
317317+ let usage_msg = "Usage: audio_example [--http]" in
318318+ Arg.parse args (fun _ -> ()) usage_msg;
319319+310320 (* Use stderr for direct printing to avoid interfering with JSON-RPC protocol *)
311321 Printf.fprintf stderr "Starting AudioExampleServer...\n";
312322 flush stderr;
···315325 (* Configure the server with appropriate capabilities *)
316326 ignore (configure_server server ());
317327318318- (* Run the server *)
319319- run_server server328328+ (* Create and start MCP server with the selected transport *)
329329+ let mcp_server = Mcp_server.create ~server ~transport:!transport_type () in
330330+ Mcp_server.start mcp_server
+13-2
bin/capitalize_sdk.ml
···11open Mcp
22open Mcp_sdk
33+open Mcp_server
3445(* Helper for extracting string value from JSON *)
56let get_string_param json name =
···106107107108(* Main function *)
108109let () =
110110+ (* Parse command line arguments *)
111111+ let transport_type = ref Stdio in
112112+ let args = [
113113+ ("--http", Arg.Unit (fun () -> transport_type := Http),
114114+ "Start server with HTTP transport (default is stdio)");
115115+ ] in
116116+ let usage_msg = "Usage: capitalize_sdk [--http]" in
117117+ Arg.parse args (fun _ -> ()) usage_msg;
118118+109119 (* Use stderr for direct printing to avoid interfering with JSON-RPC protocol *)
110120 Printf.fprintf stderr "Starting CapitalizeServer...\n";
111121 flush stderr;
···114124 (* Configure the server with appropriate capabilities *)
115125 ignore (configure_server server ());
116126117117- (* Run the server *)
118118- run_server server127127+ (* Create and start MCP server with the selected transport *)
128128+ let mcp_server = create ~server ~transport:!transport_type () in
129129+ start mcp_server
+13-2
bin/completion_example.ml
···11open Mcp
22open Mcp_sdk
33+open Mcp_server
3445(* Helper for extracting string value from JSON *)
56let get_string_param json name =
···157158158159(* Main function *)
159160let () =
161161+ (* Parse command line arguments *)
162162+ let transport_type = ref Stdio in
163163+ let args = [
164164+ ("--http", Arg.Unit (fun () -> transport_type := Http),
165165+ "Start server with HTTP transport (default is stdio)");
166166+ ] in
167167+ let usage_msg = "Usage: completion_example [--http]" in
168168+ Arg.parse args (fun _ -> ()) usage_msg;
169169+160170 (* Use stderr for direct printing to avoid interfering with JSON-RPC protocol *)
161171 Printf.fprintf stderr "Starting CompletionServer...\n";
162172 flush stderr;
···178188 ] in
179189 set_capabilities server capabilities;
180190181181- (* Run the server *)
182182- run_server server191191+ (* Create and start MCP server with the selected transport *)
192192+ let mcp_server = create ~server ~transport:!transport_type () in
193193+ start mcp_server
···11+open Mcp
22+open Mcp_sdk
33+open Mcp_server
44+55+(* Random pixel image generator MCP server *)
66+77+(* Base64 encoding helper *)
88+module Base64 = struct
99+ let encode_char n =
1010+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".[n]
1111+1212+ let encode_block i bytes =
1313+ let buffer = Buffer.create 4 in
1414+ let b1 = Char.code (String.get bytes (i * 3)) in
1515+ let b2 = if i * 3 + 1 < String.length bytes then Char.code (String.get bytes (i * 3 + 1)) else 0 in
1616+ let b3 = if i * 3 + 2 < String.length bytes then Char.code (String.get bytes (i * 3 + 2)) else 0 in
1717+1818+ let n = (b1 lsl 16) lor (b2 lsl 8) lor b3 in
1919+ Buffer.add_char buffer (encode_char ((n lsr 18) land 63));
2020+ Buffer.add_char buffer (encode_char ((n lsr 12) land 63));
2121+2222+ if i * 3 + 1 < String.length bytes then
2323+ Buffer.add_char buffer (encode_char ((n lsr 6) land 63))
2424+ else
2525+ Buffer.add_char buffer '=';
2626+2727+ if i * 3 + 2 < String.length bytes then
2828+ Buffer.add_char buffer (encode_char (n land 63))
2929+ else
3030+ Buffer.add_char buffer '=';
3131+3232+ Buffer.contents buffer
3333+3434+ let encode data =
3535+ let buffer = Buffer.create (4 * (String.length data + 2) / 3) in
3636+ for i = 0 to (String.length data - 1) / 3 do
3737+ Buffer.add_string buffer (encode_block i data)
3838+ done;
3939+ Buffer.contents buffer
4040+end
4141+4242+(* Image generation utilities *)
4343+module ImageGenerator = struct
4444+ (* Simple PNG generation *)
4545+ let create_png width height pixels =
4646+ (* PNG signature *)
4747+ let signature = [|137; 80; 78; 71; 13; 10; 26; 10|] in
4848+4949+ (* IHDR chunk data *)
5050+ let ihdr_data = Bytes.create 13 in
5151+ (* Width - big endian *)
5252+ Bytes.set ihdr_data 0 (Char.chr ((width lsr 24) land 0xff));
5353+ Bytes.set ihdr_data 1 (Char.chr ((width lsr 16) land 0xff));
5454+ Bytes.set ihdr_data 2 (Char.chr ((width lsr 8) land 0xff));
5555+ Bytes.set ihdr_data 3 (Char.chr (width land 0xff));
5656+ (* Height - big endian *)
5757+ Bytes.set ihdr_data 4 (Char.chr ((height lsr 24) land 0xff));
5858+ Bytes.set ihdr_data 5 (Char.chr ((height lsr 16) land 0xff));
5959+ Bytes.set ihdr_data 6 (Char.chr ((height lsr 8) land 0xff));
6060+ Bytes.set ihdr_data 7 (Char.chr (height land 0xff));
6161+ (* Bit depth - 8 bits *)
6262+ Bytes.set ihdr_data 8 (Char.chr 8);
6363+ (* Color type - RGB with alpha *)
6464+ Bytes.set ihdr_data 9 (Char.chr 6);
6565+ (* Compression, filter, interlace - all 0 *)
6666+ Bytes.set ihdr_data 10 (Char.chr 0);
6767+ Bytes.set ihdr_data 11 (Char.chr 0);
6868+ Bytes.set ihdr_data 12 (Char.chr 0);
6969+7070+ (* Very simple CRC32 implementation for PNG chunks *)
7171+ let calculate_crc data =
7272+ let crc = ref 0xffffffff in
7373+ for i = 0 to Bytes.length data - 1 do
7474+ let byte = Char.code (Bytes.get data i) in
7575+ crc := !crc lxor byte;
7676+ for _ = 0 to 7 do
7777+ if !crc land 1 <> 0 then
7878+ crc := (!crc lsr 1) lxor 0xedb88320
7979+ else
8080+ crc := !crc lsr 1
8181+ done
8282+ done;
8383+ !crc lxor 0xffffffff
8484+ in
8585+8686+ (* Create IHDR chunk *)
8787+ let ihdr_chunk = Buffer.create 25 in
8888+ (* Length - 13 bytes *)
8989+ Buffer.add_char ihdr_chunk (Char.chr 0);
9090+ Buffer.add_char ihdr_chunk (Char.chr 0);
9191+ Buffer.add_char ihdr_chunk (Char.chr 0);
9292+ Buffer.add_char ihdr_chunk (Char.chr 13);
9393+ (* Chunk type - IHDR *)
9494+ Buffer.add_string ihdr_chunk "IHDR";
9595+ (* Chunk data *)
9696+ Buffer.add_string ihdr_chunk (Bytes.unsafe_to_string ihdr_data);
9797+ (* CRC *)
9898+ let ihdr_crc_data = Bytes.create 17 in
9999+ Bytes.blit_string "IHDR" 0 ihdr_crc_data 0 4;
100100+ Bytes.blit ihdr_data 0 ihdr_crc_data 4 13;
101101+ let crc = calculate_crc ihdr_crc_data in
102102+ Buffer.add_char ihdr_chunk (Char.chr ((crc lsr 24) land 0xff));
103103+ Buffer.add_char ihdr_chunk (Char.chr ((crc lsr 16) land 0xff));
104104+ Buffer.add_char ihdr_chunk (Char.chr ((crc lsr 8) land 0xff));
105105+ Buffer.add_char ihdr_chunk (Char.chr (crc land 0xff));
106106+107107+ (* Create IDAT chunk (uncompressed for simplicity) *)
108108+ let row_size = width * 4 in
109109+ let data_size = height * (row_size + 1) in
110110+ let idat_chunk = Buffer.create (12 + data_size) in
111111+ (* Length *)
112112+ Buffer.add_char idat_chunk (Char.chr ((data_size lsr 24) land 0xff));
113113+ Buffer.add_char idat_chunk (Char.chr ((data_size lsr 16) land 0xff));
114114+ Buffer.add_char idat_chunk (Char.chr ((data_size lsr 8) land 0xff));
115115+ Buffer.add_char idat_chunk (Char.chr (data_size land 0xff));
116116+ (* Chunk type - IDAT *)
117117+ Buffer.add_string idat_chunk "IDAT";
118118+119119+ (* Very simple zlib header (no compression) *)
120120+ Buffer.add_char idat_chunk (Char.chr 0x78); (* CMF byte *)
121121+ Buffer.add_char idat_chunk (Char.chr 0x01); (* FLG byte *)
122122+123123+ (* Raw image data with filter type 0 (None) for each scanline *)
124124+ for y = 0 to height - 1 do
125125+ (* Filter type 0 (None) *)
126126+ Buffer.add_char idat_chunk (Char.chr 0);
127127+ for x = 0 to width - 1 do
128128+ let idx = (y * width + x) * 4 in
129129+ Buffer.add_char idat_chunk (Char.chr (Bytes.get_uint8 pixels idx)); (* R *)
130130+ Buffer.add_char idat_chunk (Char.chr (Bytes.get_uint8 pixels (idx + 1))); (* G *)
131131+ Buffer.add_char idat_chunk (Char.chr (Bytes.get_uint8 pixels (idx + 2))); (* B *)
132132+ Buffer.add_char idat_chunk (Char.chr (Bytes.get_uint8 pixels (idx + 3))); (* A *)
133133+ done
134134+ done;
135135+136136+ (* Zlib Adler-32 checksum (simplified) *)
137137+ let adler = ref 1 in
138138+ Buffer.add_char idat_chunk (Char.chr ((!adler lsr 24) land 0xff));
139139+ Buffer.add_char idat_chunk (Char.chr ((!adler lsr 16) land 0xff));
140140+ Buffer.add_char idat_chunk (Char.chr ((!adler lsr 8) land 0xff));
141141+ Buffer.add_char idat_chunk (Char.chr (!adler land 0xff));
142142+143143+ (* CRC *)
144144+ let idat_crc = ref 0 in (* Not calculating CRC for simplicity *)
145145+ Buffer.add_char idat_chunk (Char.chr ((!idat_crc lsr 24) land 0xff));
146146+ Buffer.add_char idat_chunk (Char.chr ((!idat_crc lsr 16) land 0xff));
147147+ Buffer.add_char idat_chunk (Char.chr ((!idat_crc lsr 8) land 0xff));
148148+ Buffer.add_char idat_chunk (Char.chr (!idat_crc land 0xff));
149149+150150+ (* Create IEND chunk *)
151151+ let iend_chunk = Buffer.create 12 in
152152+ (* Length - 0 bytes *)
153153+ Buffer.add_char iend_chunk (Char.chr 0);
154154+ Buffer.add_char iend_chunk (Char.chr 0);
155155+ Buffer.add_char iend_chunk (Char.chr 0);
156156+ Buffer.add_char iend_chunk (Char.chr 0);
157157+ (* Chunk type - IEND *)
158158+ Buffer.add_string iend_chunk "IEND";
159159+ (* CRC *)
160160+ let iend_crc = 0xAE426082 in (* Precomputed CRC for IEND chunk *)
161161+ Buffer.add_char iend_chunk (Char.chr ((iend_crc lsr 24) land 0xff));
162162+ Buffer.add_char iend_chunk (Char.chr ((iend_crc lsr 16) land 0xff));
163163+ Buffer.add_char iend_chunk (Char.chr ((iend_crc lsr 8) land 0xff));
164164+ Buffer.add_char iend_chunk (Char.chr (iend_crc land 0xff));
165165+166166+ (* Combine all parts *)
167167+ let result = Buffer.create (8 + Buffer.length ihdr_chunk + Buffer.length idat_chunk + Buffer.length iend_chunk) in
168168+ (* PNG signature *)
169169+ Array.iter (fun c -> Buffer.add_char result (Char.chr c)) signature;
170170+ (* IHDR chunk *)
171171+ Buffer.add_buffer result ihdr_chunk;
172172+ (* IDAT chunk *)
173173+ Buffer.add_buffer result idat_chunk;
174174+ (* IEND chunk *)
175175+ Buffer.add_buffer result iend_chunk;
176176+177177+ Buffer.contents result
178178+179179+ (* Generate random pixel art image *)
180180+ let generate_random_image ?(width=16) ?(height=16) ?(pixel_size=1) ?(seed=None) () =
181181+ let pixels = Bytes.create (width * height * 4) in
182182+183183+ (* Set random seed if provided *)
184184+ (match seed with
185185+ | Some s -> Random.init s
186186+ | None -> Random.self_init ());
187187+188188+ (* Generate a random color palette *)
189189+ let palette_size = Random.int 8 + 2 in (* 2-10 colors *)
190190+ let palette = Array.init palette_size (fun _ ->
191191+ (Random.int 256, Random.int 256, Random.int 256, 255) (* RGBA *)
192192+ ) in
193193+194194+ (* Fill the pixel buffer *)
195195+ for y = 0 to height - 1 do
196196+ for x = 0 to width - 1 do
197197+ let color_idx = Random.int palette_size in
198198+ let (r, g, b, a) = palette.(color_idx) in
199199+ let idx = (y * width + x) * 4 in
200200+ Bytes.set_uint8 pixels idx r;
201201+ Bytes.set_uint8 pixels (idx + 1) g;
202202+ Bytes.set_uint8 pixels (idx + 2) b;
203203+ Bytes.set_uint8 pixels (idx + 3) a;
204204+ done
205205+ done;
206206+207207+ (* Create symmetrical patterns - horizontally, vertically, or both *)
208208+ let symmetry_type = Random.int 3 in
209209+ if symmetry_type > 0 then begin
210210+ for y = 0 to height - 1 do
211211+ for x = 0 to width / 2 do
212212+ (* Mirror horizontally (except center column for odd widths) *)
213213+ if symmetry_type = 1 || symmetry_type = 2 then begin
214214+ let mirror_x = width - 1 - x in
215215+ if x <> mirror_x then begin
216216+ let src_idx = (y * width + x) * 4 in
217217+ let dst_idx = (y * width + mirror_x) * 4 in
218218+ for i = 0 to 3 do
219219+ Bytes.set pixels (dst_idx + i) (Bytes.get pixels (src_idx + i))
220220+ done
221221+ end
222222+ end
223223+ done
224224+ done;
225225+226226+ (* Mirror vertically for symmetry_type = 2 *)
227227+ if symmetry_type = 2 then begin
228228+ for y = 0 to height / 2 do
229229+ let mirror_y = height - 1 - y in
230230+ if y <> mirror_y then begin
231231+ for x = 0 to width - 1 do
232232+ let src_idx = (y * width + x) * 4 in
233233+ let dst_idx = (mirror_y * width + x) * 4 in
234234+ for i = 0 to 3 do
235235+ Bytes.set pixels (dst_idx + i) (Bytes.get pixels (src_idx + i))
236236+ done
237237+ done
238238+ end
239239+ done
240240+ end
241241+ end;
242242+243243+ (* Scale up the image if pixel_size > 1 *)
244244+ let final_width = width * pixel_size in
245245+ let final_height = height * pixel_size in
246246+247247+ if pixel_size = 1 then
248248+ create_png width height pixels
249249+ else begin
250250+ let scaled_pixels = Bytes.create (final_width * final_height * 4) in
251251+252252+ for y = 0 to height - 1 do
253253+ for x = 0 to width - 1 do
254254+ let src_idx = (y * width + x) * 4 in
255255+ for py = 0 to pixel_size - 1 do
256256+ for px = 0 to pixel_size - 1 do
257257+ let dst_x = x * pixel_size + px in
258258+ let dst_y = y * pixel_size + py in
259259+ let dst_idx = (dst_y * final_width + dst_x) * 4 in
260260+ for i = 0 to 3 do
261261+ Bytes.set scaled_pixels (dst_idx + i) (Bytes.get pixels (src_idx + i))
262262+ done
263263+ done
264264+ done
265265+ done
266266+ done;
267267+268268+ create_png final_width final_height scaled_pixels
269269+ end
270270+end
271271+272272+(* Helper for extracting values from JSON *)
273273+let get_param_int json name default =
274274+ match json with
275275+ | `Assoc fields ->
276276+ (match List.assoc_opt name fields with
277277+ | Some (`Int i) -> i
278278+ | Some (`Float f) -> int_of_float f
279279+ | _ -> default)
280280+ | _ -> default
281281+282282+(* Create a server *)
283283+let server = create_server
284284+ ~name:"OCaml MCP Image Generator"
285285+ ~version:"0.1.0"
286286+ ~protocol_version:"2024-11-05"
287287+ ()
288288+289289+(* Define startup and shutdown hooks *)
290290+let startup () =
291291+ Printf.fprintf stderr "ImageGeneratorServer is starting up!\n";
292292+ flush stderr;
293293+ Log.info "ImageGeneratorServer is starting up!"
294294+295295+let shutdown () =
296296+ Printf.fprintf stderr "ImageGeneratorServer is shutting down. Goodbye!\n";
297297+ flush stderr;
298298+ Log.info "ImageGeneratorServer is shutting down. Goodbye!"
299299+300300+(* Register the hooks *)
301301+let () =
302302+ set_startup_hook server startup;
303303+ set_shutdown_hook server shutdown
304304+305305+(* Make an image content helper *)
306306+let make_image_content data mime_type =
307307+ let image_content = ImageContent.{
308308+ data;
309309+ mime_type;
310310+ annotations = None;
311311+ } in
312312+ Image image_content
313313+314314+(* Define and register a random pixel art generator tool *)
315315+let _ = add_tool server
316316+ ~name:"generate_random_pixel_art"
317317+ ~description:"Generates a random pixel art image"
318318+ ~schema_properties:[
319319+ ("width", "integer", "Width of the pixel art grid (default: 16)");
320320+ ("height", "integer", "Height of the pixel art grid (default: 16)");
321321+ ("pixel_size", "integer", "Size of each pixel (default: 8)");
322322+ ("seed", "integer", "Random seed (optional)");
323323+ ]
324324+ ~schema_required:[]
325325+ (fun args ->
326326+ try
327327+ let width = get_param_int args "width" 16 in
328328+ let height = get_param_int args "height" 16 in
329329+ let pixel_size = get_param_int args "pixel_size" 8 in
330330+331331+ (* Validate parameters *)
332332+ let width = max 1 (min 64 width) in (* Limit to 1-64 *)
333333+ let height = max 1 (min 64 height) in (* Limit to 1-64 *)
334334+ let pixel_size = max 1 (min 16 pixel_size) in (* Limit to 1-16 *)
335335+336336+ (* Extract optional seed *)
337337+ let seed = match args with
338338+ | `Assoc fields ->
339339+ (match List.assoc_opt "seed" fields with
340340+ | Some (`Int s) -> Some s
341341+ | _ -> None)
342342+ | _ -> None
343343+ in
344344+345345+ (* Generate the image *)
346346+ let image_data = ImageGenerator.generate_random_image
347347+ ~width ~height ~pixel_size ~seed () in
348348+349349+ (* Encode as base64 *)
350350+ let base64_data = Base64.encode image_data in
351351+352352+ Log.info (Printf.sprintf "Generated random pixel art image (%dx%d grid, %dpx pixels)"
353353+ width height pixel_size);
354354+355355+ (* Create a response with both text and image content *)
356356+ CallToolResult.yojson_of_t CallToolResult.{
357357+ content = [
358358+ Text TextContent.{
359359+ text = Printf.sprintf "Generated random pixel art image (%dx%d grid, %dpx pixels)"
360360+ width height pixel_size;
361361+ annotations = None
362362+ };
363363+ Image ImageContent.{
364364+ data = base64_data;
365365+ mime_type = "image/png";
366366+ annotations = None
367367+ }
368368+ ];
369369+ is_error = false;
370370+ meta = None
371371+ }
372372+ with
373373+ | Failure msg ->
374374+ Log.error (Printf.sprintf "Error in image generator tool: %s" msg);
375375+ CallToolResult.yojson_of_t CallToolResult.{
376376+ content = [
377377+ Text TextContent.{
378378+ text = Printf.sprintf "Error: %s" msg;
379379+ annotations = None
380380+ }
381381+ ];
382382+ is_error = true;
383383+ meta = None
384384+ }
385385+ )
386386+387387+(* Define and register a pixel art prompt *)
388388+let _ = add_prompt server
389389+ ~name:"pixel-art-prompt"
390390+ ~description:"A prompt that includes a random pixel art image"
391391+ ~arguments:[
392392+ ("width", Some "Width of the pixel art (1-64)", false);
393393+ ("height", Some "Height of the pixel art (1-64)", false);
394394+ ("pixel_size", Some "Size of each pixel (1-16)", false);
395395+ ]
396396+ (fun args ->
397397+ (* Parse parameters with defaults *)
398398+ let width =
399399+ try int_of_string (List.assoc "width" args)
400400+ with _ -> 16
401401+ in
402402+ let height =
403403+ try int_of_string (List.assoc "height" args)
404404+ with _ -> 16
405405+ in
406406+ let pixel_size =
407407+ try int_of_string (List.assoc "pixel_size" args)
408408+ with _ -> 8
409409+ in
410410+411411+ (* Validate parameters *)
412412+ let width = max 1 (min 64 width) in
413413+ let height = max 1 (min 64 height) in
414414+ let pixel_size = max 1 (min 16 pixel_size) in
415415+416416+ (* Generate image *)
417417+ let image_data = ImageGenerator.generate_random_image
418418+ ~width ~height ~pixel_size () in
419419+420420+ (* Encode as base64 *)
421421+ let base64_data = Base64.encode image_data in
422422+423423+ Log.info (Printf.sprintf "Generated pixel art for prompt (%dx%d grid, %dpx pixels)"
424424+ width height pixel_size);
425425+426426+ [
427427+ Prompt.{
428428+ role = `User;
429429+ content = make_text_content "I've generated a random pixel art image for you:"
430430+ };
431431+ Prompt.{
432432+ role = `User;
433433+ content = make_image_content base64_data "image/png"
434434+ };
435435+ Prompt.{
436436+ role = `User;
437437+ content = make_text_content (Printf.sprintf "Please describe what you see in this %dx%d pixel art."
438438+ width height)
439439+ };
440440+ Prompt.{
441441+ role = `Assistant;
442442+ content = make_text_content "I'll describe what I see in this pixel art image."
443443+ }
444444+ ]
445445+ )
446446+447447+(* Main function *)
448448+let () =
449449+ (* Parse command line arguments *)
450450+ let transport_type = ref Stdio in
451451+ let args = [
452452+ ("--http", Arg.Unit (fun () -> transport_type := Http),
453453+ "Start server with HTTP transport (default is stdio)");
454454+ ] in
455455+ let usage_msg = "Usage: image_generator_example [--http]" in
456456+ Arg.parse args (fun _ -> ()) usage_msg;
457457+458458+ (* Use stderr for direct printing to avoid interfering with JSON-RPC protocol *)
459459+ Printf.fprintf stderr "Starting ImageGeneratorServer...\n";
460460+ flush stderr;
461461+ Log.info "Starting ImageGeneratorServer...";
462462+463463+ (* Configure the server with appropriate capabilities *)
464464+ ignore (configure_server server ());
465465+466466+ (* Create and start MCP server with the selected transport *)
467467+ let mcp_server = create ~server ~transport:!transport_type () in
468468+ start mcp_server
+502
bin/multimodal_example.ml
···11+open Mcp
22+open Mcp_sdk
33+open Mcp_server
44+55+(* Multimodal example MCP server *)
66+77+(* Base64 encoding helper *)
88+module Base64 = struct
99+ let encode_char n =
1010+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".[n]
1111+1212+ let encode_block i bytes =
1313+ let buffer = Buffer.create 4 in
1414+ let b1 = Char.code (String.get bytes (i * 3)) in
1515+ let b2 = if i * 3 + 1 < String.length bytes then Char.code (String.get bytes (i * 3 + 1)) else 0 in
1616+ let b3 = if i * 3 + 2 < String.length bytes then Char.code (String.get bytes (i * 3 + 2)) else 0 in
1717+1818+ let n = (b1 lsl 16) lor (b2 lsl 8) lor b3 in
1919+ Buffer.add_char buffer (encode_char ((n lsr 18) land 63));
2020+ Buffer.add_char buffer (encode_char ((n lsr 12) land 63));
2121+2222+ if i * 3 + 1 < String.length bytes then
2323+ Buffer.add_char buffer (encode_char ((n lsr 6) land 63))
2424+ else
2525+ Buffer.add_char buffer '=';
2626+2727+ if i * 3 + 2 < String.length bytes then
2828+ Buffer.add_char buffer (encode_char (n land 63))
2929+ else
3030+ Buffer.add_char buffer '=';
3131+3232+ Buffer.contents buffer
3333+3434+ let encode data =
3535+ let buffer = Buffer.create (4 * (String.length data + 2) / 3) in
3636+ for i = 0 to (String.length data - 1) / 3 do
3737+ Buffer.add_string buffer (encode_block i data)
3838+ done;
3939+ Buffer.contents buffer
4040+end
4141+4242+(* Audio generator *)
4343+module AudioGenerator = struct
4444+ (* Generate a simple sine wave *)
4545+ let generate_sine_wave ~frequency ~duration ~sample_rate ~amplitude =
4646+ (* WAV parameters *)
4747+ let num_channels = 1 in (* Mono *)
4848+ let bits_per_sample = 16 in
4949+ let byte_rate = sample_rate * num_channels * bits_per_sample / 8 in
5050+ let block_align = num_channels * bits_per_sample / 8 in
5151+ let num_samples = int_of_float (float_of_int sample_rate *. duration) in
5252+ let data_size = num_samples * block_align in
5353+5454+ (* Create buffer for the WAV data *)
5555+ let buffer = Buffer.create (44 + data_size) in
5656+5757+ (* Write WAV header *)
5858+ (* "RIFF" chunk *)
5959+ Buffer.add_string buffer "RIFF";
6060+ let file_size = 36 + data_size in
6161+ Buffer.add_char buffer (Char.chr (file_size land 0xff));
6262+ Buffer.add_char buffer (Char.chr ((file_size lsr 8) land 0xff));
6363+ Buffer.add_char buffer (Char.chr ((file_size lsr 16) land 0xff));
6464+ Buffer.add_char buffer (Char.chr ((file_size lsr 24) land 0xff));
6565+ Buffer.add_string buffer "WAVE";
6666+6767+ (* "fmt " sub-chunk *)
6868+ Buffer.add_string buffer "fmt ";
6969+ Buffer.add_char buffer (Char.chr 16); (* Sub-chunk size (16 for PCM) *)
7070+ Buffer.add_char buffer (Char.chr 0);
7171+ Buffer.add_char buffer (Char.chr 0);
7272+ Buffer.add_char buffer (Char.chr 0);
7373+ Buffer.add_char buffer (Char.chr 1); (* Audio format (1 for PCM) *)
7474+ Buffer.add_char buffer (Char.chr 0);
7575+ Buffer.add_char buffer (Char.chr num_channels); (* Number of channels *)
7676+ Buffer.add_char buffer (Char.chr 0);
7777+7878+ (* Sample rate *)
7979+ Buffer.add_char buffer (Char.chr (sample_rate land 0xff));
8080+ Buffer.add_char buffer (Char.chr ((sample_rate lsr 8) land 0xff));
8181+ Buffer.add_char buffer (Char.chr ((sample_rate lsr 16) land 0xff));
8282+ Buffer.add_char buffer (Char.chr ((sample_rate lsr 24) land 0xff));
8383+8484+ (* Byte rate *)
8585+ Buffer.add_char buffer (Char.chr (byte_rate land 0xff));
8686+ Buffer.add_char buffer (Char.chr ((byte_rate lsr 8) land 0xff));
8787+ Buffer.add_char buffer (Char.chr ((byte_rate lsr 16) land 0xff));
8888+ Buffer.add_char buffer (Char.chr ((byte_rate lsr 24) land 0xff));
8989+9090+ (* Block align *)
9191+ Buffer.add_char buffer (Char.chr block_align);
9292+ Buffer.add_char buffer (Char.chr 0);
9393+9494+ (* Bits per sample *)
9595+ Buffer.add_char buffer (Char.chr bits_per_sample);
9696+ Buffer.add_char buffer (Char.chr 0);
9797+9898+ (* "data" sub-chunk *)
9999+ Buffer.add_string buffer "data";
100100+ Buffer.add_char buffer (Char.chr (data_size land 0xff));
101101+ Buffer.add_char buffer (Char.chr ((data_size lsr 8) land 0xff));
102102+ Buffer.add_char buffer (Char.chr ((data_size lsr 16) land 0xff));
103103+ Buffer.add_char buffer (Char.chr ((data_size lsr 24) land 0xff));
104104+105105+ (* Generate sine wave data *)
106106+ let max_amplitude = float_of_int (1 lsl (bits_per_sample - 1)) -. 1.0 in
107107+ for i = 0 to num_samples - 1 do
108108+ let t = float_of_int i /. float_of_int sample_rate in
109109+ let value = int_of_float (amplitude *. max_amplitude *. sin (2.0 *. Float.pi *. frequency *. t)) in
110110+ (* Write 16-bit sample (little-endian) *)
111111+ Buffer.add_char buffer (Char.chr (value land 0xff));
112112+ Buffer.add_char buffer (Char.chr ((value lsr 8) land 0xff));
113113+ done;
114114+115115+ Buffer.contents buffer
116116+end
117117+118118+(* Image generator *)
119119+module ImageGenerator = struct
120120+ (* Simple PNG generation *)
121121+ let generate_simple_image width height color_str =
122122+ (* Parse color - expected format: #RRGGBB or #RRGGBBAA *)
123123+ let r, g, b, a =
124124+ try
125125+ if String.length color_str >= 7 && color_str.[0] = '#' then
126126+ let r = int_of_string ("0x" ^ String.sub color_str 1 2) in
127127+ let g = int_of_string ("0x" ^ String.sub color_str 3 2) in
128128+ let b = int_of_string ("0x" ^ String.sub color_str 5 2) in
129129+ let a = if String.length color_str >= 9 then
130130+ int_of_string ("0x" ^ String.sub color_str 7 2)
131131+ else 255 in
132132+ (r, g, b, a)
133133+ else
134134+ (255, 0, 0, 255) (* Default to red if invalid *)
135135+ with _ ->
136136+ (255, 0, 0, 255) (* Default to red on parsing error *)
137137+ in
138138+139139+ (* Create a very simple 1x1 PNG with the specified color *)
140140+ (* PNG signature *)
141141+ let signature = [137; 80; 78; 71; 13; 10; 26; 10] in
142142+143143+ (* Create buffer for the PNG data *)
144144+ let buffer = Buffer.create 100 in
145145+146146+ (* PNG signature *)
147147+ List.iter (fun b -> Buffer.add_char buffer (Char.chr b)) signature;
148148+149149+ (* IHDR chunk *)
150150+ Buffer.add_char buffer (Char.chr 0); (* length - 13 bytes *)
151151+ Buffer.add_char buffer (Char.chr 0);
152152+ Buffer.add_char buffer (Char.chr 0);
153153+ Buffer.add_char buffer (Char.chr 13);
154154+155155+ Buffer.add_string buffer "IHDR";
156156+157157+ (* Width *)
158158+ Buffer.add_char buffer (Char.chr ((width lsr 24) land 0xff));
159159+ Buffer.add_char buffer (Char.chr ((width lsr 16) land 0xff));
160160+ Buffer.add_char buffer (Char.chr ((width lsr 8) land 0xff));
161161+ Buffer.add_char buffer (Char.chr (width land 0xff));
162162+163163+ (* Height *)
164164+ Buffer.add_char buffer (Char.chr ((height lsr 24) land 0xff));
165165+ Buffer.add_char buffer (Char.chr ((height lsr 16) land 0xff));
166166+ Buffer.add_char buffer (Char.chr ((height lsr 8) land 0xff));
167167+ Buffer.add_char buffer (Char.chr (height land 0xff));
168168+169169+ Buffer.add_char buffer (Char.chr 8); (* Bit depth - 8 bits per channel *)
170170+ Buffer.add_char buffer (Char.chr 6); (* Color type - RGBA *)
171171+ Buffer.add_char buffer (Char.chr 0); (* Compression method - deflate *)
172172+ Buffer.add_char buffer (Char.chr 0); (* Filter method - adaptive filtering *)
173173+ Buffer.add_char buffer (Char.chr 0); (* Interlace method - no interlace *)
174174+175175+ (* IHDR CRC - precomputed for simplicity *)
176176+ Buffer.add_char buffer (Char.chr 0);
177177+ Buffer.add_char buffer (Char.chr 0);
178178+ Buffer.add_char buffer (Char.chr 0);
179179+ Buffer.add_char buffer (Char.chr 0);
180180+181181+ (* IDAT chunk - simplified for example *)
182182+ let pixels_per_row = width * 4 in
183183+ let data_size = (1 + pixels_per_row) * height in
184184+185185+ Buffer.add_char buffer (Char.chr ((data_size lsr 24) land 0xff));
186186+ Buffer.add_char buffer (Char.chr ((data_size lsr 16) land 0xff));
187187+ Buffer.add_char buffer (Char.chr ((data_size lsr 8) land 0xff));
188188+ Buffer.add_char buffer (Char.chr (data_size land 0xff));
189189+190190+ Buffer.add_string buffer "IDAT";
191191+192192+ (* Simple zlib header *)
193193+ Buffer.add_char buffer (Char.chr 0x78);
194194+ Buffer.add_char buffer (Char.chr 0x01);
195195+196196+ (* Raw image data *)
197197+ for _ = 0 to height - 1 do
198198+ Buffer.add_char buffer (Char.chr 0); (* Filter type 0 - None *)
199199+ for _ = 0 to width - 1 do
200200+ Buffer.add_char buffer (Char.chr r);
201201+ Buffer.add_char buffer (Char.chr g);
202202+ Buffer.add_char buffer (Char.chr b);
203203+ Buffer.add_char buffer (Char.chr a);
204204+ done
205205+ done;
206206+207207+ (* Dummy Adler32 checksum *)
208208+ Buffer.add_char buffer (Char.chr 0);
209209+ Buffer.add_char buffer (Char.chr 0);
210210+ Buffer.add_char buffer (Char.chr 0);
211211+ Buffer.add_char buffer (Char.chr 0);
212212+213213+ (* IDAT CRC - precomputed for simplicity *)
214214+ Buffer.add_char buffer (Char.chr 0);
215215+ Buffer.add_char buffer (Char.chr 0);
216216+ Buffer.add_char buffer (Char.chr 0);
217217+ Buffer.add_char buffer (Char.chr 0);
218218+219219+ (* IEND chunk *)
220220+ Buffer.add_char buffer (Char.chr 0);
221221+ Buffer.add_char buffer (Char.chr 0);
222222+ Buffer.add_char buffer (Char.chr 0);
223223+ Buffer.add_char buffer (Char.chr 0);
224224+225225+ Buffer.add_string buffer "IEND";
226226+227227+ (* IEND CRC - precomputed value *)
228228+ Buffer.add_char buffer (Char.chr 0xAE);
229229+ Buffer.add_char buffer (Char.chr 0x42);
230230+ Buffer.add_char buffer (Char.chr 0x60);
231231+ Buffer.add_char buffer (Char.chr 0x82);
232232+233233+ Buffer.contents buffer
234234+end
235235+236236+(* Helper for extracting values from JSON *)
237237+let get_param_int json name default =
238238+ match json with
239239+ | `Assoc fields -> begin
240240+ match List.assoc_opt name fields with
241241+ | Some (`Int i) -> begin
242242+ i
243243+ end
244244+ | Some (`Float f) -> begin
245245+ int_of_float f
246246+ end
247247+ | _ -> begin
248248+ default
249249+ end
250250+ end
251251+ | _ -> begin
252252+ default
253253+ end
254254+255255+let get_param_float json name default =
256256+ match json with
257257+ | `Assoc fields -> begin
258258+ match List.assoc_opt name fields with
259259+ | Some (`Int i) -> begin
260260+ float_of_int i
261261+ end
262262+ | Some (`Float f) -> begin
263263+ f
264264+ end
265265+ | _ -> begin
266266+ default
267267+ end
268268+ end
269269+ | _ -> begin
270270+ default
271271+ end
272272+273273+let get_param_string json name default =
274274+ match json with
275275+ | `Assoc fields -> begin
276276+ match List.assoc_opt name fields with
277277+ | Some (`String s) -> begin
278278+ s
279279+ end
280280+ | _ -> begin
281281+ default
282282+ end
283283+ end
284284+ | _ -> begin
285285+ default
286286+ end
287287+288288+(* Create a server *)
289289+let server = create_server
290290+ ~name:"OCaml MCP Multimodal Example"
291291+ ~version:"0.1.0"
292292+ ~protocol_version:"2024-11-05"
293293+ ()
294294+295295+(* Define startup and shutdown hooks *)
296296+let startup () =
297297+ Printf.fprintf stderr "MultimodalServer is starting up!\n";
298298+ flush stderr;
299299+ Log.info "MultimodalServer is starting up!"
300300+301301+let shutdown () =
302302+ Printf.fprintf stderr "MultimodalServer is shutting down. Goodbye!\n";
303303+ flush stderr;
304304+ Log.info "MultimodalServer is shutting down. Goodbye!"
305305+306306+(* Register the hooks *)
307307+let () =
308308+ set_startup_hook server startup;
309309+ set_shutdown_hook server shutdown
310310+311311+(* Define and register a multimodal tool *)
312312+let _ = add_tool server
313313+ ~name:"generate_multimodal_response"
314314+ ~description:"Generates a response with text, image and audio content"
315315+ ~schema_properties:[
316316+ ("message", "string", "The text message to include");
317317+ ("color", "string", "Color for the image (hex format #RRGGBB)");
318318+ ("frequency", "integer", "Frequency for the audio tone in Hz");
319319+ ]
320320+ ~schema_required:["message"]
321321+ (fun args ->
322322+ try
323323+ let message = get_param_string args "message" "Hello, multimodal world!" in
324324+ let color = get_param_string args "color" "#FF0000" in
325325+ let frequency = get_param_int args "frequency" 440 in
326326+327327+ (* Generate image *)
328328+ let image_data = ImageGenerator.generate_simple_image 100 100 color in
329329+ let image_base64 = Base64.encode image_data in
330330+331331+ (* Generate audio *)
332332+ let audio_data = AudioGenerator.generate_sine_wave
333333+ ~frequency:(float_of_int frequency)
334334+ ~duration:1.0
335335+ ~sample_rate:8000
336336+ ~amplitude:0.8 in
337337+ let audio_base64 = Base64.encode audio_data in
338338+339339+ (* Create a response with text, image and audio content *)
340340+ CallToolResult.yojson_of_t CallToolResult.{
341341+ content = [
342342+ Text TextContent.{
343343+ text = message;
344344+ annotations = None
345345+ };
346346+ Image ImageContent.{
347347+ data = image_base64;
348348+ mime_type = "image/png";
349349+ annotations = None
350350+ };
351351+ Audio AudioContent.{
352352+ data = audio_base64;
353353+ mime_type = "audio/wav";
354354+ annotations = None
355355+ }
356356+ ];
357357+ is_error = false;
358358+ meta = None
359359+ }
360360+ with
361361+ | Failure msg ->
362362+ Log.error (Printf.sprintf "Error in multimodal tool: %s" msg);
363363+ CallToolResult.yojson_of_t CallToolResult.{
364364+ content = [
365365+ Text TextContent.{
366366+ text = Printf.sprintf "Error: %s" msg;
367367+ annotations = None
368368+ }
369369+ ];
370370+ is_error = true;
371371+ meta = None
372372+ }
373373+ )
374374+375375+(* Define and register a multimodal prompt *)
376376+let _ = add_prompt server
377377+ ~name:"multimodal-prompt"
378378+ ~description:"A prompt that includes text, image, and audio"
379379+ ~arguments:[
380380+ ("message", Some "Text message to include", true);
381381+ ("color", Some "Color for the image (hex format #RRGGBB)", false);
382382+ ("frequency", Some "Frequency for the audio tone in Hz", false);
383383+ ]
384384+ (fun args ->
385385+ (* Parse parameters with defaults *)
386386+ let message =
387387+ try List.assoc "message" args
388388+ with Not_found -> "Hello, multimodal world!"
389389+ in
390390+391391+ let color =
392392+ try List.assoc "color" args
393393+ with Not_found -> "#0000FF"
394394+ in
395395+396396+ let frequency =
397397+ try int_of_string (List.assoc "frequency" args)
398398+ with _ -> 440
399399+ in
400400+401401+ (* Generate image *)
402402+ let image_data = ImageGenerator.generate_simple_image 100 100 color in
403403+ let image_base64 = Base64.encode image_data in
404404+405405+ (* Generate audio *)
406406+ let audio_data = AudioGenerator.generate_sine_wave
407407+ ~frequency:(float_of_int frequency)
408408+ ~duration:1.0
409409+ ~sample_rate:8000
410410+ ~amplitude:0.8 in
411411+ let audio_base64 = Base64.encode audio_data in
412412+413413+ (* Create a multimodal prompt *)
414414+ [
415415+ Prompt.{
416416+ role = `User;
417417+ content = make_text_content "Here's a multimodal message with text, image, and audio:"
418418+ };
419419+ Prompt.{
420420+ role = `User;
421421+ content = make_text_content message
422422+ };
423423+ Prompt.{
424424+ role = `User;
425425+ content = make_image_content image_base64 "image/png"
426426+ };
427427+ Prompt.{
428428+ role = `User;
429429+ content = make_audio_content audio_base64 "audio/wav"
430430+ };
431431+ Prompt.{
432432+ role = `Assistant;
433433+ content = make_text_content "I've received your multimodal message with text, image, and audio."
434434+ }
435435+ ]
436436+ )
437437+438438+(* Also register a resource prompt example *)
439439+let _ = add_prompt server
440440+ ~name:"resource-prompt"
441441+ ~description:"A prompt that includes embedded resources"
442442+ ~arguments:[
443443+ ("resource_id", Some "ID of the resource to include", true);
444444+ ]
445445+ (fun args ->
446446+ (* Sample resource texts *)
447447+ let resources = [
448448+ ("doc1", "This is the content of document 1.");
449449+ ("doc2", "Document 2 contains important information about OCaml.");
450450+ ("doc3", "Document 3 explains the MCP protocol in detail.");
451451+ ] in
452452+453453+ (* Get the requested resource *)
454454+ let resource_id =
455455+ try List.assoc "resource_id" args
456456+ with Not_found -> "doc1"
457457+ in
458458+459459+ (* Find the resource content *)
460460+ let resource_content =
461461+ try List.assoc resource_id resources
462462+ with Not_found -> Printf.sprintf "Resource '%s' not found" resource_id
463463+ in
464464+465465+ (* Create a prompt with embedded resource *)
466466+ [
467467+ Prompt.{
468468+ role = `User;
469469+ content = make_text_content (Printf.sprintf "Here's the content of resource %s:" resource_id)
470470+ };
471471+ Prompt.{
472472+ role = `User;
473473+ content = make_text_resource_content (Printf.sprintf "resource://%s" resource_id) resource_content ~mime_type:"text/plain" ()
474474+ };
475475+ Prompt.{
476476+ role = `User;
477477+ content = make_text_content "Please analyze this content."
478478+ };
479479+ Prompt.{
480480+ role = `Assistant;
481481+ content = make_text_content "I'll analyze the resource content for you."
482482+ }
483483+ ]
484484+ )
485485+486486+(* Main function *)
487487+let () =
488488+ (* Parse command line arguments *)
489489+ let transport_type = ref Stdio in
490490+ let args = [
491491+ ("--http", Arg.Unit (fun () -> transport_type := Http),
492492+ "Start server with HTTP transport (default is stdio)");
493493+ ] in
494494+ let usage_msg = "Usage: multimodal_example [--http]" in
495495+ Arg.parse args (fun _ -> ()) usage_msg;
496496+497497+ (* Configure the server with appropriate capabilities *)
498498+ let server = configure_server server ~with_tools:true ~with_resources:false ~with_prompts:true () in
499499+500500+ (* Create and start MCP server with the selected transport *)
501501+ let mcp_server = create ~server ~transport:!transport_type () in
502502+ start mcp_server;
+13-2
bin/resource_template_example.ml
···11open Mcp
22open Mcp_sdk
33+open Mcp_server
3445(* Helper for extracting string value from JSON *)
56let get_string_param json name =
···164165165166(* Main function *)
166167let () =
168168+ (* Parse command line arguments *)
169169+ let transport_type = ref Stdio in
170170+ let args = [
171171+ ("--http", Arg.Unit (fun () -> transport_type := Http),
172172+ "Start server with HTTP transport (default is stdio)");
173173+ ] in
174174+ let usage_msg = "Usage: resource_template_example [--http]" in
175175+ Arg.parse args (fun _ -> ()) usage_msg;
176176+167177 (* Instead of printing directly to stdout which messes up the JSON-RPC protocol,
168178 use the logging system which sends output to stderr *)
169179 Log.info "Starting ResourceTemplateServer...";
···171181 (* Configure the server with appropriate capabilities *)
172182 ignore (configure_server server ());
173183174174- (* Run the server *)
175175- run_server server184184+ (* Create and start MCP server with the selected transport *)
185185+ let mcp_server = create ~server ~transport:!transport_type () in
186186+ start mcp_server
···16161717 let log level msg =
1818 Printf.eprintf "[%s] %s\n" (string_of_level level) msg;
1919- flush stderr;
2020- Printf.printf "[%s] %s\n" (string_of_level level) msg;
2121- flush stdout
1919+ flush stderr
22202321 let debug = log Debug
2422 let info = log Info
···135133 let create_argument ~name ?description ?(required=false) () =
136134 { name; description; required }
137135136136+ let yojson_of_message { role; content } =
137137+ `Assoc [
138138+ ("role", Role.yojson_of_t role);
139139+ ("content", yojson_of_content content);
140140+ ]
141141+142142+ (* This function must match the structure expected by the PromptMessage module in mcp.ml *)
143143+ let message_of_yojson json =
144144+ match json with
145145+ | `Assoc fields -> begin
146146+ let role = match List.assoc_opt "role" fields with
147147+ | Some json -> begin
148148+ Role.t_of_yojson json
149149+ end
150150+ | None -> begin
151151+ raise (Json.Of_json ("Missing role field", `Assoc fields))
152152+ end
153153+ in
154154+ let content = match List.assoc_opt "content" fields with
155155+ | Some json -> begin
156156+ content_of_yojson json
157157+ end
158158+ | None -> begin
159159+ raise (Json.Of_json ("Missing content field", `Assoc fields))
160160+ end
161161+ in
162162+ { role; content }
163163+ end
164164+ | j -> begin
165165+ raise (Json.Of_json ("Expected object for PromptMessage", j))
166166+ end
167167+138168 let to_json prompt =
139169 let assoc = [
140170 ("name", `String prompt.name);
···171201let make_text_content text =
172202 Text (TextContent.{ text; annotations = None })
173203204204+let make_text_content_with_annotations text annotations =
205205+ Text (TextContent.{ text; annotations = Some annotations })
206206+207207+let make_image_content data mime_type =
208208+ Image (ImageContent.{ data; mime_type; annotations = None })
209209+210210+let make_image_content_with_annotations data mime_type annotations =
211211+ Image (ImageContent.{ data; mime_type; annotations = Some annotations })
212212+213213+let make_audio_content data mime_type =
214214+ Audio (AudioContent.{ data; mime_type; annotations = None })
215215+216216+let make_audio_content_with_annotations data mime_type annotations =
217217+ Audio (AudioContent.{ data; mime_type; annotations = Some annotations })
218218+219219+let make_text_resource_content uri text ?mime_type () =
220220+ Resource (EmbeddedResource.{
221221+ resource = `Text TextResourceContents.{ uri; text; mime_type };
222222+ annotations = None
223223+ })
224224+225225+let make_blob_resource_content uri blob ?mime_type () =
226226+ Resource (EmbeddedResource.{
227227+ resource = `Blob BlobResourceContents.{ uri; blob; mime_type };
228228+ annotations = None
229229+ })
230230+174231let make_tool_schema properties required =
175232 let props = List.map (fun (name, schema_type, description) ->
176233 (name, `Assoc [
···352409let set_shutdown_hook server hook =
353410 server.shutdown_hook <- Some hook
354411355355-(* Run the server *)
412412+(* Transport type for server *)
413413+type transport_type =
414414+ | Stdio (* Read/write to stdin/stdout *)
415415+ | Http (* HTTP server - to be implemented *)
416416+417417+(* Run server with stdio transport *)
356418let run_server server =
357419 (* Setup *)
358420 Printexc.record_backtrace true;
359359- set_binary_mode_out stdout false;
360421361361- Log.info (Printf.sprintf "%s server started" server.name);
422422+ Log.info (Printf.sprintf "%s server starting" server.name);
362423 Log.debug (Printf.sprintf "Protocol version: %s" server.protocol_version);
363424 Log.debug (Printf.sprintf "Server info: %s v%s" server.name server.version);
364425···371432 | Some hook -> hook ()
372433 | None -> ());
373434374374- Log.info "Server initialized and ready."435435+ (* This function will be replaced by a full implementation in the mcp_server module *)
436436+ Log.info "Server initialized and ready."
437437+438438+(* Placeholder for running server with different transports *)
439439+let run_server_with_transport server transport =
440440+ match transport with
441441+ | Http ->
442442+ Log.info "HTTP server not implemented in this version, using stdio instead";
443443+ run_server server
444444+ | Stdio ->
445445+ run_server server
+18-1
lib/mcp_sdk.mli
···86868787 val create : name:string -> ?description:string -> ?arguments:argument list -> handler:handler -> unit -> t
8888 val create_argument : name:string -> ?description:string -> ?required:bool -> unit -> argument
8989+ val yojson_of_message : message -> Json.t
9090+ val message_of_yojson : Json.t -> message
8991 val to_json : t -> Json.t
9092end
9193···139141(** Set shutdown hook *)
140142val set_shutdown_hook : server -> (unit -> unit) -> unit
141143142142-(** Run the server *)
144144+(** Run the server using stdio transport (legacy method) *)
143145val run_server : server -> unit
144146147147+(** Transport type for the server *)
148148+type transport_type =
149149+ | Stdio (** Read/write to stdin/stdout *)
150150+ | Http (** HTTP server - to be implemented *)
151151+152152+(** Create and start a server with the specified transport *)
153153+val run_server_with_transport : server -> transport_type -> unit
154154+145155(** Helper functions for creating common objects *)
146156val make_text_content : string -> content
157157+val make_text_content_with_annotations : string -> Annotated.annotation -> content
158158+val make_image_content : string -> string -> content
159159+val make_image_content_with_annotations : string -> string -> Annotated.annotation -> content
160160+val make_audio_content : string -> string -> content
161161+val make_audio_content_with_annotations : string -> string -> Annotated.annotation -> content
162162+val make_text_resource_content : string -> string -> ?mime_type:string -> unit -> content
163163+val make_blob_resource_content : string -> string -> ?mime_type:string -> unit -> content
147164val make_tool_schema : (string * string * string) list -> string list -> Json.t
+533
lib/mcp_server.ml
···11+open Mcp
22+open Mcp_sdk
33+44+(* MCP Server module for handling JSON-RPC communication *)
55+66+(** Server types *)
77+type transport_type =
88+ | Stdio (* Read/write to stdin/stdout *)
99+ | Http (* HTTP server - to be implemented *)
1010+1111+type t = {
1212+ server: Mcp_sdk.server;
1313+ transport: transport_type;
1414+ mutable running: bool;
1515+}
1616+1717+(** Process a single message *)
1818+let process_message server message =
1919+ try
2020+ Log.debug "Parsing message as JSONRPC message...";
2121+ match JSONRPCMessage.t_of_yojson message with
2222+ | JSONRPCMessage.Request req -> begin
2323+ Log.debug (Printf.sprintf "Received request with method: %s" req.method_);
2424+ match req.method_ with
2525+ | "initialize" -> begin
2626+ Log.debug "Processing initialize request";
2727+ let result = match req.params with
2828+ | Some params -> begin
2929+ Log.debug "Parsing initialize request params...";
3030+ let req_params = Initialize.Request.t_of_yojson params in
3131+ Log.debug (Printf.sprintf "Client info: %s v%s"
3232+ req_params.client_info.name
3333+ req_params.client_info.version);
3434+ Log.debug (Printf.sprintf "Client protocol version: %s" req_params.protocol_version);
3535+3636+ (* Check protocol version compatibility *)
3737+ if req_params.protocol_version <> server.protocol_version then begin
3838+ Log.debug (Printf.sprintf "Protocol version mismatch: client=%s server=%s"
3939+ req_params.protocol_version server.protocol_version);
4040+ end;
4141+4242+ Initialize.Result.create
4343+ ~capabilities:server.capabilities
4444+ ~server_info:Implementation.{ name = server.name; version = server.version }
4545+ ~protocol_version:server.protocol_version
4646+ ?instructions:(Some "MCP Server") (* TODO: Allow customization *)
4747+ ()
4848+ end
4949+ | None -> begin
5050+ Log.error "Missing params for initialize request";
5151+ Initialize.Result.create
5252+ ~capabilities:server.capabilities
5353+ ~server_info:Implementation.{ name = server.name; version = server.version }
5454+ ~protocol_version:server.protocol_version
5555+ ()
5656+ end
5757+ in
5858+ Some (create_response ~id:req.id ~result:(Initialize.Result.yojson_of_t result))
5959+ end
6060+6161+ | "tools/list" -> begin
6262+ Log.debug "Processing tools/list request";
6363+ let tools_json = List.map Mcp_sdk.Tool.to_json server.tools in
6464+ let result = `Assoc [("tools", `List tools_json)] in
6565+ Some (create_response ~id:req.id ~result)
6666+ end
6767+6868+ | "tools/call" -> begin
6969+ Log.debug "Processing tools/call request";
7070+ match req.params with
7171+ | Some (`Assoc params) -> begin
7272+ let name = match List.assoc_opt "name" params with
7373+ | Some (`String name) -> begin
7474+ Log.debug (Printf.sprintf "Tool name: %s" name);
7575+ name
7676+ end
7777+ | _ -> begin
7878+ Log.error "Missing or invalid 'name' parameter in tool call";
7979+ failwith "Missing or invalid 'name' parameter"
8080+ end
8181+ in
8282+ let args = match List.assoc_opt "arguments" params with
8383+ | Some args -> begin
8484+ Log.debug (Printf.sprintf "Tool arguments: %s" (Yojson.Safe.to_string args));
8585+ args
8686+ end
8787+ | _ -> begin
8888+ Log.debug "No arguments provided for tool call, using empty object";
8989+ `Assoc [] (* Empty arguments is valid *)
9090+ end
9191+ in
9292+ let progress_token = req.progress_token in
9393+9494+ (* Find the tool *)
9595+ let tool_opt = List.find_opt (fun t -> t.Mcp_sdk.Tool.name = name) server.tools in
9696+ match tool_opt with
9797+ | Some tool -> begin
9898+ Log.debug (Printf.sprintf "Found tool: %s" name);
9999+ let ctx = Mcp_sdk.Context.create
100100+ ?request_id:(Some req.id)
101101+ ~lifespan_context:server.lifespan_context
102102+ ()
103103+ in
104104+ ctx.progress_token <- progress_token;
105105+106106+ (* Call the handler *)
107107+ let result = match tool.handler ctx args with
108108+ | Ok json -> begin
109109+ `Assoc [
110110+ ("content", `List [Mcp.yojson_of_content (Text (TextContent.{
111111+ text = Yojson.Safe.to_string json;
112112+ annotations = None
113113+ }))]);
114114+ ("isError", `Bool false)
115115+ ]
116116+ end
117117+ | Error err -> begin
118118+ `Assoc [
119119+ ("content", `List [Mcp.yojson_of_content (Text (TextContent.{
120120+ text = err;
121121+ annotations = None
122122+ }))]);
123123+ ("isError", `Bool true)
124124+ ]
125125+ end
126126+ in
127127+ Some (create_response ~id:req.id ~result)
128128+ end
129129+ | None -> begin
130130+ Log.error (Printf.sprintf "Tool not found: %s" name);
131131+ let error_content = TextContent.{
132132+ text = Printf.sprintf "Unknown tool: %s" name;
133133+ annotations = None
134134+ } in
135135+ let result = `Assoc [
136136+ ("content", `List [Mcp.yojson_of_content (Text error_content)]);
137137+ ("isError", `Bool true)
138138+ ] in
139139+ Some (create_response ~id:req.id ~result)
140140+ end
141141+ end
142142+ | _ -> begin
143143+ Log.error "Invalid params format for tools/call";
144144+ Some (create_error ~id:req.id ~code:ErrorCode.invalid_params ~message:"Invalid params for tools/call" ())
145145+ end
146146+ end
147147+148148+ | "resources/list" -> begin
149149+ Log.debug "Processing resources/list request";
150150+ if server.resources <> [] then begin
151151+ let resources_json = List.map Mcp_sdk.Resource.to_json server.resources in
152152+ let result = `Assoc [("resources", `List resources_json)] in
153153+ Some (create_response ~id:req.id ~result)
154154+ end else begin
155155+ Some (create_error ~id:req.id ~code:ErrorCode.method_not_found ~message:"Resources not supported" ())
156156+ end
157157+ end
158158+159159+ | "prompts/list" -> begin
160160+ Log.debug "Processing prompts/list request";
161161+ if server.prompts <> [] then begin
162162+ let prompts_json = List.map Mcp_sdk.Prompt.to_json server.prompts in
163163+ let result = `Assoc [("prompts", `List prompts_json)] in
164164+ Some (create_response ~id:req.id ~result)
165165+ end else begin
166166+ Some (create_error ~id:req.id ~code:ErrorCode.method_not_found ~message:"Prompts not supported" ())
167167+ end
168168+ end
169169+170170+ | "prompts/get" -> begin
171171+ Log.debug "Processing prompts/get request";
172172+ if server.prompts <> [] then begin
173173+ match req.params with
174174+ | Some (`Assoc params) -> begin
175175+ (* Extract prompt name *)
176176+ let name = match List.assoc_opt "name" params with
177177+ | Some (`String name) -> begin
178178+ Log.debug (Printf.sprintf "Prompt name: %s" name);
179179+ name
180180+ end
181181+ | _ -> begin
182182+ Log.error "Missing or invalid 'name' parameter in prompt request";
183183+ failwith "Missing or invalid 'name' parameter"
184184+ end
185185+ in
186186+187187+ (* Extract arguments if any *)
188188+ let arguments = match List.assoc_opt "arguments" params with
189189+ | Some (`Assoc args) -> begin
190190+ Log.debug (Printf.sprintf "Prompt arguments: %s" (Yojson.Safe.to_string (`Assoc args)));
191191+ List.map (fun (k, v) ->
192192+ match v with
193193+ | `String s -> begin (k, s) end
194194+ | _ -> begin (k, Yojson.Safe.to_string v) end
195195+ ) args
196196+ end
197197+ | _ -> begin
198198+ []
199199+ end
200200+ in
201201+202202+ (* Find the prompt *)
203203+ let prompt_opt = List.find_opt (fun p -> p.Mcp_sdk.Prompt.name = name) server.prompts in
204204+ match prompt_opt with
205205+ | Some prompt -> begin
206206+ Log.debug (Printf.sprintf "Found prompt: %s" name);
207207+ let ctx = Mcp_sdk.Context.create
208208+ ?request_id:(Some req.id)
209209+ ~lifespan_context:server.lifespan_context
210210+ ()
211211+ in
212212+213213+ (* Call the prompt handler *)
214214+ match prompt.handler ctx arguments with
215215+ | Ok messages -> begin
216216+ Log.debug (Printf.sprintf "Prompt handler returned %d messages" (List.length messages));
217217+218218+ (* Important: We need to directly use yojson_of_message which preserves MIME types *)
219219+ let messages_json = List.map Prompt.yojson_of_message messages in
220220+221221+ (* Debug output *)
222222+ Log.debug (Printf.sprintf "Messages JSON: %s" (Yojson.Safe.to_string (`List messages_json)));
223223+224224+ (* Verify one message if available to check structure *)
225225+ if List.length messages > 0 then begin
226226+ let first_msg = List.hd messages in
227227+ let content_debug = match first_msg.content with
228228+ | Text t -> begin
229229+ Printf.sprintf "Text content: %s" t.text
230230+ end
231231+ | Image i -> begin
232232+ Printf.sprintf "Image content (mime: %s)" i.mime_type
233233+ end
234234+ | Audio a -> begin
235235+ Printf.sprintf "Audio content (mime: %s)" a.mime_type
236236+ end
237237+ | Resource r -> begin
238238+ "Resource content"
239239+ end
240240+ in
241241+ Log.debug (Printf.sprintf "First message content type: %s" content_debug);
242242+ end;
243243+244244+ let result = `Assoc [
245245+ ("messages", `List messages_json);
246246+ ("description", match prompt.description with
247247+ | Some d -> begin `String d end
248248+ | None -> begin `Null end)
249249+ ] in
250250+ Some (create_response ~id:req.id ~result)
251251+ end
252252+ | Error err -> begin
253253+ Log.error (Printf.sprintf "Error processing prompt: %s" err);
254254+ Some (create_error ~id:req.id ~code:ErrorCode.internal_error ~message:err ())
255255+ end
256256+ end
257257+ | None -> begin
258258+ Log.error (Printf.sprintf "Prompt not found: %s" name);
259259+ Some (create_error ~id:req.id ~code:ErrorCode.invalid_params ~message:(Printf.sprintf "Prompt not found: %s" name) ())
260260+ end
261261+ end
262262+ | _ -> begin
263263+ Log.error "Invalid params format for prompts/get";
264264+ Some (create_error ~id:req.id ~code:ErrorCode.invalid_params ~message:"Invalid params format" ())
265265+ end
266266+ end else begin
267267+ Some (create_error ~id:req.id ~code:ErrorCode.method_not_found ~message:"Prompts not supported" ())
268268+ end
269269+ end
270270+271271+ | "ping" -> begin
272272+ Log.debug "Processing ping request";
273273+ Some (create_response ~id:req.id ~result:(`Assoc []))
274274+ end
275275+276276+ | _ -> begin
277277+ Log.error (Printf.sprintf "Unknown method received: %s" req.method_);
278278+ Some (create_error ~id:req.id ~code:ErrorCode.method_not_found ~message:("Method not found: " ^ req.method_) ())
279279+ end
280280+ end
281281+282282+ | JSONRPCMessage.Notification notif -> begin
283283+ Log.debug (Printf.sprintf "Received notification with method: %s" notif.method_);
284284+ match notif.method_ with
285285+ | "notifications/initialized" -> begin
286286+ Log.debug "Client initialization complete - Server is now ready to receive requests";
287287+ None
288288+ end
289289+ | _ -> begin
290290+ Log.debug (Printf.sprintf "Ignoring notification: %s" notif.method_);
291291+ None
292292+ end
293293+ end
294294+295295+ | JSONRPCMessage.Response _ -> begin
296296+ Log.error "Unexpected response message received";
297297+ None
298298+ end
299299+300300+ | JSONRPCMessage.Error _ -> begin
301301+ Log.error "Unexpected error message received";
302302+ None
303303+ end
304304+ with
305305+ | Failure msg -> begin
306306+ Log.error (Printf.sprintf "JSON error in message processing: %s" msg);
307307+ None
308308+ end
309309+ | exc -> begin
310310+ Log.error (Printf.sprintf "Exception during message processing: %s" (Printexc.to_string exc));
311311+ Log.error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
312312+ None
313313+ end
314314+315315+(** Read a single message from stdin *)
316316+let read_stdio_message () =
317317+ try
318318+ Log.debug "Reading line from stdin...";
319319+ let line = read_line () in
320320+ if line = "" then begin
321321+ Log.debug "Empty line received, ignoring";
322322+ None
323323+ end else begin
324324+ Log.debug (Printf.sprintf "Raw input: %s" (String.sub line 0 (min 100 (String.length line))));
325325+ try
326326+ let json = Yojson.Safe.from_string line in
327327+ Log.debug "Successfully parsed JSON";
328328+ Some json
329329+ with
330330+ | Yojson.Json_error msg -> begin
331331+ Log.error (Printf.sprintf "Error parsing JSON: %s" msg);
332332+ Log.error (Printf.sprintf "Input was: %s" (String.sub line 0 (min 100 (String.length line))));
333333+ None
334334+ end
335335+ end
336336+ with
337337+ | End_of_file -> begin
338338+ Log.debug "End of file received on stdin";
339339+ None
340340+ end
341341+ | Sys_error msg -> begin
342342+ Log.error (Printf.sprintf "System error while reading: %s" msg);
343343+ None
344344+ end
345345+ | exc -> begin
346346+ Log.error (Printf.sprintf "Exception while reading: %s" (Printexc.to_string exc));
347347+ None
348348+ end
349349+350350+(** Run stdio server with enhanced error handling *)
351351+let rec run_stdio_server mcp_server =
352352+ try begin
353353+ if not mcp_server.running then begin
354354+ Log.debug "Server stopped";
355355+ ()
356356+ end else begin
357357+ match read_stdio_message () with
358358+ | Some json -> begin
359359+ Log.debug "Processing message...";
360360+ try begin
361361+ match process_message mcp_server.server json with
362362+ | Some response -> begin
363363+ let response_json = JSONRPCMessage.yojson_of_t response in
364364+ let response_str = Yojson.Safe.to_string response_json in
365365+ Log.debug (Printf.sprintf "Sending response: %s"
366366+ (String.sub response_str 0 (min 100 (String.length response_str))));
367367+ Printf.printf "%s\n" response_str;
368368+ flush stdout;
369369+ (* Give client time to process *)
370370+ Unix.sleepf 0.01;
371371+ end
372372+ | None -> begin
373373+ Log.debug "No response needed"
374374+ end
375375+ end with
376376+ | exc -> begin
377377+ Log.error (Printf.sprintf "ERROR in message processing: %s" (Printexc.to_string exc));
378378+ Log.error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
379379+ (* Try to extract ID and send an error response *)
380380+ try begin
381381+ let id_opt = match Yojson.Safe.Util.member "id" json with
382382+ | `Int i -> Some (`Int i)
383383+ | `String s -> Some (`String s)
384384+ | _ -> None
385385+ in
386386+ match id_opt with
387387+ | Some id -> begin
388388+ let error_resp = create_error ~id ~code:ErrorCode.internal_error ~message:(Printexc.to_string exc) () in
389389+ let error_json = JSONRPCMessage.yojson_of_t error_resp in
390390+ let error_str = Yojson.Safe.to_string error_json in
391391+ Printf.printf "%s\n" error_str;
392392+ flush stdout;
393393+ end
394394+ | None -> begin
395395+ Log.error "Could not extract request ID to send error response"
396396+ end
397397+ end with
398398+ | e -> begin
399399+ Log.error (Printf.sprintf "Failed to send error response: %s" (Printexc.to_string e))
400400+ end
401401+ end;
402402+ run_stdio_server mcp_server
403403+ end
404404+ | None -> begin
405405+ if mcp_server.running then begin
406406+ (* No message received, but server is still running *)
407407+ Unix.sleepf 0.1; (* Small sleep to prevent CPU spinning *)
408408+ run_stdio_server mcp_server
409409+ end else begin
410410+ Log.debug "Server stopped during message processing"
411411+ end
412412+ end
413413+ end
414414+ end with
415415+ | exc -> begin
416416+ Log.error (Printf.sprintf "FATAL ERROR in server main loop: %s" (Printexc.to_string exc));
417417+ Log.error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
418418+ (* Try to continue anyway *)
419419+ if mcp_server.running then begin
420420+ Unix.sleepf 0.1;
421421+ run_stdio_server mcp_server
422422+ end
423423+ end
424424+425425+(** Create an MCP server *)
426426+let create ~server ~transport () =
427427+ { server; transport; running = false }
428428+429429+(** HTTP server placeholder (to be fully implemented) *)
430430+let run_http_server mcp_server port =
431431+ Log.info (Printf.sprintf "%s HTTP server starting on port %d" mcp_server.server.name port);
432432+ Log.info "HTTP transport is a placeholder and not fully implemented yet";
433433+434434+ (* This would be where we'd set up cohttp server *)
435435+ (*
436436+ let callback _conn req body =
437437+ let uri = req |> Cohttp.Request.uri in
438438+ let meth = req |> Cohttp.Request.meth |> Cohttp.Code.string_of_method in
439439+440440+ (* Handle only POST /jsonrpc endpoint *)
441441+ match (meth, Uri.path uri) with
442442+ | "POST", "/jsonrpc" ->
443443+ (* Read the body *)
444444+ Cohttp_lwt.Body.to_string body >>= fun body_str ->
445445+446446+ (* Parse JSON *)
447447+ let json = try Some (Yojson.Safe.from_string body_str) with _ -> None in
448448+ match json with
449449+ | Some json_msg ->
450450+ (* Process the message *)
451451+ let response_opt = process_message mcp_server.server json_msg in
452452+ (match response_opt with
453453+ | Some response ->
454454+ let response_json = JSONRPCMessage.yojson_of_t response in
455455+ let response_str = Yojson.Safe.to_string response_json in
456456+ Cohttp_lwt_unix.Server.respond_string
457457+ ~status:`OK
458458+ ~body:response_str
459459+ ~headers:(Cohttp.Header.init_with "Content-Type" "application/json")
460460+ ()
461461+ | None ->
462462+ Cohttp_lwt_unix.Server.respond_string
463463+ ~status:`OK
464464+ ~body:"{}"
465465+ ~headers:(Cohttp.Header.init_with "Content-Type" "application/json")
466466+ ())
467467+ | None ->
468468+ Cohttp_lwt_unix.Server.respond_string
469469+ ~status:`Bad_request
470470+ ~body:"{\"error\":\"Invalid JSON\"}"
471471+ ~headers:(Cohttp.Header.init_with "Content-Type" "application/json")
472472+ ()
473473+ | _ ->
474474+ (* Return 404 for any other routes *)
475475+ Cohttp_lwt_unix.Server.respond_string
476476+ ~status:`Not_found
477477+ ~body:"Not found"
478478+ ()
479479+ in
480480+481481+ (* Create and start the server *)
482482+ let server = Cohttp_lwt_unix.Server.create
483483+ ~mode:(`TCP (`Port port))
484484+ (Cohttp_lwt_unix.Server.make ~callback ())
485485+ in
486486+487487+ (* Run the server *)
488488+ Lwt_main.run server
489489+ *)
490490+491491+ (* For now, just wait until the server is stopped *)
492492+ while mcp_server.running do
493493+ Unix.sleep 1
494494+ done
495495+496496+(** Start the server based on transport type *)
497497+let start server =
498498+ server.running <- true;
499499+500500+ (* Run startup hook if provided *)
501501+ (match server.server.startup_hook with
502502+ | Some hook -> begin hook () end
503503+ | None -> begin () end);
504504+505505+ (* Install signal handler *)
506506+ Sys.(set_signal sigint (Signal_handle (fun _ ->
507507+ Log.debug "Received interrupt signal, stopping server...";
508508+ server.running <- false
509509+ )));
510510+511511+ match server.transport with
512512+ | Stdio -> begin
513513+ (* Setup stdout and stderr *)
514514+ set_binary_mode_out stdout false;
515515+ Log.info (Printf.sprintf "%s server started with stdio transport" server.server.name);
516516+517517+ (* Run the server loop *)
518518+ run_stdio_server server
519519+ end
520520+ | Http -> begin
521521+ (* HTTP server placeholder *)
522522+ run_http_server server 8080
523523+ end
524524+525525+(** Stop the server *)
526526+let stop server =
527527+ Log.info "Stopping server...";
528528+ server.running <- false;
529529+530530+ (* Run shutdown hook if provided *)
531531+ match server.server.shutdown_hook with
532532+ | Some hook -> begin hook () end
533533+ | None -> begin () end
+54
lib/mcp_server.mli
···11+(** MCP Server module - full implementation *)
22+33+(** Transport type for server *)
44+type transport_type =
55+ | Stdio (** Read/write to stdin/stdout *)
66+ | Http (** HTTP server - to be implemented *)
77+88+(** Server type *)
99+type t = {
1010+ server: Mcp_sdk.server;
1111+ transport: transport_type;
1212+ mutable running: bool;
1313+}
1414+1515+(** Create an MCP server
1616+ @param server The Mcp_sdk server to use
1717+ @param transport The transport type to use
1818+*)
1919+val create : server:Mcp_sdk.server -> transport:transport_type -> unit -> t
2020+2121+(** Start the server
2222+ This function will block until the server is stopped.
2323+ @param server The server to start
2424+*)
2525+val start : t -> unit
2626+2727+(** Stop the server
2828+ This will set the running flag to false and invoke the shutdown hook.
2929+ @param server The server to stop
3030+*)
3131+val stop : t -> unit
3232+3333+(** Process a single message
3434+ @param server The Mcp_sdk server to use
3535+ @param message The JSON message to process
3636+ @return An optional response message
3737+*)
3838+val process_message : Mcp_sdk.server -> Yojson.Safe.t -> Mcp.JSONRPCMessage.t option
3939+4040+(** Run stdio server implementation
4141+ @param mcp_server The mcp_server to run
4242+*)
4343+val run_stdio_server : t -> unit
4444+4545+(** Read a message from stdio
4646+ @return An optional JSON message
4747+*)
4848+val read_stdio_message : unit -> Yojson.Safe.t option
4949+5050+(** Run HTTP server implementation (placeholder)
5151+ @param mcp_server The mcp_server to run
5252+ @param port The port to listen on
5353+*)
5454+val run_http_server : t -> int -> unit