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