Model Context Protocol in OCaml
1open Jsonrpc
2
3(* Error codes for JSON-RPC *)
4module ErrorCode = struct
5 type t =
6 | ParseError (* -32700 - Invalid JSON *)
7 | InvalidRequest (* -32600 - Invalid JSON-RPC request *)
8 | MethodNotFound (* -32601 - Method not available *)
9 | InvalidParams (* -32602 - Invalid method parameters *)
10 | InternalError (* -32603 - Internal JSON-RPC error *)
11 | ResourceNotFound (* -32002 - Custom MCP error: requested resource not found *)
12 | AuthRequired (* -32001 - Custom MCP error: authentication required *)
13 | CustomError of int (* For any other error codes *)
14
15 (* Convert the error code to its integer representation *)
16 let to_int = function
17 | ParseError -> -32700
18 | InvalidRequest -> -32600
19 | MethodNotFound -> -32601
20 | InvalidParams -> -32602
21 | InternalError -> -32603
22 | ResourceNotFound -> -32002
23 | AuthRequired -> -32001
24 | CustomError code -> code
25
26 (* Get error message for standard error codes *)
27 let to_message = function
28 | ParseError -> "Parse error"
29 | InvalidRequest -> "Invalid Request"
30 | MethodNotFound -> "Method not found"
31 | InvalidParams -> "Invalid params"
32 | InternalError -> "Internal error"
33 | ResourceNotFound -> "Resource not found"
34 | AuthRequired -> "Authentication required"
35 | CustomError _ -> "Error"
36end
37
38(* Protocol method strings *)
39module Method = struct
40 (* Initialization and lifecycle methods *)
41 let initialize = "initialize"
42 let initialized = "notifications/initialized"
43
44 (* Resource methods *)
45 let resources_list = "resources/list"
46 let resources_read = "resources/read"
47 let resources_templates_list = "resources/templates/list"
48 let resources_subscribe = "resources/subscribe"
49 let resources_list_changed = "notifications/resources/list_changed"
50 let resources_updated = "notifications/resources/updated"
51
52 (* Tool methods *)
53 let tools_list = "tools/list"
54 let tools_call = "tools/call"
55 let tools_list_changed = "notifications/tools/list_changed"
56
57 (* Prompt methods *)
58 let prompts_list = "prompts/list"
59 let prompts_get = "prompts/get"
60 let prompts_list_changed = "notifications/prompts/list_changed"
61
62 (* Progress notifications *)
63 let progress = "notifications/progress"
64end
65
66(* Common types *)
67
68module Role = struct
69 type t = [ `User | `Assistant ]
70
71 let to_string = function
72 | `User -> "user"
73 | `Assistant -> "assistant"
74
75 let of_string = function
76 | "user" -> `User
77 | "assistant" -> `Assistant
78 | s -> raise (Json.Of_json ("Unknown role: " ^ s, `String s))
79
80 let yojson_of_t t = `String (to_string t)
81 let t_of_yojson = function
82 | `String s -> of_string s
83 | j -> raise (Json.Of_json ("Expected string for Role", j))
84end
85
86module ProgressToken = struct
87 type t = [ `String of string | `Int of int ]
88
89 include (Id : Json.Jsonable.S with type t := t)
90end
91
92module RequestId = Id
93
94module Cursor = struct
95 type t = string
96
97 let yojson_of_t t = `String t
98 let t_of_yojson = function
99 | `String s -> s
100 | j -> raise (Json.Of_json ("Expected string for Cursor", j))
101end
102
103(* Annotations *)
104
105module Annotated = struct
106 type t = {
107 annotations: annotation option;
108 }
109 and annotation = {
110 audience: Role.t list option;
111 priority: float option;
112 }
113
114 let yojson_of_annotation { audience; priority } =
115 let assoc = [] in
116 let assoc = match audience with
117 | Some audience -> ("audience", `List (List.map Role.yojson_of_t audience)) :: assoc
118 | None -> assoc
119 in
120 let assoc = match priority with
121 | Some priority -> ("priority", `Float priority) :: assoc
122 | None -> assoc
123 in
124 `Assoc assoc
125
126 let annotation_of_yojson = function
127 | `Assoc fields ->
128 let audience = List.assoc_opt "audience" fields |> Option.map (function
129 | `List items -> List.map Role.t_of_yojson items
130 | j -> raise (Json.Of_json ("Expected list for audience", j))
131 ) in
132 let priority = List.assoc_opt "priority" fields |> Option.map (function
133 | `Float f -> f
134 | j -> raise (Json.Of_json ("Expected float for priority", j))
135 ) in
136 { audience; priority }
137 | j -> raise (Json.Of_json ("Expected object for annotation", j))
138
139 let yojson_of_t { annotations } =
140 match annotations with
141 | Some annotations -> `Assoc [ "annotations", yojson_of_annotation annotations ]
142 | None -> `Assoc []
143
144 let t_of_yojson = function
145 | `Assoc fields ->
146 let annotations = List.assoc_opt "annotations" fields |> Option.map annotation_of_yojson in
147 { annotations }
148 | j -> raise (Json.Of_json ("Expected object for Annotated", j))
149end
150
151(* Content types *)
152
153module TextContent = struct
154 type t = {
155 text: string;
156 annotations: Annotated.annotation option;
157 }
158
159 let yojson_of_t { text; annotations } =
160 let assoc = [
161 ("text", `String text);
162 ("type", `String "text");
163 ] in
164 let assoc = match annotations with
165 | Some annotations -> ("annotations", Annotated.yojson_of_annotation annotations) :: assoc
166 | None -> assoc
167 in
168 `Assoc assoc
169
170 let t_of_yojson = function
171 | `Assoc fields ->
172 let text = match List.assoc_opt "text" fields with
173 | Some (`String s) -> s
174 | _ -> raise (Json.Of_json ("Missing or invalid 'text' field", `Assoc fields))
175 in
176 let _ = match List.assoc_opt "type" fields with
177 | Some (`String "text") -> ()
178 | _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
179 in
180 let annotations = List.assoc_opt "annotations" fields |> Option.map Annotated.annotation_of_yojson in
181 { text; annotations }
182 | j -> raise (Json.Of_json ("Expected object for TextContent", j))
183end
184
185module ImageContent = struct
186 type t = {
187 data: string;
188 mime_type: string;
189 annotations: Annotated.annotation option;
190 }
191
192 let yojson_of_t { data; mime_type; annotations } =
193 let assoc = [
194 ("type", `String "image");
195 ("data", `String data);
196 ("mimeType", `String mime_type);
197 ] in
198 let assoc = match annotations with
199 | Some annotations -> ("annotations", Annotated.yojson_of_annotation annotations) :: assoc
200 | None -> assoc
201 in
202 `Assoc assoc
203
204 let t_of_yojson = function
205 | `Assoc fields ->
206 let data = match List.assoc_opt "data" fields with
207 | Some (`String s) -> s
208 | _ -> raise (Json.Of_json ("Missing or invalid 'data' field", `Assoc fields))
209 in
210 let mime_type = match List.assoc_opt "mimeType" fields with
211 | Some (`String s) -> s
212 | _ -> raise (Json.Of_json ("Missing or invalid 'mimeType' field", `Assoc fields))
213 in
214 let _ = match List.assoc_opt "type" fields with
215 | Some (`String "image") -> ()
216 | _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
217 in
218 let annotations = List.assoc_opt "annotations" fields |> Option.map Annotated.annotation_of_yojson in
219 { data; mime_type; annotations }
220 | j -> raise (Json.Of_json ("Expected object for ImageContent", j))
221end
222
223module AudioContent = struct
224 type t = {
225 data: string;
226 mime_type: string;
227 annotations: Annotated.annotation option;
228 }
229
230 let yojson_of_t { data; mime_type; annotations } =
231 let assoc = [
232 ("type", `String "audio");
233 ("data", `String data);
234 ("mimeType", `String mime_type);
235 ] in
236 let assoc = match annotations with
237 | Some annotations -> ("annotations", Annotated.yojson_of_annotation annotations) :: assoc
238 | None -> assoc
239 in
240 `Assoc assoc
241
242 let t_of_yojson = function
243 | `Assoc fields ->
244 let data = match List.assoc_opt "data" fields with
245 | Some (`String s) -> s
246 | _ -> raise (Json.Of_json ("Missing or invalid 'data' field", `Assoc fields))
247 in
248 let mime_type = match List.assoc_opt "mimeType" fields with
249 | Some (`String s) -> s
250 | _ -> raise (Json.Of_json ("Missing or invalid 'mimeType' field", `Assoc fields))
251 in
252 let _ = match List.assoc_opt "type" fields with
253 | Some (`String "audio") -> ()
254 | _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
255 in
256 let annotations = List.assoc_opt "annotations" fields |> Option.map Annotated.annotation_of_yojson in
257 { data; mime_type; annotations }
258 | j -> raise (Json.Of_json ("Expected object for AudioContent", j))
259end
260
261module ResourceContents = struct
262 type t = {
263 uri: string;
264 mime_type: string option;
265 }
266
267 let yojson_of_t { uri; mime_type } =
268 let assoc = [
269 ("uri", `String uri);
270 ] in
271 let assoc = match mime_type with
272 | Some mime_type -> ("mimeType", `String mime_type) :: assoc
273 | None -> assoc
274 in
275 `Assoc assoc
276
277 let t_of_yojson = function
278 | `Assoc fields ->
279 let uri = match List.assoc_opt "uri" fields with
280 | Some (`String s) -> s
281 | _ -> raise (Json.Of_json ("Missing or invalid 'uri' field", `Assoc fields))
282 in
283 let mime_type = List.assoc_opt "mimeType" fields |> Option.map (function
284 | `String s -> s
285 | j -> raise (Json.Of_json ("Expected string for mimeType", j))
286 ) in
287 { uri; mime_type }
288 | j -> raise (Json.Of_json ("Expected object for ResourceContents", j))
289end
290
291module TextResourceContents = struct
292 type t = {
293 uri: string;
294 text: string;
295 mime_type: string option;
296 }
297
298 let yojson_of_t { uri; text; mime_type } =
299 let assoc = [
300 ("uri", `String uri);
301 ("text", `String text);
302 ] in
303 let assoc = match mime_type with
304 | Some mime_type -> ("mimeType", `String mime_type) :: assoc
305 | None -> assoc
306 in
307 `Assoc assoc
308
309 let t_of_yojson = function
310 | `Assoc fields ->
311 let uri = match List.assoc_opt "uri" fields with
312 | Some (`String s) -> s
313 | _ -> raise (Json.Of_json ("Missing or invalid 'uri' field", `Assoc fields))
314 in
315 let text = match List.assoc_opt "text" fields with
316 | Some (`String s) -> s
317 | _ -> raise (Json.Of_json ("Missing or invalid 'text' field", `Assoc fields))
318 in
319 let mime_type = List.assoc_opt "mimeType" fields |> Option.map (function
320 | `String s -> s
321 | j -> raise (Json.Of_json ("Expected string for mimeType", j))
322 ) in
323 { uri; text; mime_type }
324 | j -> raise (Json.Of_json ("Expected object for TextResourceContents", j))
325end
326
327module BlobResourceContents = struct
328 type t = {
329 uri: string;
330 blob: string;
331 mime_type: string option;
332 }
333
334 let yojson_of_t { uri; blob; mime_type } =
335 let assoc = [
336 ("uri", `String uri);
337 ("blob", `String blob);
338 ] in
339 let assoc = match mime_type with
340 | Some mime_type -> ("mimeType", `String mime_type) :: assoc
341 | None -> assoc
342 in
343 `Assoc assoc
344
345 let t_of_yojson = function
346 | `Assoc fields ->
347 let uri = match List.assoc_opt "uri" fields with
348 | Some (`String s) -> s
349 | _ -> raise (Json.Of_json ("Missing or invalid 'uri' field", `Assoc fields))
350 in
351 let blob = match List.assoc_opt "blob" fields with
352 | Some (`String s) -> s
353 | _ -> raise (Json.Of_json ("Missing or invalid 'blob' field", `Assoc fields))
354 in
355 let mime_type = List.assoc_opt "mimeType" fields |> Option.map (function
356 | `String s -> s
357 | j -> raise (Json.Of_json ("Expected string for mimeType", j))
358 ) in
359 { uri; blob; mime_type }
360 | j -> raise (Json.Of_json ("Expected object for BlobResourceContents", j))
361end
362
363module EmbeddedResource = struct
364 type t = {
365 resource: [ `Text of TextResourceContents.t | `Blob of BlobResourceContents.t ];
366 annotations: Annotated.annotation option;
367 }
368
369 let yojson_of_t { resource; annotations } =
370 let resource_json = match resource with
371 | `Text txt -> TextResourceContents.yojson_of_t txt
372 | `Blob blob -> BlobResourceContents.yojson_of_t blob
373 in
374 let assoc = [
375 ("resource", resource_json);
376 ("type", `String "resource");
377 ] in
378 let assoc = match annotations with
379 | Some annotations -> ("annotations", Annotated.yojson_of_annotation annotations) :: assoc
380 | None -> assoc
381 in
382 `Assoc assoc
383
384 let t_of_yojson = function
385 | `Assoc fields ->
386 let _ = match List.assoc_opt "type" fields with
387 | Some (`String "resource") -> ()
388 | _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
389 in
390 let resource = match List.assoc_opt "resource" fields with
391 | Some (`Assoc res_fields) ->
392 if List.mem_assoc "text" res_fields then
393 `Text (TextResourceContents.t_of_yojson (`Assoc res_fields))
394 else if List.mem_assoc "blob" res_fields then
395 `Blob (BlobResourceContents.t_of_yojson (`Assoc res_fields))
396 else
397 raise (Json.Of_json ("Invalid resource content", `Assoc res_fields))
398 | _ -> raise (Json.Of_json ("Missing or invalid 'resource' field", `Assoc fields))
399 in
400 let annotations = List.assoc_opt "annotations" fields |> Option.map Annotated.annotation_of_yojson in
401 { resource; annotations }
402 | j -> raise (Json.Of_json ("Expected object for EmbeddedResource", j))
403end
404
405type content =
406 | Text of TextContent.t
407 | Image of ImageContent.t
408 | Audio of AudioContent.t
409 | Resource of EmbeddedResource.t
410
411let yojson_of_content = function
412 | Text t -> TextContent.yojson_of_t t
413 | Image i -> ImageContent.yojson_of_t i
414 | Audio a -> AudioContent.yojson_of_t a
415 | Resource r -> EmbeddedResource.yojson_of_t r
416
417let content_of_yojson = function
418 | `Assoc fields ->
419 (match List.assoc_opt "type" fields with
420 | Some (`String "text") -> Text (TextContent.t_of_yojson (`Assoc fields))
421 | Some (`String "image") -> Image (ImageContent.t_of_yojson (`Assoc fields))
422 | Some (`String "audio") -> Audio (AudioContent.t_of_yojson (`Assoc fields))
423 | Some (`String "resource") -> Resource (EmbeddedResource.t_of_yojson (`Assoc fields))
424 | _ -> raise (Json.Of_json ("Invalid or missing content type", `Assoc fields)))
425 | j -> raise (Json.Of_json ("Expected object for content", j))
426
427(* Message types *)
428
429module PromptMessage = struct
430 type t = {
431 role: Role.t;
432 content: content;
433 }
434
435 let yojson_of_t { role; content } =
436 `Assoc [
437 ("role", Role.yojson_of_t role);
438 ("content", yojson_of_content content);
439 ]
440
441 let t_of_yojson = function
442 | `Assoc fields ->
443 let role = match List.assoc_opt "role" fields with
444 | Some json -> Role.t_of_yojson json
445 | None -> raise (Json.Of_json ("Missing role field", `Assoc fields))
446 in
447 let content = match List.assoc_opt "content" fields with
448 | Some json -> content_of_yojson json
449 | None -> raise (Json.Of_json ("Missing content field", `Assoc fields))
450 in
451 { role; content }
452 | j -> raise (Json.Of_json ("Expected object for PromptMessage", j))
453end
454
455module SamplingMessage = struct
456 type t = {
457 role: Role.t;
458 content: [ `Text of TextContent.t | `Image of ImageContent.t | `Audio of AudioContent.t ];
459 }
460
461 let yojson_of_t { role; content } =
462 let content_json = match content with
463 | `Text t -> TextContent.yojson_of_t t
464 | `Image i -> ImageContent.yojson_of_t i
465 | `Audio a -> AudioContent.yojson_of_t a
466 in
467 `Assoc [
468 ("role", Role.yojson_of_t role);
469 ("content", content_json);
470 ]
471
472 let t_of_yojson = function
473 | `Assoc fields ->
474 let role = match List.assoc_opt "role" fields with
475 | Some json -> Role.t_of_yojson json
476 | None -> raise (Json.Of_json ("Missing role field", `Assoc fields))
477 in
478 let content = match List.assoc_opt "content" fields with
479 | Some (`Assoc content_fields) ->
480 (match List.assoc_opt "type" content_fields with
481 | Some (`String "text") -> `Text (TextContent.t_of_yojson (`Assoc content_fields))
482 | Some (`String "image") -> `Image (ImageContent.t_of_yojson (`Assoc content_fields))
483 | Some (`String "audio") -> `Audio (AudioContent.t_of_yojson (`Assoc content_fields))
484 | _ -> raise (Json.Of_json ("Invalid content type", `Assoc content_fields)))
485 | _ -> raise (Json.Of_json ("Missing or invalid content field", `Assoc fields))
486 in
487 { role; content }
488 | j -> raise (Json.Of_json ("Expected object for SamplingMessage", j))
489end
490
491(* Implementation info *)
492
493module Implementation = struct
494 type t = {
495 name: string;
496 version: string;
497 }
498
499 let yojson_of_t { name; version } =
500 `Assoc [
501 ("name", `String name);
502 ("version", `String version);
503 ]
504
505 let t_of_yojson = function
506 | `Assoc fields ->
507 let name = match List.assoc_opt "name" fields with
508 | Some (`String s) -> s
509 | _ -> raise (Json.Of_json ("Missing or invalid 'name' field", `Assoc fields))
510 in
511 let version = match List.assoc_opt "version" fields with
512 | Some (`String s) -> s
513 | _ -> raise (Json.Of_json ("Missing or invalid 'version' field", `Assoc fields))
514 in
515 { name; version }
516 | j -> raise (Json.Of_json ("Expected object for Implementation", j))
517end
518
519(* JSONRPC Message types *)
520
521module JSONRPCMessage = struct
522 type notification = {
523 method_: string;
524 params: Json.t option;
525 }
526
527 type request = {
528 id: RequestId.t;
529 method_: string;
530 params: Json.t option;
531 progress_token: ProgressToken.t option;
532 }
533
534 type response = {
535 id: RequestId.t;
536 result: Json.t;
537 }
538
539 type error = {
540 id: RequestId.t;
541 code: int;
542 message: string;
543 data: Json.t option;
544 }
545
546 type t =
547 | Notification of notification
548 | Request of request
549 | Response of response
550 | Error of error
551
552 let yojson_of_notification (n: notification) =
553 let assoc = [
554 ("jsonrpc", `String "2.0");
555 ("method", `String n.method_);
556 ] in
557 let assoc = match n.params with
558 | Some params -> ("params", params) :: assoc
559 | None -> assoc
560 in
561 `Assoc assoc
562
563 let yojson_of_request (r: request) =
564 let assoc = [
565 ("jsonrpc", `String "2.0");
566 ("id", Id.yojson_of_t r.id);
567 ("method", `String r.method_);
568 ] in
569 let assoc = match r.params with
570 | Some params ->
571 let params_json = match params with
572 | `Assoc fields ->
573 let fields = match r.progress_token with
574 | Some token ->
575 let meta = `Assoc [ "progressToken", ProgressToken.yojson_of_t token ] in
576 ("_meta", meta) :: fields
577 | None -> fields
578 in
579 `Assoc fields
580 | _ -> params
581 in
582 ("params", params_json) :: assoc
583 | None -> assoc
584 in
585 `Assoc assoc
586
587 let yojson_of_response (r: response) =
588 `Assoc [
589 ("jsonrpc", `String "2.0");
590 ("id", Id.yojson_of_t r.id);
591 ("result", r.result);
592 ]
593
594 let yojson_of_error (e: error) =
595 let error_assoc = [
596 ("code", `Int e.code);
597 ("message", `String e.message);
598 ] in
599 let error_assoc = match e.data with
600 | Some data -> ("data", data) :: error_assoc
601 | None -> error_assoc
602 in
603 `Assoc [
604 ("jsonrpc", `String "2.0");
605 ("id", Id.yojson_of_t e.id);
606 ("error", `Assoc error_assoc);
607 ]
608
609 let yojson_of_t = function
610 | Notification n -> yojson_of_notification n
611 | Request r -> yojson_of_request r
612 | Response r -> yojson_of_response r
613 | Error e -> yojson_of_error e
614
615 let notification_of_yojson = function
616 | `Assoc fields ->
617 let method_ = match List.assoc_opt "method" fields with
618 | Some (`String s) -> s
619 | _ -> raise (Json.Of_json ("Missing or invalid 'method' field", `Assoc fields))
620 in
621 let params = List.assoc_opt "params" fields in
622 { method_; params }
623 | j -> raise (Json.Of_json ("Expected object for notification", j))
624
625 let request_of_yojson = function
626 | `Assoc fields ->
627 let id = match List.assoc_opt "id" fields with
628 | Some id_json -> Id.t_of_yojson id_json
629 | _ -> raise (Json.Of_json ("Missing or invalid 'id' field", `Assoc fields))
630 in
631 let method_ = match List.assoc_opt "method" fields with
632 | Some (`String s) -> s
633 | _ -> raise (Json.Of_json ("Missing or invalid 'method' field", `Assoc fields))
634 in
635 let params = List.assoc_opt "params" fields in
636 let progress_token =
637 match params with
638 | Some (`Assoc param_fields) ->
639 (match List.assoc_opt "_meta" param_fields with
640 | Some (`Assoc meta_fields) ->
641 (match List.assoc_opt "progressToken" meta_fields with
642 | Some token_json -> Some (ProgressToken.t_of_yojson token_json)
643 | None -> None)
644 | _ -> None)
645 | _ -> None
646 in
647 { id; method_; params; progress_token }
648 | j -> raise (Json.Of_json ("Expected object for request", j))
649
650 let response_of_yojson = function
651 | `Assoc fields ->
652 let id = match List.assoc_opt "id" fields with
653 | Some id_json -> Id.t_of_yojson id_json
654 | _ -> raise (Json.Of_json ("Missing or invalid 'id' field", `Assoc fields))
655 in
656 let result = match List.assoc_opt "result" fields with
657 | Some result -> result
658 | _ -> raise (Json.Of_json ("Missing 'result' field", `Assoc fields))
659 in
660 { id; result }
661 | j -> raise (Json.Of_json ("Expected object for response", j))
662
663 let error_of_yojson = function
664 | `Assoc fields ->
665 let id = match List.assoc_opt "id" fields with
666 | Some id_json -> Id.t_of_yojson id_json
667 | _ -> raise (Json.Of_json ("Missing or invalid 'id' field", `Assoc fields))
668 in
669 let error = match List.assoc_opt "error" fields with
670 | Some (`Assoc error_fields) -> error_fields
671 | _ -> raise (Json.Of_json ("Missing or invalid 'error' field", `Assoc fields))
672 in
673 let code = match List.assoc_opt "code" error with
674 | Some (`Int code) -> code
675 | _ -> raise (Json.Of_json ("Missing or invalid 'code' field in error", `Assoc error))
676 in
677 let message = match List.assoc_opt "message" error with
678 | Some (`String msg) -> msg
679 | _ -> raise (Json.Of_json ("Missing or invalid 'message' field in error", `Assoc error))
680 in
681 let data = List.assoc_opt "data" error in
682 { id; code; message; data }
683 | j -> raise (Json.Of_json ("Expected object for error", j))
684
685 let t_of_yojson json =
686 match json with
687 | `Assoc fields ->
688 let _jsonrpc = match List.assoc_opt "jsonrpc" fields with
689 | Some (`String "2.0") -> ()
690 | _ -> raise (Json.Of_json ("Missing or invalid 'jsonrpc' field", json))
691 in
692 if List.mem_assoc "method" fields then
693 if List.mem_assoc "id" fields then
694 Request (request_of_yojson json)
695 else
696 Notification (notification_of_yojson json)
697 else if List.mem_assoc "result" fields then
698 Response (response_of_yojson json)
699 else if List.mem_assoc "error" fields then
700 Error (error_of_yojson json)
701 else
702 raise (Json.Of_json ("Invalid JSONRPC message format", json))
703 | j -> raise (Json.Of_json ("Expected object for JSONRPC message", j))
704
705 let create_notification ?(params=None) ~method_ () =
706 Notification { method_; params }
707
708 let create_request ?(params=None) ?(progress_token=None) ~id ~method_ () =
709 Request { id; method_; params; progress_token }
710
711 let create_response ~id ~result =
712 Response { id; result }
713
714 let create_error ~id ~code ~message ?(data=None) () =
715 Error { id; code; message; data }
716end
717
718(* MCP-specific request/response types *)
719
720module Initialize = struct
721 module Request = struct
722 type t = {
723 capabilities: Json.t; (* ClientCapabilities *)
724 client_info: Implementation.t;
725 protocol_version: string;
726 }
727
728 let yojson_of_t { capabilities; client_info; protocol_version } =
729 `Assoc [
730 ("capabilities", capabilities);
731 ("clientInfo", Implementation.yojson_of_t client_info);
732 ("protocolVersion", `String protocol_version);
733 ]
734
735 let t_of_yojson = function
736 | `Assoc fields ->
737 let capabilities = match List.assoc_opt "capabilities" fields with
738 | Some json -> json
739 | None -> raise (Json.Of_json ("Missing capabilities field", `Assoc fields))
740 in
741 let client_info = match List.assoc_opt "clientInfo" fields with
742 | Some json -> Implementation.t_of_yojson json
743 | None -> raise (Json.Of_json ("Missing clientInfo field", `Assoc fields))
744 in
745 let protocol_version = match List.assoc_opt "protocolVersion" fields with
746 | Some (`String s) -> s
747 | _ -> raise (Json.Of_json ("Missing or invalid protocolVersion field", `Assoc fields))
748 in
749 { capabilities; client_info; protocol_version }
750 | j -> raise (Json.Of_json ("Expected object for InitializeRequest", j))
751
752 let create ~capabilities ~client_info ~protocol_version =
753 { capabilities; client_info; protocol_version }
754
755 let to_jsonrpc ~id t =
756 let params = yojson_of_t t in
757 JSONRPCMessage.create_request ~id ~method_:"initialize" ~params:(Some params) ()
758 end
759
760 module Result = struct
761 type t = {
762 capabilities: Json.t; (* ServerCapabilities *)
763 server_info: Implementation.t;
764 protocol_version: string;
765 instructions: string option;
766 meta: Json.t option;
767 }
768
769 let yojson_of_t { capabilities; server_info; protocol_version; instructions; meta } =
770 let assoc = [
771 ("capabilities", capabilities);
772 ("serverInfo", Implementation.yojson_of_t server_info);
773 ("protocolVersion", `String protocol_version);
774 ] in
775 let assoc = match instructions with
776 | Some instr -> ("instructions", `String instr) :: assoc
777 | None -> assoc
778 in
779 let assoc = match meta with
780 | Some meta -> ("_meta", meta) :: assoc
781 | None -> assoc
782 in
783 `Assoc assoc
784
785 let t_of_yojson = function
786 | `Assoc fields ->
787 let capabilities = match List.assoc_opt "capabilities" fields with
788 | Some json -> json
789 | None -> raise (Json.Of_json ("Missing capabilities field", `Assoc fields))
790 in
791 let server_info = match List.assoc_opt "serverInfo" fields with
792 | Some json -> Implementation.t_of_yojson json
793 | None -> raise (Json.Of_json ("Missing serverInfo field", `Assoc fields))
794 in
795 let protocol_version = match List.assoc_opt "protocolVersion" fields with
796 | Some (`String s) -> s
797 | _ -> raise (Json.Of_json ("Missing or invalid protocolVersion field", `Assoc fields))
798 in
799 let instructions = match List.assoc_opt "instructions" fields with
800 | Some (`String s) -> Some s
801 | _ -> None
802 in
803 let meta = List.assoc_opt "_meta" fields in
804 { capabilities; server_info; protocol_version; instructions; meta }
805 | j -> raise (Json.Of_json ("Expected object for InitializeResult", j))
806
807 let create ~capabilities ~server_info ~protocol_version ?instructions ?meta () =
808 { capabilities; server_info; protocol_version; instructions; meta }
809
810 let to_jsonrpc ~id t =
811 JSONRPCMessage.create_response ~id ~result:(yojson_of_t t)
812 end
813end
814
815module Initialized = struct
816 module Notification = struct
817 type t = {
818 meta: Json.t option;
819 }
820
821 let yojson_of_t { meta } =
822 let assoc = [] in
823 let assoc = match meta with
824 | Some meta -> ("_meta", meta) :: assoc
825 | None -> assoc
826 in
827 `Assoc assoc
828
829 let t_of_yojson = function
830 | `Assoc fields ->
831 let meta = List.assoc_opt "_meta" fields in
832 { meta }
833 | j -> raise (Json.Of_json ("Expected object for InitializedNotification", j))
834
835 let create ?meta () = { meta }
836
837 let to_jsonrpc t =
838 let params = match yojson_of_t t with
839 | `Assoc [] -> None
840 | json -> Some json
841 in
842 JSONRPCMessage.create_notification ~method_:"notifications/initialized" ~params ()
843 end
844end
845
846
847(* Export the main interface for using the MCP protocol *)
848
849let parse_message json =
850 JSONRPCMessage.t_of_yojson json
851
852let create_notification = JSONRPCMessage.create_notification
853let create_request = JSONRPCMessage.create_request
854let create_response = JSONRPCMessage.create_response
855let create_error = JSONRPCMessage.create_error
856
857(* Content type constructors *)
858let make_text_content text =
859 Text (TextContent.{ text; annotations = None })
860
861let make_image_content data mime_type =
862 Image (ImageContent.{ data; mime_type; annotations = None })
863
864let make_audio_content data mime_type =
865 Audio (AudioContent.{ data; mime_type; annotations = None })
866
867let make_resource_text_content uri text mime_type =
868 Resource (EmbeddedResource.{
869 resource = `Text TextResourceContents.{ uri; text; mime_type };
870 annotations = None;
871 })
872
873let make_resource_blob_content uri blob mime_type =
874 Resource (EmbeddedResource.{
875 resource = `Blob BlobResourceContents.{ uri; blob; mime_type };
876 annotations = None;
877 })