Model Context Protocol in OCaml

Add Resource, ResourceTemplate, and Completion modules

- Added Resource module to represent server resources
- Added ResourceTemplate for URI templates
- Added ResourceReference and PromptReference for references
- Added Completion support with Request and Result modules
- Added helper functions for creating completion requests and responses

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+441 -2
lib
+334 -1
lib/mcp.ml
···
| j -> raise (Json.Of_json ("Expected object for CallToolResult", j))
end
(* Message types *)
module PromptMessage = struct
···
let create_notification = JSONRPCMessage.create_notification
let create_request = JSONRPCMessage.create_request
let create_response = JSONRPCMessage.create_response
-
let create_error = JSONRPCMessage.create_error
···
| j -> raise (Json.Of_json ("Expected object for CallToolResult", j))
end
+
(** Resource definition *)
+
module Resource = struct
+
type t = {
+
name: string;
+
uri: string;
+
description: string option;
+
mime_type: string option;
+
size: int option;
+
annotations: Annotated.annotation option;
+
}
+
+
let yojson_of_t { name; uri; description; mime_type; size; annotations } =
+
let assoc = [
+
("name", `String name);
+
("uri", `String uri);
+
] in
+
let assoc = match description with
+
| Some desc -> ("description", `String desc) :: assoc
+
| None -> assoc
+
in
+
let assoc = match mime_type with
+
| Some mime -> ("mimeType", `String mime) :: assoc
+
| None -> assoc
+
in
+
let assoc = match size with
+
| Some s -> ("size", `Int s) :: assoc
+
| None -> assoc
+
in
+
let assoc = match annotations with
+
| Some ann -> ("annotations", Annotated.yojson_of_annotation ann) :: assoc
+
| None -> assoc
+
in
+
`Assoc assoc
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let name = match List.assoc_opt "name" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'name' field", `Assoc fields))
+
in
+
let uri = match List.assoc_opt "uri" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'uri' field", `Assoc fields))
+
in
+
let description = match List.assoc_opt "description" fields with
+
| Some (`String s) -> Some s
+
| _ -> None
+
in
+
let mime_type = match List.assoc_opt "mimeType" fields with
+
| Some (`String s) -> Some s
+
| _ -> None
+
in
+
let size = match List.assoc_opt "size" fields with
+
| Some (`Int s) -> Some s
+
| _ -> None
+
in
+
let annotations = match List.assoc_opt "annotations" fields with
+
| Some json -> Some (Annotated.annotation_of_yojson json)
+
| _ -> None
+
in
+
{ name; uri; description; mime_type; size; annotations }
+
| j -> raise (Json.Of_json ("Expected object for Resource", j))
+
end
+
+
(** Resource Template definition *)
+
module ResourceTemplate = struct
+
type t = {
+
name: string;
+
uri_template: string;
+
description: string option;
+
mime_type: string option;
+
annotations: Annotated.annotation option;
+
}
+
+
let yojson_of_t { name; uri_template; description; mime_type; annotations } =
+
let assoc = [
+
("name", `String name);
+
("uriTemplate", `String uri_template);
+
] in
+
let assoc = match description with
+
| Some desc -> ("description", `String desc) :: assoc
+
| None -> assoc
+
in
+
let assoc = match mime_type with
+
| Some mime -> ("mimeType", `String mime) :: assoc
+
| None -> assoc
+
in
+
let assoc = match annotations with
+
| Some ann -> ("annotations", Annotated.yojson_of_annotation ann) :: assoc
+
| None -> assoc
+
in
+
`Assoc assoc
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let name = match List.assoc_opt "name" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'name' field", `Assoc fields))
+
in
+
let uri_template = match List.assoc_opt "uriTemplate" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'uriTemplate' field", `Assoc fields))
+
in
+
let description = match List.assoc_opt "description" fields with
+
| Some (`String s) -> Some s
+
| _ -> None
+
in
+
let mime_type = match List.assoc_opt "mimeType" fields with
+
| Some (`String s) -> Some s
+
| _ -> None
+
in
+
let annotations = match List.assoc_opt "annotations" fields with
+
| Some json -> Some (Annotated.annotation_of_yojson json)
+
| _ -> None
+
in
+
{ name; uri_template; description; mime_type; annotations }
+
| j -> raise (Json.Of_json ("Expected object for ResourceTemplate", j))
+
end
+
+
(** Resource Reference *)
+
module ResourceReference = struct
+
type t = {
+
uri: string;
+
}
+
+
let yojson_of_t { uri } =
+
`Assoc [
+
("type", `String "ref/resource");
+
("uri", `String uri);
+
]
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let _ = match List.assoc_opt "type" fields with
+
| Some (`String "ref/resource") -> ()
+
| _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
+
in
+
let uri = match List.assoc_opt "uri" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'uri' field", `Assoc fields))
+
in
+
{ uri }
+
| j -> raise (Json.Of_json ("Expected object for ResourceReference", j))
+
end
+
+
(** Prompt Reference *)
+
module PromptReference = struct
+
type t = {
+
name: string;
+
}
+
+
let yojson_of_t { name } =
+
`Assoc [
+
("type", `String "ref/prompt");
+
("name", `String name);
+
]
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let _ = match List.assoc_opt "type" fields with
+
| Some (`String "ref/prompt") -> ()
+
| _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
+
in
+
let name = match List.assoc_opt "name" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'name' field", `Assoc fields))
+
in
+
{ name }
+
| j -> raise (Json.Of_json ("Expected object for PromptReference", j))
+
end
+
+
(** Completion support *)
+
module Completion = struct
+
+
module Argument = struct
+
type t = {
+
name: string;
+
value: string;
+
}
+
+
let yojson_of_t { name; value } =
+
`Assoc [
+
("name", `String name);
+
("value", `String value);
+
]
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let name = match List.assoc_opt "name" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'name' field", `Assoc fields))
+
in
+
let value = match List.assoc_opt "value" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'value' field", `Assoc fields))
+
in
+
{ name; value }
+
| j -> raise (Json.Of_json ("Expected object for Completion.Argument", j))
+
end
+
+
module Request = struct
+
type reference = [ `Prompt of PromptReference.t | `Resource of ResourceReference.t ]
+
+
type t = {
+
argument: Argument.t;
+
ref: reference;
+
}
+
+
let yojson_of_reference = function
+
| `Prompt p -> PromptReference.yojson_of_t p
+
| `Resource r -> ResourceReference.yojson_of_t r
+
+
let reference_of_yojson = function
+
| `Assoc fields ->
+
(match List.assoc_opt "type" fields with
+
| Some (`String "ref/prompt") -> `Prompt (PromptReference.t_of_yojson (`Assoc fields))
+
| Some (`String "ref/resource") -> `Resource (ResourceReference.t_of_yojson (`Assoc fields))
+
| _ -> raise (Json.Of_json ("Invalid or missing reference type", `Assoc fields)))
+
| j -> raise (Json.Of_json ("Expected object for reference", j))
+
+
let yojson_of_t { argument; ref } =
+
`Assoc [
+
("argument", Argument.yojson_of_t argument);
+
("ref", yojson_of_reference ref);
+
]
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let argument = match List.assoc_opt "argument" fields with
+
| Some json -> Argument.t_of_yojson json
+
| _ -> raise (Json.Of_json ("Missing argument field", `Assoc fields))
+
in
+
let ref = match List.assoc_opt "ref" fields with
+
| Some json -> reference_of_yojson json
+
| _ -> raise (Json.Of_json ("Missing ref field", `Assoc fields))
+
in
+
{ argument; ref }
+
| j -> raise (Json.Of_json ("Expected object for Completion.Request", j))
+
+
let create ~argument ~ref =
+
{ argument; ref }
+
+
let to_params t =
+
yojson_of_t t
+
end
+
+
module Result = struct
+
type completion = {
+
values: string list;
+
has_more: bool option;
+
total: int option;
+
}
+
+
type t = {
+
completion: completion;
+
meta: Json.t option;
+
}
+
+
let yojson_of_completion { values; has_more; total } =
+
let assoc = [
+
("values", `List (List.map (fun s -> `String s) values));
+
] in
+
let assoc = match has_more with
+
| Some b -> ("hasMore", `Bool b) :: assoc
+
| None -> assoc
+
in
+
let assoc = match total with
+
| Some n -> ("total", `Int n) :: assoc
+
| None -> assoc
+
in
+
`Assoc assoc
+
+
let completion_of_yojson = function
+
| `Assoc fields ->
+
let values = match List.assoc_opt "values" fields with
+
| Some (`List items) ->
+
List.map (function
+
| `String s -> s
+
| _ -> raise (Json.Of_json ("Expected string in values array", `List items))
+
) items
+
| _ -> raise (Json.Of_json ("Missing or invalid 'values' field", `Assoc fields))
+
in
+
let has_more = match List.assoc_opt "hasMore" fields with
+
| Some (`Bool b) -> Some b
+
| None -> None
+
| _ -> raise (Json.Of_json ("Invalid 'hasMore' field", `Assoc fields))
+
in
+
let total = match List.assoc_opt "total" fields with
+
| Some (`Int n) -> Some n
+
| None -> None
+
| _ -> raise (Json.Of_json ("Invalid 'total' field", `Assoc fields))
+
in
+
{ values; has_more; total }
+
| j -> raise (Json.Of_json ("Expected object for completion", j))
+
+
let yojson_of_t { completion; meta } =
+
let assoc = [
+
("completion", yojson_of_completion completion);
+
] in
+
let assoc = match meta with
+
| Some meta_json -> ("_meta", meta_json) :: assoc
+
| None -> assoc
+
in
+
`Assoc assoc
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let completion = match List.assoc_opt "completion" fields with
+
| Some json -> completion_of_yojson json
+
| _ -> raise (Json.Of_json ("Missing completion field", `Assoc fields))
+
in
+
let meta = List.assoc_opt "_meta" fields in
+
{ completion; meta }
+
| j -> raise (Json.Of_json ("Expected object for Completion.Result", j))
+
+
let create ~completion ?meta () =
+
{ completion; meta }
+
+
let to_result t =
+
yojson_of_t t
+
end
+
end
+
(* Message types *)
module PromptMessage = struct
···
let create_notification = JSONRPCMessage.create_notification
let create_request = JSONRPCMessage.create_request
let create_response = JSONRPCMessage.create_response
+
let create_error = JSONRPCMessage.create_error
+
+
(* Helper functions *)
+
let create_completion_request ~id ~argument ~ref =
+
let params = Completion.Request.to_params { argument; ref } in
+
create_request ~id ~method_:"completion/complete" ~params:(Some params) ()
+
+
let create_completion_response ~id ~values ?(has_more=None) ?(total=None) ?(meta=None) () =
+
let completion = { Completion.Result.values; has_more; total } in
+
let result = Completion.Result.to_result { completion; meta } in
+
create_response ~id ~result
+107 -1
lib/mcp.mli
···
val t_of_yojson : Json.t -> t
end
(** Parse a JSON message into an MCP message *)
val parse_message : Json.t -> JSONRPCMessage.t
···
val create_notification : ?params:Json.t option -> method_:string -> unit -> JSONRPCMessage.t
val create_request : ?params:Json.t option -> ?progress_token:ProgressToken.t option -> id:RequestId.t -> method_:string -> unit -> JSONRPCMessage.t
val create_response : id:RequestId.t -> result:Json.t -> JSONRPCMessage.t
-
val create_error : id:RequestId.t -> code:int -> message:string -> ?data:Json.t option -> unit -> JSONRPCMessage.t
···
val t_of_yojson : Json.t -> t
end
+
(** Resource definition *)
+
module Resource : sig
+
type t = {
+
name: string;
+
uri: string;
+
description: string option;
+
mime_type: string option;
+
size: int option;
+
annotations: Annotated.annotation option;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
(** Resource Template definition *)
+
module ResourceTemplate : sig
+
type t = {
+
name: string;
+
uri_template: string;
+
description: string option;
+
mime_type: string option;
+
annotations: Annotated.annotation option;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
(** Resource Reference *)
+
module ResourceReference : sig
+
type t = {
+
uri: string;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
(** Prompt Reference *)
+
module PromptReference : sig
+
type t = {
+
name: string;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
(** Completion support *)
+
module Completion : sig
+
module Argument : sig
+
type t = {
+
name: string;
+
value: string;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
module Request : sig
+
type reference = [ `Prompt of PromptReference.t | `Resource of ResourceReference.t ]
+
+
type t = {
+
argument: Argument.t;
+
ref: reference;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
+
val yojson_of_reference : reference -> Json.t
+
val reference_of_yojson : Json.t -> reference
+
+
val create : argument:Argument.t -> ref:reference -> t
+
val to_params : t -> Json.t
+
end
+
+
module Result : sig
+
type completion = {
+
values: string list;
+
has_more: bool option;
+
total: int option;
+
}
+
+
type t = {
+
completion: completion;
+
meta: Json.t option;
+
}
+
+
val yojson_of_completion : completion -> Json.t
+
val completion_of_yojson : Json.t -> completion
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
+
val create : completion:completion -> ?meta:Json.t -> unit -> t
+
val to_result : t -> Json.t
+
end
+
end
+
(** Parse a JSON message into an MCP message *)
val parse_message : Json.t -> JSONRPCMessage.t
···
val create_notification : ?params:Json.t option -> method_:string -> unit -> JSONRPCMessage.t
val create_request : ?params:Json.t option -> ?progress_token:ProgressToken.t option -> id:RequestId.t -> method_:string -> unit -> JSONRPCMessage.t
val create_response : id:RequestId.t -> result:Json.t -> JSONRPCMessage.t
+
val create_error : id:RequestId.t -> code:int -> message:string -> ?data:Json.t option -> unit -> JSONRPCMessage.t
+
+
(** Helper functions for common requests/responses *)
+
val create_completion_request : id:RequestId.t -> argument:Completion.Argument.t -> ref:Completion.Request.reference -> JSONRPCMessage.t
+
val create_completion_response : id:RequestId.t -> values:string list -> ?has_more:bool option -> ?total:int option -> ?meta:Json.t option -> unit -> JSONRPCMessage.t