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