···
(* Helper for extracting string value from JSON *)
···
(match List.assoc_opt name fields with
| Some (`Int value) -> value
+
| Some (`String value) -> int_of_string value
| _ -> raise (Failure (Printf.sprintf "Missing or invalid parameter: %s" name)))
| _ -> raise (Failure "Expected JSON object")
···
Bytes.sub_string result 0 (loop 0 0)
+
(* Generate a random image as SVG format *)
let generate_random_image width height =
+
(* Helper to get random color with param control *)
+
let random_color ?(min=0) ?(max=255) () =
+
let r = min + Random.int (max - min + 1) in
+
let g = min + Random.int (max - min + 1) in
+
let b = min + Random.int (max - min + 1) in
+
Printf.sprintf "#%02x%02x%02x" r g b
+
(* Create SVG header *)
+
let svg_buffer = Buffer.create 10240 in
+
Buffer.add_string svg_buffer (Printf.sprintf
+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
+
<svg width=\"%d\" height=\"%d\" xmlns=\"http://www.w3.org/2000/svg\">\n\
+
<rect width=\"%d\" height=\"%d\" fill=\"%s\"/>\n"
+
width height width height (random_color ~min:200 ~max:240 ())
+
(* Generate different SVG shapes based on image size and randomness *)
+
let shape_count = (width * height) / 5000 + 5 in
+
(* Add random circles *)
+
for _ = 1 to shape_count / 2 do
+
let cx = Random.int width in
+
let cy = Random.int height in
+
let r = 5 + Random.int (min width height / 8) in
+
let color = random_color ~min:50 ~max:200 () in
+
let opacity = 0.3 +. (Random.float 0.7) in
+
Buffer.add_string svg_buffer (Printf.sprintf
+
"<circle cx=\"%d\" cy=\"%d\" r=\"%d\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
+
(* Add random rectangles *)
+
for _ = 1 to shape_count / 3 do
+
let x = Random.int width in
+
let y = Random.int height in
+
let w = 10 + Random.int (width / 5) in
+
let h = 10 + Random.int (height / 5) in
+
let color = random_color ~min:50 ~max:200 () in
+
let opacity = 0.2 +. (Random.float 0.6) in
+
let rx = 2 + Random.int 20 in (* Rounded corners *)
+
Buffer.add_string svg_buffer (Printf.sprintf
+
"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" rx=\"%d\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
+
x y w h rx color opacity
+
for _ = 1 to shape_count do
+
let x1 = Random.int width in
+
let y1 = Random.int height in
+
let x2 = Random.int width in
+
let y2 = Random.int height in
+
let stroke = random_color () in
+
let sw = 1 + Random.int 5 in
+
Buffer.add_string svg_buffer (Printf.sprintf
+
"<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke=\"%s\" stroke-width=\"%d\" />\n"
+
(* Add some random polygons *)
+
for _ = 1 to shape_count / 4 do
+
let points = 3 + Random.int 5 in (* 3 to 7 points *)
+
let cx = Random.int width in
+
let cy = Random.int height in
+
let radius = 10 + Random.int (min width height / 6) in
+
let points_str = Buffer.create 100 in
+
for i = 0 to points - 1 do
+
let angle = 2.0 *. Float.pi *. (float_of_int i) /. (float_of_int points) in
+
let px = cx + int_of_float (float_of_int radius *. cos angle) in
+
let py = cy + int_of_float (float_of_int radius *. sin angle) in
+
Buffer.add_string points_str (Printf.sprintf "%d,%d " px py);
+
let fill = random_color ~min:100 ~max:220 () in
+
let opacity = 0.2 +. Random.float 0.5 in
+
Buffer.add_string svg_buffer (Printf.sprintf
+
"<polygon points=\"%s\" fill=\"%s\" fill-opacity=\"%.2f\" />\n"
+
(Buffer.contents points_str) fill opacity
+
Buffer.add_string svg_buffer "</svg>";
+
(* Return the SVG directly, no need for Base64 since it's already text *)
+
Buffer.contents svg_buffer
+
(* Helper to write 32-bit little endian integer *)
+
let write_int32_le buf n =
+
Buffer.add_char buf (Char.chr (n land 0xff));
+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff));
+
Buffer.add_char buf (Char.chr ((n lsr 16) land 0xff));
+
Buffer.add_char buf (Char.chr ((n lsr 24) land 0xff))
+
(* Helper to write 16-bit little endian integer *)
+
let write_int16_le buf n =
+
Buffer.add_char buf (Char.chr (n land 0xff));
+
Buffer.add_char buf (Char.chr ((n lsr 8) land 0xff))
(* Generate a simple WAV file with sine wave *)
let generate_sine_wave_audio frequency duration =
let sample_rate = 8000 in
let num_samples = sample_rate * duration in
+
let header_buf = Buffer.create 44 in
+
(* Fill WAV header properly *)
+
Buffer.add_string header_buf "RIFF";
+
write_int32_le header_buf (36 + num_samples * 2); (* File size minus 8 *)
+
Buffer.add_string header_buf "WAVE";
+
Buffer.add_string header_buf "fmt ";
+
write_int32_le header_buf 16; (* Format chunk size *)
+
write_int16_le header_buf 1; (* PCM format *)
+
write_int16_le header_buf 1; (* Mono *)
+
write_int32_le header_buf sample_rate; (* Sample rate *)
+
write_int32_le header_buf (sample_rate * 2); (* Byte rate *)
+
write_int16_le header_buf 2; (* Block align *)
+
write_int16_le header_buf 16; (* Bits per sample *)
+
Buffer.add_string header_buf "data";
+
write_int32_le header_buf (num_samples * 2); (* Data size *)
(* Generate sine wave samples *)
+
let samples_buf = Buffer.create (num_samples * 2) in
let amplitude = 16384.0 in (* 16-bit with headroom *)
for i = 0 to num_samples - 1 do
···
(* Convert to 16-bit little-endian *)
let sample = if sample < 0 then sample + 65536 else sample in
+
write_int16_le samples_buf sample;
+
(* Combine header and samples, then encode as Base64 *)
+
let wav_data = Buffer.contents header_buf ^ Buffer.contents samples_buf in
let server = create_server
···
(* Create a multimodal tool result *)
+
~image:(Some (image_data, "image/svg+xml"))
~audio:(Some (audio_data, "audio/wav"))
···
let image_data = generate_random_image width height in
+
[ImageContent { data = image_data; mime_type = "image/svg+xml" }]
···
+
"mimeType": "image/svg+xml"
···
Random.self_init(); (* Initialize random generator *)
Eio_main.run @@ fun env ->
+
Mcp_server.run_server env server