Model Context Protocol in OCaml

Add AudioContent, Tool, and CallToolResult modules

- Added ErrorCode module with standard JSON-RPC error codes
- Added AudioContent module for audio content support
- Added Tool module for defining tools that can be called by clients
- Added CallToolResult module for handling tool call results
- Updated content type to include Audio content

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

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

Changed files
+175
lib
+126
lib/mcp.ml
···
open Jsonrpc
(* Common types *)
module Role = struct
···
| j -> raise (Json.Of_json ("Expected object for ImageContent", j))
end
module ResourceContents = struct
type t = {
uri: string;
···
| j -> raise (Json.Of_json ("Expected object for EmbeddedResource", j))
end
type content =
| Text of TextContent.t
| Image of ImageContent.t
| Resource of EmbeddedResource.t
let yojson_of_content = function
| Text t -> TextContent.yojson_of_t t
| Image i -> ImageContent.yojson_of_t i
| Resource r -> EmbeddedResource.yojson_of_t r
let content_of_yojson = function
···
(match List.assoc_opt "type" fields with
| Some (`String "text") -> Text (TextContent.t_of_yojson (`Assoc fields))
| Some (`String "image") -> Image (ImageContent.t_of_yojson (`Assoc fields))
| Some (`String "resource") -> Resource (EmbeddedResource.t_of_yojson (`Assoc fields))
| _ -> raise (Json.Of_json ("Invalid or missing content type", `Assoc fields)))
| j -> raise (Json.Of_json ("Expected object for content", j))
(* Message types *)
···
end
(* JSONRPC Message types *)
module JSONRPCMessage = struct
type notification = {
···
open Jsonrpc
+
(* Standard error codes *)
+
module ErrorCode = struct
+
let parse_error = -32700
+
let invalid_request = -32600
+
let method_not_found = -32601
+
let invalid_params = -32602
+
let internal_error = -32603
+
let resource_not_found = -32002
+
let server_error_start = -32000
+
let server_error_end = -32099
+
end
+
(* Common types *)
module Role = struct
···
| j -> raise (Json.Of_json ("Expected object for ImageContent", j))
end
+
module AudioContent = struct
+
type t = {
+
data: string;
+
mime_type: string;
+
annotations: Annotated.annotation option;
+
}
+
+
let yojson_of_t { data; mime_type; annotations } =
+
let assoc = [
+
("data", `String data);
+
("mimeType", `String mime_type);
+
("type", `String "audio");
+
] in
+
let assoc = match annotations with
+
| Some annotations -> ("annotations", Annotated.yojson_of_annotation annotations) :: assoc
+
| None -> assoc
+
in
+
`Assoc assoc
+
+
let t_of_yojson = function
+
| `Assoc fields ->
+
let data = match List.assoc_opt "data" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'data' field", `Assoc fields))
+
in
+
let mime_type = match List.assoc_opt "mimeType" fields with
+
| Some (`String s) -> s
+
| _ -> raise (Json.Of_json ("Missing or invalid 'mimeType' field", `Assoc fields))
+
in
+
let _ = match List.assoc_opt "type" fields with
+
| Some (`String "audio") -> ()
+
| _ -> raise (Json.Of_json ("Missing or invalid 'type' field", `Assoc fields))
+
in
+
let annotations = List.assoc_opt "annotations" fields |> Option.map Annotated.annotation_of_yojson in
+
{ data; mime_type; annotations }
+
| j -> raise (Json.Of_json ("Expected object for AudioContent", j))
+
end
+
module ResourceContents = struct
type t = {
uri: string;
···
| j -> raise (Json.Of_json ("Expected object for EmbeddedResource", j))
end
+
(** Tool definition *)
+
module Tool = struct
+
type t = {
+
name: string;
+
description: string option;
+
input_schema: Json.t;
+
}
+
+
let yojson_of_t { name; description; input_schema } =
+
let assoc = [
+
("name", `String name);
+
("inputSchema", input_schema);
+
] in
+
let assoc = match description with
+
| Some desc -> ("description", `String desc) :: 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 description = match List.assoc_opt "description" fields with
+
| Some (`String s) -> Some s
+
| _ -> None
+
in
+
let input_schema = match List.assoc_opt "inputSchema" fields with
+
| Some json -> json
+
| _ -> raise (Json.Of_json ("Missing 'inputSchema' field", `Assoc fields))
+
in
+
{ name; description; input_schema }
+
| j -> raise (Json.Of_json ("Expected object for Tool", j))
+
end
+
type content =
| Text of TextContent.t
| Image of ImageContent.t
+
| Audio of AudioContent.t
| Resource of EmbeddedResource.t
let yojson_of_content = function
| Text t -> TextContent.yojson_of_t t
| Image i -> ImageContent.yojson_of_t i
+
| Audio a -> AudioContent.yojson_of_t a
| Resource r -> EmbeddedResource.yojson_of_t r
let content_of_yojson = function
···
(match List.assoc_opt "type" fields with
| Some (`String "text") -> Text (TextContent.t_of_yojson (`Assoc fields))
| Some (`String "image") -> Image (ImageContent.t_of_yojson (`Assoc fields))
+
| Some (`String "audio") -> Audio (AudioContent.t_of_yojson (`Assoc fields))
| Some (`String "resource") -> Resource (EmbeddedResource.t_of_yojson (`Assoc fields))
| _ -> raise (Json.Of_json ("Invalid or missing content type", `Assoc fields)))
| j -> raise (Json.Of_json ("Expected object for content", j))
+
+
(** Tool result *)
+
module CallToolResult = struct
+
type t = {
+
content: content list;
+
is_error: bool;
+
meta: Json.t option;
+
}
+
+
let yojson_of_t { content; is_error; meta } =
+
let assoc = [
+
("content", `List (List.map yojson_of_content content));
+
("isError", `Bool is_error);
+
] 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 content = match List.assoc_opt "content" fields with
+
| Some (`List items) -> List.map content_of_yojson items
+
| _ -> raise (Json.Of_json ("Missing or invalid 'content' field", `Assoc fields))
+
in
+
let is_error = match List.assoc_opt "isError" fields with
+
| Some (`Bool b) -> b
+
| None -> false (* Default to false if not specified *)
+
| _ -> raise (Json.Of_json ("Invalid 'isError' field", `Assoc fields))
+
in
+
let meta = List.assoc_opt "_meta" fields in
+
{ content; is_error; meta }
+
| j -> raise (Json.Of_json ("Expected object for CallToolResult", j))
+
end
(* Message types *)
···
end
(* JSONRPC Message types *)
+
module JSONRPCMessage = struct
type notification = {
+49
lib/mcp.mli
···
open Jsonrpc
(** Common types *)
(** Roles for conversation participants *)
···
val t_of_yojson : Json.t -> t
end
(** Base resource contents *)
module ResourceContents : sig
type t = {
···
type content =
| Text of TextContent.t
| Image of ImageContent.t
| Resource of EmbeddedResource.t
val yojson_of_content : content -> Json.t
···
val create : ?meta:Json.t -> unit -> t
val to_jsonrpc : t -> JSONRPCMessage.t
end
end
(** Parse a JSON message into an MCP message *)
···
open Jsonrpc
+
(** Standard error codes *)
+
module ErrorCode : sig
+
val parse_error : int
+
val invalid_request : int
+
val method_not_found : int
+
val invalid_params : int
+
val internal_error : int
+
val resource_not_found : int
+
val server_error_start : int
+
val server_error_end : int
+
end
+
(** Common types *)
(** Roles for conversation participants *)
···
val t_of_yojson : Json.t -> t
end
+
(** Audio content *)
+
module AudioContent : sig
+
type t = {
+
data: string;
+
mime_type: string;
+
annotations: Annotated.annotation option;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
(** Base resource contents *)
module ResourceContents : sig
type t = {
···
type content =
| Text of TextContent.t
| Image of ImageContent.t
+
| Audio of AudioContent.t
| Resource of EmbeddedResource.t
val yojson_of_content : content -> Json.t
···
val create : ?meta:Json.t -> unit -> t
val to_jsonrpc : t -> JSONRPCMessage.t
end
+
end
+
+
(** Tool definition *)
+
module Tool : sig
+
type t = {
+
name: string;
+
description: string option;
+
input_schema: Json.t;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
+
end
+
+
(** Tool result *)
+
module CallToolResult : sig
+
type t = {
+
content: content list;
+
is_error: bool;
+
meta: Json.t option;
+
}
+
+
val yojson_of_t : t -> Json.t
+
val t_of_yojson : Json.t -> t
end
(** Parse a JSON message into an MCP message *)