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