···
Bytes.sub_string result 0 (loop 0 0)
+
(* Generate a simple GIF format image *)
let generate_random_image width height =
+
(* Ensure dimensions are reasonable *)
+
let width = min 256 (max 16 width) in
+
let height = min 256 (max 16 height) in
+
(* Create a buffer for GIF data *)
+
let buf = Buffer.create 1024 in
+
(* GIF Header - "GIF89a" *)
+
Buffer.add_string buf "GIF89a";
+
(* Logical Screen Descriptor *)
+
(* Width - 2 bytes little endian *)
+
Buffer.add_char buf (Char.chr (width land 0xff));
+
Buffer.add_char buf (Char.chr ((width lsr 8) land 0xff));
+
(* Height - 2 bytes little endian *)
+
Buffer.add_char buf (Char.chr (height land 0xff));
+
Buffer.add_char buf (Char.chr ((height lsr 8) land 0xff));
+
(* Packed fields - 1 byte:
+
Global Color Table Flag - 1 bit (1)
+
Color Resolution - 3 bits (7 = 8 bits per color)
+
Size of Global Color Table - 3 bits (2 = 8 colors) *)
+
Buffer.add_char buf (Char.chr 0xF2);
+
(* Background color index - 1 byte *)
+
Buffer.add_char buf (Char.chr 0);
+
(* Pixel aspect ratio - 1 byte *)
+
Buffer.add_char buf (Char.chr 0);
+
(* Global Color Table - 8 colors x 3 bytes (R,G,B) *)
+
(* Simple 8-color palette *)
+
Buffer.add_string buf "\xFF\xFF\xFF"; (* White (0) *)
+
Buffer.add_string buf "\xFF\x00\x00"; (* Red (1) *)
+
Buffer.add_string buf "\x00\xFF\x00"; (* Green (2) *)
+
Buffer.add_string buf "\x00\x00\xFF"; (* Blue (3) *)
+
Buffer.add_string buf "\xFF\xFF\x00"; (* Yellow (4) *)
+
Buffer.add_string buf "\xFF\x00\xFF"; (* Magenta (5) *)
+
Buffer.add_string buf "\x00\xFF\xFF"; (* Cyan (6) *)
+
Buffer.add_string buf "\x00\x00\x00"; (* Black (7) *)
+
(* Graphics Control Extension (optional) *)
+
Buffer.add_char buf (Char.chr 0x21); (* Extension Introducer *)
+
Buffer.add_char buf (Char.chr 0xF9); (* Graphic Control Label *)
+
Buffer.add_char buf (Char.chr 0x04); (* Block Size *)
+
Buffer.add_char buf (Char.chr 0x01); (* Packed field: 1 bit for transparency *)
+
Buffer.add_char buf (Char.chr 0x00); (* Delay time (1/100s) - 2 bytes *)
+
Buffer.add_char buf (Char.chr 0x00);
+
Buffer.add_char buf (Char.chr 0x00); (* Transparent color index *)
+
Buffer.add_char buf (Char.chr 0x00); (* Block terminator *)
+
Buffer.add_char buf (Char.chr 0x2C); (* Image Separator *)
+
Buffer.add_char buf (Char.chr 0x00); (* Left position - 2 bytes *)
+
Buffer.add_char buf (Char.chr 0x00);
+
Buffer.add_char buf (Char.chr 0x00); (* Top position - 2 bytes *)
+
Buffer.add_char buf (Char.chr 0x00);
+
(* Image width - 2 bytes little endian *)
+
Buffer.add_char buf (Char.chr (width land 0xff));
+
Buffer.add_char buf (Char.chr ((width lsr 8) land 0xff));
+
(* Image height - 2 bytes little endian *)
+
Buffer.add_char buf (Char.chr (height land 0xff));
+
Buffer.add_char buf (Char.chr ((height lsr 8) land 0xff));
+
(* Packed fields - 1 byte - no local color table *)
+
Buffer.add_char buf (Char.chr 0x00);
+
(* LZW Minimum Code Size - 1 byte *)
+
Buffer.add_char buf (Char.chr 0x03); (* Minimum code size 3 for 8 colors *)
+
(* Generate a simple image - a checkerboard pattern *)
+
let step = width / 8 in
+
let image_data = Buffer.create (width * height / 4) in
+
(* Very simple LZW compression - just store raw clear codes and color indexes *)
+
(* Start with Clear code *)
+
Buffer.add_char image_data (Char.chr 0x08); (* Clear code 8 *)
+
(* For very simple encoding, we'll just use a sequence of color indexes *)
+
for y = 0 to height - 1 do
+
for x = 0 to width - 1 do
+
(* Checkerboard pattern with different colors *)
+
if ((x / step) + (y / step)) mod 2 = 0 then
+
Buffer.add_char image_data (Char.chr color);
+
(* End with End of Information code *)
+
Buffer.add_char image_data (Char.chr 0x09);
+
(* Add image data blocks - GIF uses 255-byte max chunks *)
+
let data = Buffer.contents image_data in
+
let data_len = String.length data in
+
while !pos < data_len do
+
let chunk_size = min 255 (data_len - !pos) in
+
Buffer.add_char buf (Char.chr chunk_size);
+
for i = 0 to chunk_size - 1 do
+
Buffer.add_char buf (String.get data (!pos + i));
+
pos := !pos + chunk_size;
+
(* Zero-length block to end the image data *)
+
Buffer.add_char buf (Char.chr 0x00);
+
Buffer.add_char buf (Char.chr 0x3B);
+
(* Base64 encode the GIF data *)
+
Base64.encode (Buffer.contents buf)
(* Helper to write 32-bit little endian integer *)
let write_int32_le buf n =
···
let server = create_server
~name:"OCaml MCP Multimodal Example"
+
~protocol_version:"2024-11-05" () |>
(* Set default capabilities *)
configure_server server ~with_tools:true ~with_resources:true ~with_prompts:true ()
···
(* Create a multimodal tool result *)
+
~image:(Some (image_data, "image/gif"))
~audio:(Some (audio_data, "audio/wav"))
···
let image_data = generate_random_image width height in
+
[ImageContent { data = image_data; mime_type = "image/gif" }]
···
+
"mimeType": "image/gif"