···11+# MCP Server Session API
22+33+The `Server_session` module provides a high-level, easy-to-use API for creating MCP (Model Context Protocol) servers in OCaml using Eio.
44+55+## Overview
66+77+This module handles:
88+- **Initialization handshake**: Automatically handles the MCP initialization protocol
99+- **Request routing**: Routes incoming requests to your handler functions
1010+- **Response encoding**: Automatically encodes responses using the correct MCP message types
1111+- **Notification sending**: Provides convenient functions for sending notifications to clients
1212+- **Error handling**: Returns proper JSON-RPC error responses for missing handlers
1313+1414+## Quick Start
1515+1616+```ocaml
1717+open Mcp
1818+1919+(* Define your handlers *)
2020+let list_tools ~cursor:_ =
2121+ let tool = Messages.Tools.make_tool
2222+ ~name:"my_tool"
2323+ ~description:"An example tool"
2424+ ~input_schema:(Jsont.Object ([], Jsont.Meta.none))
2525+ ()
2626+ in
2727+ Messages.Tools.make_list_result ~tools:[tool] ()
2828+2929+let call_tool ~name ~arguments =
3030+ match name with
3131+ | "my_tool" ->
3232+ Messages.Tools.make_call_result
3333+ ~content:[Content.text "Tool result"]
3434+ ()
3535+ | _ ->
3636+ Messages.Tools.make_call_result
3737+ ~content:[Content.text "Unknown tool"]
3838+ ~is_error:true
3939+ ()
4040+4141+(* Configure your server *)
4242+let config = {
4343+ Server_session.server_info = Capabilities.Implementation.make
4444+ ~name:"my-server"
4545+ ~version:"1.0.0";
4646+ server_capabilities = Capabilities.Server.make
4747+ ~tools:(Capabilities.Tools.make ())
4848+ ();
4949+ instructions = Some "My MCP server";
5050+}
5151+5252+let handlers = {
5353+ Server_session.list_tools = Some list_tools;
5454+ call_tool = Some call_tool;
5555+ list_resources = None;
5656+ list_resource_templates = None;
5757+ read_resource = None;
5858+ subscribe_resource = None;
5959+ unsubscribe_resource = None;
6060+ list_prompts = None;
6161+ get_prompt = None;
6262+ complete = None;
6363+ ping = None;
6464+}
6565+6666+(* Start the server *)
6767+let () =
6868+ Eio_main.run @@ fun env ->
6969+ Eio.Switch.run @@ fun sw ->
7070+ let transport = (* create your transport *) in
7171+ let server = Server_session.create
7272+ ~sw
7373+ ~transport
7474+ config
7575+ handlers
7676+ in
7777+ (* Server is now running *)
7878+```
7979+8080+## Architecture
8181+8282+### Initialization Flow
8383+8484+1. **Client sends Initialize request** → Server stores client capabilities and info
8585+2. **Server responds with capabilities** → Returns server capabilities and info
8686+3. **Client sends Initialized notification** → Server marks initialization complete
8787+4. **Server is ready** → Now accepts requests and can send notifications
8888+8989+### Request Handling
9090+9191+When a request arrives:
9292+9393+1. The method name is matched against handler types
9494+2. If a handler exists, it's called with the decoded parameters
9595+3. The result is encoded and sent back
9696+4. If no handler exists, a METHOD_NOT_FOUND error is returned
9797+9898+### Error Handling
9999+100100+The module automatically handles:
101101+- Missing handlers → METHOD_NOT_FOUND error
102102+- Invalid params → INVALID_PARAMS error
103103+- Handler exceptions → INTERNAL_ERROR with exception message
104104+- Pre-initialization requests → Rejected with error
105105+106106+## Supported Capabilities
107107+108108+The server can implement any combination of these capabilities:
109109+110110+### Resources
111111+- `list_resources`: List available resources with optional pagination
112112+- `list_resource_templates`: List resource templates
113113+- `read_resource`: Read resource contents by URI
114114+- `subscribe_resource`: Subscribe to resource updates
115115+- `unsubscribe_resource`: Unsubscribe from updates
116116+117117+Notifications you can send:
118118+- `send_resource_updated`: Notify about a specific resource update
119119+- `send_resource_list_changed`: Notify that the resource list changed
120120+121121+### Tools
122122+- `list_tools`: List available tools with optional pagination
123123+- `call_tool`: Execute a tool by name with arguments
124124+125125+Notifications you can send:
126126+- `send_tool_list_changed`: Notify that the tool list changed
127127+128128+### Prompts
129129+- `list_prompts`: List available prompts with optional pagination
130130+- `get_prompt`: Get a prompt by name with arguments
131131+132132+Notifications you can send:
133133+- `send_prompt_list_changed`: Notify that the prompt list changed
134134+135135+### Other
136136+- `complete`: Auto-completion suggestions
137137+- `ping`: Keepalive handler
138138+139139+### Logging
140140+- `send_log_message`: Send log messages to the client
141141+142142+### Progress
143143+- `send_progress`: Send progress updates for long-running operations
144144+145145+## Handler Signatures
146146+147147+All handlers return strongly-typed message results:
148148+149149+```ocaml
150150+type handlers = {
151151+ list_resources : (cursor:string option -> Messages.Resources.list_result) option;
152152+ read_resource : (uri:string -> Messages.Resources.read_result) option;
153153+ list_tools : (cursor:string option -> Messages.Tools.list_result) option;
154154+ call_tool : (name:string -> arguments:Jsont.json option -> Messages.Tools.call_result) option;
155155+ list_prompts : (cursor:string option -> Messages.Prompts.list_result) option;
156156+ get_prompt : (name:string -> arguments:(string * string) list option -> Messages.Prompts.get_result) option;
157157+ complete : (ref_:Messages.Completions.completion_ref -> argument:string -> Messages.Completions.result) option;
158158+ ping : (unit -> unit) option;
159159+ (* ... *)
160160+}
161161+```
162162+163163+Set a handler to `None` if you don't support that operation.
164164+165165+## Accessing Client Information
166166+167167+After initialization, you can query client capabilities:
168168+169169+```ocaml
170170+let server = Server_session.create ~sw ~transport config handlers in
171171+172172+(* Get client capabilities *)
173173+let client_caps = Server_session.client_capabilities server in
174174+match client_caps.roots with
175175+| Some _ -> (* Client supports roots *)
176176+| None -> (* Client doesn't support roots *)
177177+178178+(* Get client info *)
179179+let client_info = Server_session.client_info server in
180180+Printf.printf "Connected to: %s v%s\n"
181181+ client_info.name
182182+ client_info.version;
183183+184184+(* Get protocol version *)
185185+let version = Server_session.protocol_version server in
186186+```
187187+188188+## Sending Notifications
189189+190190+Send notifications to inform clients of changes:
191191+192192+```ocaml
193193+(* Resource was updated *)
194194+Server_session.send_resource_updated server ~uri:"file:///example.txt";
195195+196196+(* Resource list changed *)
197197+Server_session.send_resource_list_changed server;
198198+199199+(* Tool list changed *)
200200+Server_session.send_tool_list_changed server;
201201+202202+(* Log a message *)
203203+let log_data = Jsont.String ("Something happened", Jsont.Meta.none) in
204204+Server_session.send_log_message server
205205+ ~level:Messages.Logging.Info
206206+ ~data:log_data
207207+ ();
208208+209209+(* Report progress *)
210210+Server_session.send_progress server
211211+ ~progress_token:"operation-123"
212212+ ~progress:0.5
213213+ ~total:100.0
214214+ ();
215215+```
216216+217217+## Requesting from Client
218218+219219+Some servers may need to request information from clients:
220220+221221+```ocaml
222222+(* Request the list of roots (if client supports it) *)
223223+match Server_session.request_roots_list server with
224224+| Some result ->
225225+ List.iter (fun root ->
226226+ Printf.printf "Root: %s\n" root.Messages.Roots.uri
227227+ ) result.roots
228228+| None ->
229229+ (* Client doesn't support roots capability *)
230230+ ()
231231+```
232232+233233+## Example: Simple Tool Server
234234+235235+See `examples/mcp_server_example.ml` for a complete example that demonstrates:
236236+- Tool implementation (add, echo)
237237+- Resource serving (example://greeting)
238238+- Proper capability declaration
239239+- Handler implementation
240240+241241+## Implementation Notes
242242+243243+### Thread Safety
244244+The module uses Eio structured concurrency. All operations are safe within the same Eio domain. The Session module handles concurrent requests using Eio fibers.
245245+246246+### Timeout Support
247247+You can configure request timeouts:
248248+249249+```ocaml
250250+let server = Server_session.create
251251+ ~sw
252252+ ~transport
253253+ ~timeout:30.0 (* 30 second timeout *)
254254+ ~clock:(Session.C (Eio.Stdenv.clock env))
255255+ config
256256+ handlers
257257+```
258258+259259+### Error Propagation
260260+- Handler exceptions are caught and converted to INTERNAL_ERROR responses
261261+- The server continues running after handler errors
262262+- Use proper error handling in your handlers for better error messages
263263+264264+### Shutdown
265265+The server runs as long as the Eio switch is active:
266266+267267+```ocaml
268268+(* Explicit close *)
269269+Server_session.close server;
270270+271271+(* Or let the switch handle cleanup *)
272272+Eio.Switch.run @@ fun sw ->
273273+ let server = Server_session.create ~sw ~transport config handlers in
274274+ (* server auto-closes when switch exits *)
275275+```
276276+277277+## Related Modules
278278+279279+- `Session`: Low-level bidirectional JSON-RPC session
280280+- `Messages`: MCP protocol message types
281281+- `Capabilities`: Capability negotiation types
282282+- `Transport`: Transport layer abstraction
283283+- `Content`: Content block types (text, image, etc.)
284284+285285+## References
286286+287287+- [MCP Specification](https://spec.modelcontextprotocol.io/)
288288+- [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/)
+284
claudeio/lib_mcp/capabilities.ml
···11+(** MCP Capability negotiation types *)
22+33+(* Implementation Info *)
44+55+module Implementation = struct
66+ type t = {
77+ name : string;
88+ version : string;
99+ unknown : Jsont.json;
1010+ }
1111+1212+ let make ~name ~version =
1313+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
1414+ { name; version; unknown }
1515+1616+ let jsont : t Jsont.t =
1717+ let make name version unknown = { name; version; unknown } in
1818+ Jsont.Object.map ~kind:"Implementation" make
1919+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun i -> i.name)
2020+ |> Jsont.Object.mem "version" Jsont.string ~enc:(fun i -> i.version)
2121+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun i -> i.unknown)
2222+ |> Jsont.Object.finish
2323+2424+ let pp fmt i =
2525+ Format.fprintf fmt "%s/%s" i.name i.version
2626+end
2727+2828+(* Client Capabilities *)
2929+3030+module Sampling = struct
3131+ type t = {
3232+ context : bool option;
3333+ tools : bool option;
3434+ unknown : Jsont.json;
3535+ }
3636+3737+ let empty =
3838+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
3939+ { context = None; tools = None; unknown }
4040+4141+ let make ?context ?tools () =
4242+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
4343+ { context; tools; unknown }
4444+4545+ let jsont : t Jsont.t =
4646+ let make context tools unknown = { context; tools; unknown } in
4747+ Jsont.Object.map ~kind:"Sampling" make
4848+ |> Jsont.Object.opt_mem "context" Jsont.bool ~enc:(fun s -> s.context)
4949+ |> Jsont.Object.opt_mem "tools" Jsont.bool ~enc:(fun s -> s.tools)
5050+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun s -> s.unknown)
5151+ |> Jsont.Object.finish
5252+end
5353+5454+module Elicitation = struct
5555+ type t = {
5656+ unknown : Jsont.json;
5757+ }
5858+5959+ let empty =
6060+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
6161+ { unknown }
6262+6363+ let jsont : t Jsont.t =
6464+ let make unknown = { unknown } in
6565+ Jsont.Object.map ~kind:"Elicitation" make
6666+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun e -> e.unknown)
6767+ |> Jsont.Object.finish
6868+end
6969+7070+module Roots = struct
7171+ type t = {
7272+ list_changed : bool option;
7373+ unknown : Jsont.json;
7474+ }
7575+7676+ let empty =
7777+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
7878+ { list_changed = None; unknown }
7979+8080+ let make ?list_changed () =
8181+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
8282+ { list_changed; unknown }
8383+8484+ let jsont : t Jsont.t =
8585+ let make list_changed unknown = { list_changed; unknown } in
8686+ Jsont.Object.map ~kind:"Roots" make
8787+ |> Jsont.Object.opt_mem "listChanged" Jsont.bool ~enc:(fun r -> r.list_changed)
8888+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
8989+ |> Jsont.Object.finish
9090+end
9191+9292+module Client = struct
9393+ type t = {
9494+ sampling : Sampling.t option;
9595+ elicitation : Elicitation.t option;
9696+ roots : Roots.t option;
9797+ experimental : Jsont.json option;
9898+ unknown : Jsont.json;
9999+ }
100100+101101+ let empty =
102102+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
103103+ { sampling = None; elicitation = None; roots = None; experimental = None; unknown }
104104+105105+ let make ?sampling ?elicitation ?roots ?experimental () =
106106+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
107107+ { sampling; elicitation; roots; experimental; unknown }
108108+109109+ let jsont : t Jsont.t =
110110+ let make sampling elicitation roots experimental unknown =
111111+ { sampling; elicitation; roots; experimental; unknown }
112112+ in
113113+ Jsont.Object.map ~kind:"ClientCapabilities" make
114114+ |> Jsont.Object.opt_mem "sampling" Sampling.jsont ~enc:(fun c -> c.sampling)
115115+ |> Jsont.Object.opt_mem "elicitation" Elicitation.jsont ~enc:(fun c -> c.elicitation)
116116+ |> Jsont.Object.opt_mem "roots" Roots.jsont ~enc:(fun c -> c.roots)
117117+ |> Jsont.Object.opt_mem "experimental" Jsont.json ~enc:(fun c -> c.experimental)
118118+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun c -> c.unknown)
119119+ |> Jsont.Object.finish
120120+121121+ let pp fmt c =
122122+ let caps = [
123123+ (match c.sampling with Some _ -> Some "sampling" | None -> None);
124124+ (match c.elicitation with Some _ -> Some "elicitation" | None -> None);
125125+ (match c.roots with Some _ -> Some "roots" | None -> None);
126126+ ] |> List.filter_map Fun.id in
127127+ Format.fprintf fmt "[%s]" (String.concat ", " caps)
128128+end
129129+130130+(* Server Capabilities *)
131131+132132+module Logging = struct
133133+ type t = {
134134+ unknown : Jsont.json;
135135+ }
136136+137137+ let empty =
138138+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
139139+ { unknown }
140140+141141+ let jsont : t Jsont.t =
142142+ let make unknown = { unknown } in
143143+ Jsont.Object.map ~kind:"Logging" make
144144+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun l -> l.unknown)
145145+ |> Jsont.Object.finish
146146+end
147147+148148+module Prompts = struct
149149+ type t = {
150150+ list_changed : bool option;
151151+ unknown : Jsont.json;
152152+ }
153153+154154+ let empty =
155155+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
156156+ { list_changed = None; unknown }
157157+158158+ let make ?list_changed () =
159159+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
160160+ { list_changed; unknown }
161161+162162+ let jsont : t Jsont.t =
163163+ let make list_changed unknown = { list_changed; unknown } in
164164+ Jsont.Object.map ~kind:"Prompts" make
165165+ |> Jsont.Object.opt_mem "listChanged" Jsont.bool ~enc:(fun p -> p.list_changed)
166166+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun p -> p.unknown)
167167+ |> Jsont.Object.finish
168168+end
169169+170170+module Resources = struct
171171+ type t = {
172172+ subscribe : bool option;
173173+ list_changed : bool option;
174174+ unknown : Jsont.json;
175175+ }
176176+177177+ let empty =
178178+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
179179+ { subscribe = None; list_changed = None; unknown }
180180+181181+ let make ?subscribe ?list_changed () =
182182+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
183183+ { subscribe; list_changed; unknown }
184184+185185+ let jsont : t Jsont.t =
186186+ let make subscribe list_changed unknown =
187187+ { subscribe; list_changed; unknown }
188188+ in
189189+ Jsont.Object.map ~kind:"Resources" make
190190+ |> Jsont.Object.opt_mem "subscribe" Jsont.bool ~enc:(fun r -> r.subscribe)
191191+ |> Jsont.Object.opt_mem "listChanged" Jsont.bool ~enc:(fun r -> r.list_changed)
192192+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
193193+ |> Jsont.Object.finish
194194+end
195195+196196+module Tools = struct
197197+ type t = {
198198+ list_changed : bool option;
199199+ unknown : Jsont.json;
200200+ }
201201+202202+ let empty =
203203+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
204204+ { list_changed = None; unknown }
205205+206206+ let make ?list_changed () =
207207+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
208208+ { list_changed; unknown }
209209+210210+ let jsont : t Jsont.t =
211211+ let make list_changed unknown = { list_changed; unknown } in
212212+ Jsont.Object.map ~kind:"Tools" make
213213+ |> Jsont.Object.opt_mem "listChanged" Jsont.bool ~enc:(fun t -> t.list_changed)
214214+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun t -> t.unknown)
215215+ |> Jsont.Object.finish
216216+end
217217+218218+module Completions = struct
219219+ type t = {
220220+ unknown : Jsont.json;
221221+ }
222222+223223+ let empty =
224224+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
225225+ { unknown }
226226+227227+ let jsont : t Jsont.t =
228228+ let make unknown = { unknown } in
229229+ Jsont.Object.map ~kind:"Completions" make
230230+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun c -> c.unknown)
231231+ |> Jsont.Object.finish
232232+end
233233+234234+module Server = struct
235235+ type t = {
236236+ logging : Logging.t option;
237237+ prompts : Prompts.t option;
238238+ resources : Resources.t option;
239239+ tools : Tools.t option;
240240+ completions : Completions.t option;
241241+ experimental : Jsont.json option;
242242+ unknown : Jsont.json;
243243+ }
244244+245245+ let empty =
246246+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
247247+ {
248248+ logging = None;
249249+ prompts = None;
250250+ resources = None;
251251+ tools = None;
252252+ completions = None;
253253+ experimental = None;
254254+ unknown;
255255+ }
256256+257257+ let make ?logging ?prompts ?resources ?tools ?completions ?experimental () =
258258+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
259259+ { logging; prompts; resources; tools; completions; experimental; unknown }
260260+261261+ let jsont : t Jsont.t =
262262+ let make logging prompts resources tools completions experimental unknown =
263263+ { logging; prompts; resources; tools; completions; experimental; unknown }
264264+ in
265265+ Jsont.Object.map ~kind:"ServerCapabilities" make
266266+ |> Jsont.Object.opt_mem "logging" Logging.jsont ~enc:(fun s -> s.logging)
267267+ |> Jsont.Object.opt_mem "prompts" Prompts.jsont ~enc:(fun s -> s.prompts)
268268+ |> Jsont.Object.opt_mem "resources" Resources.jsont ~enc:(fun s -> s.resources)
269269+ |> Jsont.Object.opt_mem "tools" Tools.jsont ~enc:(fun s -> s.tools)
270270+ |> Jsont.Object.opt_mem "completions" Completions.jsont ~enc:(fun s -> s.completions)
271271+ |> Jsont.Object.opt_mem "experimental" Jsont.json ~enc:(fun s -> s.experimental)
272272+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun s -> s.unknown)
273273+ |> Jsont.Object.finish
274274+275275+ let pp fmt s =
276276+ let caps = [
277277+ (match s.logging with Some _ -> Some "logging" | None -> None);
278278+ (match s.prompts with Some _ -> Some "prompts" | None -> None);
279279+ (match s.resources with Some _ -> Some "resources" | None -> None);
280280+ (match s.tools with Some _ -> Some "tools" | None -> None);
281281+ (match s.completions with Some _ -> Some "completions" | None -> None);
282282+ ] |> List.filter_map Fun.id in
283283+ Format.fprintf fmt "[%s]" (String.concat ", " caps)
284284+end
+161
claudeio/lib_mcp/capabilities.mli
···11+(** MCP Capability negotiation types.
22+33+ Capabilities are exchanged during initialization to determine what features
44+ the client and server support. *)
55+66+(** {1 Implementation Info} *)
77+88+module Implementation : sig
99+ type t = {
1010+ name : string;
1111+ version : string;
1212+ unknown : Jsont.json;
1313+ }
1414+ (** Information about client or server implementation *)
1515+1616+ val make : name:string -> version:string -> t
1717+ val jsont : t Jsont.t
1818+ val pp : Format.formatter -> t -> unit
1919+end
2020+2121+(** {1 Client Capabilities} *)
2222+2323+module Sampling : sig
2424+ type t = {
2525+ context : bool option;
2626+ tools : bool option;
2727+ unknown : Jsont.json;
2828+ }
2929+ (** Sampling capability (for servers to request LLM sampling from clients) *)
3030+3131+ val empty : t
3232+ val make : ?context:bool -> ?tools:bool -> unit -> t
3333+ val jsont : t Jsont.t
3434+end
3535+3636+module Elicitation : sig
3737+ type t = {
3838+ unknown : Jsont.json;
3939+ }
4040+ (** Elicitation capability (for servers to request user input) *)
4141+4242+ val empty : t
4343+ val jsont : t Jsont.t
4444+end
4545+4646+module Roots : sig
4747+ type t = {
4848+ list_changed : bool option;
4949+ unknown : Jsont.json;
5050+ }
5151+ (** Roots capability (for servers to query filesystem roots) *)
5252+5353+ val empty : t
5454+ val make : ?list_changed:bool -> unit -> t
5555+ val jsont : t Jsont.t
5656+end
5757+5858+module Client : sig
5959+ type t = {
6060+ sampling : Sampling.t option;
6161+ elicitation : Elicitation.t option;
6262+ roots : Roots.t option;
6363+ experimental : Jsont.json option;
6464+ unknown : Jsont.json;
6565+ }
6666+ (** Client capabilities advertised during initialization *)
6767+6868+ val empty : t
6969+ val make :
7070+ ?sampling:Sampling.t ->
7171+ ?elicitation:Elicitation.t ->
7272+ ?roots:Roots.t ->
7373+ ?experimental:Jsont.json ->
7474+ unit -> t
7575+ val jsont : t Jsont.t
7676+ val pp : Format.formatter -> t -> unit
7777+end
7878+7979+(** {1 Server Capabilities} *)
8080+8181+module Logging : sig
8282+ type t = {
8383+ unknown : Jsont.json;
8484+ }
8585+ (** Logging capability *)
8686+8787+ val empty : t
8888+ val jsont : t Jsont.t
8989+end
9090+9191+module Prompts : sig
9292+ type t = {
9393+ list_changed : bool option;
9494+ unknown : Jsont.json;
9595+ }
9696+ (** Prompts capability *)
9797+9898+ val empty : t
9999+ val make : ?list_changed:bool -> unit -> t
100100+ val jsont : t Jsont.t
101101+end
102102+103103+module Resources : sig
104104+ type t = {
105105+ subscribe : bool option;
106106+ list_changed : bool option;
107107+ unknown : Jsont.json;
108108+ }
109109+ (** Resources capability *)
110110+111111+ val empty : t
112112+ val make : ?subscribe:bool -> ?list_changed:bool -> unit -> t
113113+ val jsont : t Jsont.t
114114+end
115115+116116+module Tools : sig
117117+ type t = {
118118+ list_changed : bool option;
119119+ unknown : Jsont.json;
120120+ }
121121+ (** Tools capability *)
122122+123123+ val empty : t
124124+ val make : ?list_changed:bool -> unit -> t
125125+ val jsont : t Jsont.t
126126+end
127127+128128+module Completions : sig
129129+ type t = {
130130+ unknown : Jsont.json;
131131+ }
132132+ (** Completions capability (for auto-complete) *)
133133+134134+ val empty : t
135135+ val jsont : t Jsont.t
136136+end
137137+138138+module Server : sig
139139+ type t = {
140140+ logging : Logging.t option;
141141+ prompts : Prompts.t option;
142142+ resources : Resources.t option;
143143+ tools : Tools.t option;
144144+ completions : Completions.t option;
145145+ experimental : Jsont.json option;
146146+ unknown : Jsont.json;
147147+ }
148148+ (** Server capabilities advertised during initialization *)
149149+150150+ val empty : t
151151+ val make :
152152+ ?logging:Logging.t ->
153153+ ?prompts:Prompts.t ->
154154+ ?resources:Resources.t ->
155155+ ?tools:Tools.t ->
156156+ ?completions:Completions.t ->
157157+ ?experimental:Jsont.json ->
158158+ unit -> t
159159+ val jsont : t Jsont.t
160160+ val pp : Format.formatter -> t -> unit
161161+end
+356
claudeio/lib_mcp/client_session.ml
···11+(** High-level MCP client session implementation *)
22+33+(** {1 Configuration} *)
44+55+type config = {
66+ client_info : Capabilities.Implementation.t;
77+ client_capabilities : Capabilities.Client.t;
88+}
99+1010+(** {1 Internal State} *)
1111+1212+exception Initialization_error of string
1313+1414+type notification_handlers = {
1515+ mutable on_resource_updated : (uri:string -> unit) option;
1616+ mutable on_resource_list_changed : (unit -> unit) option;
1717+ mutable on_tool_list_changed : (unit -> unit) option;
1818+ mutable on_prompt_list_changed : (unit -> unit) option;
1919+ mutable on_log_message : (level:Messages.Logging.level -> logger:string option -> data:Jsont.json -> unit) option;
2020+}
2121+2222+type t = {
2323+ session : Session.t;
2424+ server_capabilities : Capabilities.Server.t;
2525+ server_info : Capabilities.Implementation.t;
2626+ server_instructions : string option;
2727+ handlers : notification_handlers;
2828+}
2929+3030+(** {1 Helper Functions} *)
3131+3232+(* Encode a value to JSON using jsont codec *)
3333+let encode codec value =
3434+ match Jsont.Json.encode codec value with
3535+ | Ok json -> json
3636+ | Error msg -> failwith ("Failed to encode: " ^ msg)
3737+3838+(* Decode a JSON value using jsont codec *)
3939+let decode codec json =
4040+ match Jsont.Json.decode codec json with
4141+ | Ok value -> value
4242+ | Error msg -> failwith ("Failed to decode: " ^ msg)
4343+4444+(* Parse notification parameters - returns None if params is None or null *)
4545+let parse_notification_params codec params_opt =
4646+ match params_opt with
4747+ | None -> None
4848+ | Some (Jsont.Null _) -> None
4949+ | Some json -> Some (decode codec json)
5050+5151+(** {1 Notification Routing} *)
5252+5353+let create_notification_handler handlers : Session.notification_handler =
5454+ fun ~method_ ~params ->
5555+ match method_ with
5656+ | "notifications/resources/updated" ->
5757+ (match handlers.on_resource_updated with
5858+ | None -> ()
5959+ | Some handler ->
6060+ let notif = parse_notification_params
6161+ Messages.Resources.updated_notification_jsont params in
6262+ (match notif with
6363+ | None -> ()
6464+ | Some n -> handler ~uri:n.Messages.Resources.uri))
6565+6666+ | "notifications/resources/list_changed" ->
6767+ (match handlers.on_resource_list_changed with
6868+ | None -> ()
6969+ | Some handler -> handler ())
7070+7171+ | "notifications/tools/list_changed" ->
7272+ (match handlers.on_tool_list_changed with
7373+ | None -> ()
7474+ | Some handler -> handler ())
7575+7676+ | "notifications/prompts/list_changed" ->
7777+ (match handlers.on_prompt_list_changed with
7878+ | None -> ()
7979+ | Some handler -> handler ())
8080+8181+ | "notifications/message" ->
8282+ (match handlers.on_log_message with
8383+ | None -> ()
8484+ | Some handler ->
8585+ let notif = parse_notification_params
8686+ Messages.Logging.notification_jsont params in
8787+ (match notif with
8888+ | None -> ()
8989+ | Some n ->
9090+ let data = match n.Messages.Logging.data with
9191+ | None -> Jsont.Null ((), Jsont.Meta.none)
9292+ | Some d -> d
9393+ in
9494+ handler
9595+ ~level:n.Messages.Logging.level
9696+ ~logger:n.Messages.Logging.logger
9797+ ~data))
9898+9999+ | _ ->
100100+ (* Unknown notification - ignore *)
101101+ ()
102102+103103+(** {1 Request Handler} *)
104104+105105+(* Client doesn't expect to receive requests from server in most cases *)
106106+let create_request_handler () : Session.request_handler =
107107+ fun ~method_ ~params:_ ->
108108+ (* Default: return method not found error *)
109109+ let error = Jsonrpc.Error_data.make
110110+ ~code:Method_not_found
111111+ ~message:(Printf.sprintf "Client does not handle method: %s" method_)
112112+ ()
113113+ in
114114+ raise (Session.Remote_error error)
115115+116116+(** {1 Initialization} *)
117117+118118+let perform_initialization session config =
119119+ (* Send Initialize request *)
120120+ let init_params = Messages.Initialize.make_request_params
121121+ ~protocol_version:"2024-11-05"
122122+ ~capabilities:config.client_capabilities
123123+ ~client_info:config.client_info
124124+ ()
125125+ in
126126+ let params_json = encode Messages.Initialize.request_params_jsont init_params in
127127+128128+ let response_json = Session.send_request session
129129+ ~method_:Messages.Initialize.method_
130130+ ~params:params_json
131131+ ()
132132+ in
133133+134134+ (* Decode Initialize result *)
135135+ let init_result = decode Messages.Initialize.result_jsont response_json in
136136+137137+ (* Send Initialized notification *)
138138+ let initialized_notif = Messages.Initialized.make_notification () in
139139+ let notif_json = encode Messages.Initialized.notification_jsont initialized_notif in
140140+ Session.send_notification session
141141+ ~method_:Messages.Initialized.method_
142142+ ~params:notif_json
143143+ ();
144144+145145+ (* Return server info *)
146146+ (init_result.Messages.Initialize.capabilities,
147147+ init_result.Messages.Initialize.server_info,
148148+ init_result.Messages.Initialize.instructions)
149149+150150+(** {1 Public API} *)
151151+152152+let create ~sw ~transport ?timeout ?clock config =
153153+ (* Create notification handlers *)
154154+ let handlers = {
155155+ on_resource_updated = None;
156156+ on_resource_list_changed = None;
157157+ on_tool_list_changed = None;
158158+ on_prompt_list_changed = None;
159159+ on_log_message = None;
160160+ } in
161161+162162+ (* Create session config *)
163163+ let session_config : Session.config = {
164164+ transport;
165165+ request_handler = create_request_handler ();
166166+ notification_handler = create_notification_handler handlers;
167167+ timeout;
168168+ clock;
169169+ } in
170170+171171+ (* Create underlying session *)
172172+ let session = Session.create ~sw session_config in
173173+174174+ try
175175+ (* Perform initialization handshake *)
176176+ let (server_capabilities, server_info, server_instructions) =
177177+ perform_initialization session config
178178+ in
179179+180180+ (* Return client session *)
181181+ {
182182+ session;
183183+ server_capabilities;
184184+ server_info;
185185+ server_instructions;
186186+ handlers;
187187+ }
188188+ with
189189+ | Session.Remote_error err ->
190190+ Session.close session;
191191+ raise (Initialization_error
192192+ (Printf.sprintf "Server returned error: %s" err.Jsonrpc.Error_data.message))
193193+ | Session.Timeout msg ->
194194+ Session.close session;
195195+ raise (Initialization_error ("Initialization timeout: " ^ msg))
196196+ | exn ->
197197+ Session.close session;
198198+ raise (Initialization_error
199199+ (Printf.sprintf "Initialization failed: %s" (Printexc.to_string exn)))
200200+201201+(** {1 Server Information} *)
202202+203203+let server_capabilities t = t.server_capabilities
204204+let server_info t = t.server_info
205205+let server_instructions t = t.server_instructions
206206+207207+(** {1 Basic Operations} *)
208208+209209+let ping t =
210210+ let params = Messages.Ping.make_params () in
211211+ let params_json = encode Messages.Ping.params_jsont params in
212212+ let response_json = Session.send_request t.session
213213+ ~method_:Messages.Ping.method_
214214+ ~params:params_json
215215+ ()
216216+ in
217217+ let _result = decode Messages.Ping.result_jsont response_json in
218218+ ()
219219+220220+(** {1 Resources} *)
221221+222222+let list_resources t ?cursor () =
223223+ let request = Messages.Resources.make_list_request ?cursor () in
224224+ let params_json = encode Messages.Resources.list_request_jsont request in
225225+ let response_json = Session.send_request t.session
226226+ ~method_:Messages.Resources.list_method
227227+ ~params:params_json
228228+ ()
229229+ in
230230+ decode Messages.Resources.list_result_jsont response_json
231231+232232+let read_resource t ~uri =
233233+ let request = Messages.Resources.make_read_request ~uri in
234234+ let params_json = encode Messages.Resources.read_request_jsont request in
235235+ let response_json = Session.send_request t.session
236236+ ~method_:Messages.Resources.read_method
237237+ ~params:params_json
238238+ ()
239239+ in
240240+ decode Messages.Resources.read_result_jsont response_json
241241+242242+let subscribe_resource t ~uri =
243243+ let request = Messages.Resources.make_subscribe_request ~uri in
244244+ let params_json = encode Messages.Resources.subscribe_request_jsont request in
245245+ let _response_json = Session.send_request t.session
246246+ ~method_:Messages.Resources.subscribe_method
247247+ ~params:params_json
248248+ ()
249249+ in
250250+ ()
251251+252252+let unsubscribe_resource t ~uri =
253253+ let request = Messages.Resources.make_unsubscribe_request ~uri in
254254+ let params_json = encode Messages.Resources.unsubscribe_request_jsont request in
255255+ let _response_json = Session.send_request t.session
256256+ ~method_:Messages.Resources.unsubscribe_method
257257+ ~params:params_json
258258+ ()
259259+ in
260260+ ()
261261+262262+(** {1 Tools} *)
263263+264264+let list_tools t ?cursor () =
265265+ let request = Messages.Tools.make_list_request ?cursor () in
266266+ let params_json = encode Messages.Tools.list_request_jsont request in
267267+ let response_json = Session.send_request t.session
268268+ ~method_:Messages.Tools.list_method
269269+ ~params:params_json
270270+ ()
271271+ in
272272+ decode Messages.Tools.list_result_jsont response_json
273273+274274+let call_tool t ~name ?arguments () =
275275+ let request = Messages.Tools.make_call_request ~name ?arguments () in
276276+ let params_json = encode Messages.Tools.call_request_jsont request in
277277+ let response_json = Session.send_request t.session
278278+ ~method_:Messages.Tools.call_method
279279+ ~params:params_json
280280+ ()
281281+ in
282282+ decode Messages.Tools.call_result_jsont response_json
283283+284284+(** {1 Prompts} *)
285285+286286+let list_prompts t ?cursor () =
287287+ let request = Messages.Prompts.make_list_request ?cursor () in
288288+ let params_json = encode Messages.Prompts.list_request_jsont request in
289289+ let response_json = Session.send_request t.session
290290+ ~method_:Messages.Prompts.list_method
291291+ ~params:params_json
292292+ ()
293293+ in
294294+ decode Messages.Prompts.list_result_jsont response_json
295295+296296+let get_prompt t ~name ?arguments () =
297297+ let request = Messages.Prompts.make_get_request ~name ?arguments () in
298298+ let params_json = encode Messages.Prompts.get_request_jsont request in
299299+ let response_json = Session.send_request t.session
300300+ ~method_:Messages.Prompts.get_method
301301+ ~params:params_json
302302+ ()
303303+ in
304304+ decode Messages.Prompts.get_result_jsont response_json
305305+306306+(** {1 Completions} *)
307307+308308+let complete t ~ref ~argument =
309309+ let request = Messages.Completions.make_request ~ref_:ref ~argument () in
310310+ let params_json = encode Messages.Completions.request_jsont request in
311311+ let response_json = Session.send_request t.session
312312+ ~method_:Messages.Completions.method_
313313+ ~params:params_json
314314+ ()
315315+ in
316316+ decode Messages.Completions.result_jsont response_json
317317+318318+(** {1 Logging} *)
319319+320320+let set_log_level t level =
321321+ (* Create a simple request with level parameter *)
322322+ let level_json = encode Messages.Logging.level_jsont level in
323323+ let params = Jsont.Object ([
324324+ (("level", Jsont.Meta.none), level_json)
325325+ ], Jsont.Meta.none) in
326326+ let _response_json = Session.send_request t.session
327327+ ~method_:"logging/setLevel"
328328+ ~params
329329+ ()
330330+ in
331331+ ()
332332+333333+(** {1 Notification Handlers} *)
334334+335335+let on_resource_updated t handler =
336336+ t.handlers.on_resource_updated <- Some handler
337337+338338+let on_resource_list_changed t handler =
339339+ t.handlers.on_resource_list_changed <- Some handler
340340+341341+let on_tool_list_changed t handler =
342342+ t.handlers.on_tool_list_changed <- Some handler
343343+344344+let on_prompt_list_changed t handler =
345345+ t.handlers.on_prompt_list_changed <- Some handler
346346+347347+let on_log_message t handler =
348348+ t.handlers.on_log_message <- Some handler
349349+350350+(** {1 Session Control} *)
351351+352352+let close t =
353353+ Session.close t.session
354354+355355+let is_closed t =
356356+ Session.is_closed t.session
+217
claudeio/lib_mcp/client_session.mli
···11+(** High-level MCP client session API.
22+33+ This module provides a high-level client API for connecting to MCP servers.
44+ It handles the initialization handshake, capability negotiation, and provides
55+ typed methods for all MCP protocol operations.
66+77+ {1 Example Usage}
88+99+ {[
1010+ Eio_main.run @@ fun env ->
1111+ Eio.Switch.run @@ fun sw ->
1212+ let transport = Transport_stdio.create ~sw (module Eio.Stdenv : Eio.Stdenv.S with type t = _) env in
1313+1414+ let config = {
1515+ client_info = Capabilities.Implementation.make
1616+ ~name:"my-client"
1717+ ~version:"1.0.0";
1818+ client_capabilities = Capabilities.Client.make
1919+ ~roots:(Capabilities.Roots.make ~list_changed:true ())
2020+ ();
2121+ } in
2222+2323+ let client = Client_session.create ~sw ~transport config in
2424+2525+ (* List available tools *)
2626+ let tools_result = Client_session.list_tools client () in
2727+ List.iter (fun tool ->
2828+ Printf.printf "Tool: %s\n" tool.Messages.Tools.name
2929+ ) tools_result.Messages.Tools.tools;
3030+3131+ (* Call a tool *)
3232+ let args = `Object [("query", `String "hello")] in
3333+ let result = Client_session.call_tool client
3434+ ~name:"search"
3535+ ~arguments:args
3636+ ()
3737+ in
3838+3939+ Client_session.close client
4040+ ]} *)
4141+4242+(** {1 Configuration} *)
4343+4444+type config = {
4545+ client_info : Capabilities.Implementation.t;
4646+ (** Client implementation information (name and version) *)
4747+ client_capabilities : Capabilities.Client.t;
4848+ (** Client capabilities to advertise to server *)
4949+}
5050+(** Client session configuration *)
5151+5252+(** {1 Session Management} *)
5353+5454+type t
5555+(** Client session handle *)
5656+5757+exception Initialization_error of string
5858+(** Raised when initialization handshake fails *)
5959+6060+val create :
6161+ sw:Eio.Switch.t ->
6262+ transport:Transport.t ->
6363+ ?timeout:float ->
6464+ ?clock:Session.clock ->
6565+ config ->
6666+ t
6767+(** Create a client session and perform the initialization handshake.
6868+6969+ This sends an Initialize request to the server, stores the server's
7070+ capabilities and info, then sends an Initialized notification.
7171+7272+ @param sw Switch for background fibers
7373+ @param transport Transport layer for communication
7474+ @param timeout Optional request timeout in seconds
7575+ @param clock Clock for timeout handling (required if timeout is set)
7676+ @param config Client configuration
7777+ @raise Initialization_error if the handshake fails
7878+ @raise Session.Remote_error if the server returns an error
7979+ @raise Session.Timeout if the initialize request times out *)
8080+8181+(** {1 Server Information} *)
8282+8383+val server_capabilities : t -> Capabilities.Server.t
8484+(** Get the server's advertised capabilities from initialization *)
8585+8686+val server_info : t -> Capabilities.Implementation.t
8787+(** Get the server's implementation info (name and version) *)
8888+8989+val server_instructions : t -> string option
9090+(** Get optional server instructions from initialization *)
9191+9292+(** {1 Basic Operations} *)
9393+9494+val ping : t -> unit
9595+(** Send a ping request to the server (keepalive).
9696+ @raise Session.Remote_error if the server returns an error
9797+ @raise Session.Timeout if the request times out
9898+ @raise Session.Session_closed if the session is closed *)
9999+100100+(** {1 Resources} *)
101101+102102+val list_resources : t -> ?cursor:string -> unit -> Messages.Resources.list_result
103103+(** List available resources.
104104+ @param cursor Optional pagination cursor
105105+ @raise Session.Remote_error if the server returns an error
106106+ @raise Session.Timeout if the request times out
107107+ @raise Session.Session_closed if the session is closed *)
108108+109109+val read_resource : t -> uri:string -> Messages.Resources.read_result
110110+(** Read resource contents by URI.
111111+ @param uri Resource URI to read
112112+ @raise Session.Remote_error if the server returns an error
113113+ @raise Session.Timeout if the request times out
114114+ @raise Session.Session_closed if the session is closed *)
115115+116116+val subscribe_resource : t -> uri:string -> unit
117117+(** Subscribe to resource update notifications.
118118+ @param uri Resource URI to subscribe to
119119+ @raise Session.Remote_error if the server returns an error
120120+ @raise Session.Timeout if the request times out
121121+ @raise Session.Session_closed if the session is closed *)
122122+123123+val unsubscribe_resource : t -> uri:string -> unit
124124+(** Unsubscribe from resource update notifications.
125125+ @param uri Resource URI to unsubscribe from
126126+ @raise Session.Remote_error if the server returns an error
127127+ @raise Session.Timeout if the request times out
128128+ @raise Session.Session_closed if the session is closed *)
129129+130130+(** {1 Tools} *)
131131+132132+val list_tools : t -> ?cursor:string -> unit -> Messages.Tools.list_result
133133+(** List available tools.
134134+ @param cursor Optional pagination cursor
135135+ @raise Session.Remote_error if the server returns an error
136136+ @raise Session.Timeout if the request times out
137137+ @raise Session.Session_closed if the session is closed *)
138138+139139+val call_tool : t -> name:string -> ?arguments:Jsont.json -> unit -> Messages.Tools.call_result
140140+(** Call a tool by name.
141141+ @param name Tool name
142142+ @param arguments Optional tool arguments (JSON object)
143143+ @raise Session.Remote_error if the server returns an error
144144+ @raise Session.Timeout if the request times out
145145+ @raise Session.Session_closed if the session is closed *)
146146+147147+(** {1 Prompts} *)
148148+149149+val list_prompts : t -> ?cursor:string -> unit -> Messages.Prompts.list_result
150150+(** List available prompts.
151151+ @param cursor Optional pagination cursor
152152+ @raise Session.Remote_error if the server returns an error
153153+ @raise Session.Timeout if the request times out
154154+ @raise Session.Session_closed if the session is closed *)
155155+156156+val get_prompt : t -> name:string -> ?arguments:(string * string) list -> unit -> Messages.Prompts.get_result
157157+(** Get a prompt by name with optional arguments.
158158+ @param name Prompt name
159159+ @param arguments Optional key-value pairs for prompt arguments
160160+ @raise Session.Remote_error if the server returns an error
161161+ @raise Session.Timeout if the request times out
162162+ @raise Session.Session_closed if the session is closed *)
163163+164164+(** {1 Completions} *)
165165+166166+val complete : t -> ref:Messages.Completions.completion_ref -> argument:string -> Messages.Completions.result
167167+(** Request auto-completion suggestions.
168168+ @param ref Completion reference (prompt or resource)
169169+ @param argument Argument value to complete
170170+ @raise Session.Remote_error if the server returns an error
171171+ @raise Session.Timeout if the request times out
172172+ @raise Session.Session_closed if the session is closed *)
173173+174174+(** {1 Logging} *)
175175+176176+val set_log_level : t -> Messages.Logging.level -> unit
177177+(** Set the server's logging level.
178178+ Note: This sends a "logging/setLevel" request.
179179+ @param level Desired log level
180180+ @raise Session.Remote_error if the server returns an error
181181+ @raise Session.Timeout if the request times out
182182+ @raise Session.Session_closed if the session is closed *)
183183+184184+(** {1 Notification Handlers} *)
185185+186186+val on_resource_updated : t -> (uri:string -> unit) -> unit
187187+(** Register a handler for resource update notifications.
188188+ The handler is called when a subscribed resource is updated.
189189+ Only one handler can be registered at a time (replaces previous handler). *)
190190+191191+val on_resource_list_changed : t -> (unit -> unit) -> unit
192192+(** Register a handler for resource list change notifications.
193193+ The handler is called when the list of available resources changes.
194194+ Only one handler can be registered at a time (replaces previous handler). *)
195195+196196+val on_tool_list_changed : t -> (unit -> unit) -> unit
197197+(** Register a handler for tool list change notifications.
198198+ The handler is called when the list of available tools changes.
199199+ Only one handler can be registered at a time (replaces previous handler). *)
200200+201201+val on_prompt_list_changed : t -> (unit -> unit) -> unit
202202+(** Register a handler for prompt list change notifications.
203203+ The handler is called when the list of available prompts changes.
204204+ Only one handler can be registered at a time (replaces previous handler). *)
205205+206206+val on_log_message : t -> (level:Messages.Logging.level -> logger:string option -> data:Jsont.json -> unit) -> unit
207207+(** Register a handler for log message notifications from the server.
208208+ Only one handler can be registered at a time (replaces previous handler). *)
209209+210210+(** {1 Session Control} *)
211211+212212+val close : t -> unit
213213+(** Close the client session and underlying transport.
214214+ This is idempotent - safe to call multiple times. *)
215215+216216+val is_closed : t -> bool
217217+(** Check if the session is closed. *)
+246
claudeio/lib_mcp/content.ml
···11+(** MCP Content Block types *)
22+33+(* Annotations *)
44+55+module Audience = struct
66+ type t = User | Assistant
77+88+ let jsont : t Jsont.t =
99+ Jsont.enum [
1010+ "user", User;
1111+ "assistant", Assistant;
1212+ ]
1313+1414+ let pp fmt = function
1515+ | User -> Format.fprintf fmt "user"
1616+ | Assistant -> Format.fprintf fmt "assistant"
1717+end
1818+1919+module Annotations = struct
2020+ type t = {
2121+ audience : Audience.t list option;
2222+ priority : float option;
2323+ unknown : Jsont.json;
2424+ }
2525+2626+ let empty =
2727+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
2828+ { audience = None; priority = None; unknown }
2929+3030+ let jsont : t Jsont.t =
3131+ let make audience priority unknown = { audience; priority; unknown } in
3232+ Jsont.Object.map ~kind:"Annotations" make
3333+ |> Jsont.Object.opt_mem "audience" (Jsont.list Audience.jsont)
3434+ ~enc:(fun a -> a.audience)
3535+ |> Jsont.Object.opt_mem "priority" Jsont.number
3636+ ~enc:(fun a -> a.priority)
3737+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun a -> a.unknown)
3838+ |> Jsont.Object.finish
3939+4040+ let pp fmt _ann =
4141+ Format.fprintf fmt "{annotations}"
4242+end
4343+4444+(* Text Content *)
4545+4646+module Text = struct
4747+ type t = {
4848+ text : string;
4949+ annotations : Annotations.t option;
5050+ unknown : Jsont.json;
5151+ }
5252+5353+ let make text =
5454+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
5555+ { text; annotations = None; unknown }
5656+5757+ let jsont : t Jsont.t =
5858+ let make text annotations unknown = { text; annotations; unknown } in
5959+ Jsont.Object.map ~kind:"TextContent" make
6060+ |> Jsont.Object.mem "text" Jsont.string ~enc:(fun t -> t.text)
6161+ |> Jsont.Object.opt_mem "annotations" Annotations.jsont
6262+ ~enc:(fun t -> t.annotations)
6363+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun t -> t.unknown)
6464+ |> Jsont.Object.finish
6565+6666+ let pp fmt t =
6767+ Format.fprintf fmt "%S" t.text
6868+end
6969+7070+(* Image Content *)
7171+7272+module Image = struct
7373+ type t = {
7474+ data : string;
7575+ mime_type : string;
7676+ annotations : Annotations.t option;
7777+ unknown : Jsont.json;
7878+ }
7979+8080+ let make ~data ~mime_type =
8181+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
8282+ { data; mime_type; annotations = None; unknown }
8383+8484+ let jsont : t Jsont.t =
8585+ let make data mime_type annotations unknown =
8686+ { data; mime_type; annotations; unknown }
8787+ in
8888+ Jsont.Object.map ~kind:"ImageContent" make
8989+ |> Jsont.Object.mem "data" Jsont.string ~enc:(fun i -> i.data)
9090+ |> Jsont.Object.mem "mimeType" Jsont.string ~enc:(fun i -> i.mime_type)
9191+ |> Jsont.Object.opt_mem "annotations" Annotations.jsont
9292+ ~enc:(fun i -> i.annotations)
9393+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun i -> i.unknown)
9494+ |> Jsont.Object.finish
9595+9696+ let pp fmt i =
9797+ Format.fprintf fmt "Image(%s, %d bytes)" i.mime_type (String.length i.data)
9898+end
9999+100100+(* Audio Content *)
101101+102102+module Audio = struct
103103+ type t = {
104104+ data : string;
105105+ mime_type : string;
106106+ annotations : Annotations.t option;
107107+ unknown : Jsont.json;
108108+ }
109109+110110+ let make ~data ~mime_type =
111111+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
112112+ { data; mime_type; annotations = None; unknown }
113113+114114+ let jsont : t Jsont.t =
115115+ let make data mime_type annotations unknown =
116116+ { data; mime_type; annotations; unknown }
117117+ in
118118+ Jsont.Object.map ~kind:"AudioContent" make
119119+ |> Jsont.Object.mem "data" Jsont.string ~enc:(fun a -> a.data)
120120+ |> Jsont.Object.mem "mimeType" Jsont.string ~enc:(fun a -> a.mime_type)
121121+ |> Jsont.Object.opt_mem "annotations" Annotations.jsont
122122+ ~enc:(fun a -> a.annotations)
123123+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun a -> a.unknown)
124124+ |> Jsont.Object.finish
125125+126126+ let pp fmt a =
127127+ Format.fprintf fmt "Audio(%s, %d bytes)" a.mime_type (String.length a.data)
128128+end
129129+130130+(* Embedded Resource *)
131131+132132+module Embedded_resource = struct
133133+ type resource = {
134134+ uri : string;
135135+ mime_type : string option;
136136+ text : string option;
137137+ blob : string option;
138138+ unknown : Jsont.json;
139139+ }
140140+141141+ let resource_jsont : resource Jsont.t =
142142+ let make uri mime_type text blob unknown =
143143+ { uri; mime_type; text; blob; unknown }
144144+ in
145145+ Jsont.Object.map ~kind:"Resource" make
146146+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
147147+ |> Jsont.Object.opt_mem "mimeType" Jsont.string ~enc:(fun r -> r.mime_type)
148148+ |> Jsont.Object.opt_mem "text" Jsont.string ~enc:(fun r -> r.text)
149149+ |> Jsont.Object.opt_mem "blob" Jsont.string ~enc:(fun r -> r.blob)
150150+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
151151+ |> Jsont.Object.finish
152152+153153+ type t = {
154154+ resource : resource;
155155+ annotations : Annotations.t option;
156156+ unknown : Jsont.json;
157157+ }
158158+159159+ let make_text ~uri ~text ?mime_type () =
160160+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
161161+ let resource = {
162162+ uri;
163163+ mime_type;
164164+ text = Some text;
165165+ blob = None;
166166+ unknown;
167167+ } in
168168+ { resource; annotations = None; unknown }
169169+170170+ let make_blob ~uri ~blob ~mime_type =
171171+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
172172+ let resource = {
173173+ uri;
174174+ mime_type = Some mime_type;
175175+ text = None;
176176+ blob = Some blob;
177177+ unknown;
178178+ } in
179179+ { resource; annotations = None; unknown }
180180+181181+ let jsont : t Jsont.t =
182182+ let make resource annotations unknown =
183183+ { resource; annotations; unknown }
184184+ in
185185+ Jsont.Object.map ~kind:"EmbeddedResource" make
186186+ |> Jsont.Object.mem "resource" resource_jsont ~enc:(fun e -> e.resource)
187187+ |> Jsont.Object.opt_mem "annotations" Annotations.jsont
188188+ ~enc:(fun e -> e.annotations)
189189+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun e -> e.unknown)
190190+ |> Jsont.Object.finish
191191+192192+ let pp fmt e =
193193+ Format.fprintf fmt "Resource(%s)" e.resource.uri
194194+end
195195+196196+(* Content Block *)
197197+198198+type block =
199199+ | Text of Text.t
200200+ | Image of Image.t
201201+ | Audio of Audio.t
202202+ | Embedded_resource of Embedded_resource.t
203203+204204+let block_jsont : block Jsont.t =
205205+ (* Content blocks use "type" discriminator *)
206206+ let case_map kind obj dec = Jsont.Object.Case.map kind obj ~dec in
207207+208208+ let case_text = case_map "text" Text.jsont (fun v -> Text v) in
209209+ let case_image = case_map "image" Image.jsont (fun v -> Image v) in
210210+ let case_audio = case_map "audio" Audio.jsont (fun v -> Audio v) in
211211+ let case_resource = case_map "resource" Embedded_resource.jsont
212212+ (fun v -> Embedded_resource v)
213213+ in
214214+215215+ let enc_case = function
216216+ | Text v -> Jsont.Object.Case.value case_text v
217217+ | Image v -> Jsont.Object.Case.value case_image v
218218+ | Audio v -> Jsont.Object.Case.value case_audio v
219219+ | Embedded_resource v -> Jsont.Object.Case.value case_resource v
220220+ in
221221+222222+ let cases = Jsont.Object.Case.[
223223+ make case_text;
224224+ make case_image;
225225+ make case_audio;
226226+ make case_resource;
227227+ ] in
228228+229229+ Jsont.Object.map ~kind:"ContentBlock" Fun.id
230230+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
231231+ ~tag_to_string:Fun.id ~tag_compare:String.compare
232232+ |> Jsont.Object.finish
233233+234234+let pp_block fmt = function
235235+ | Text t -> Text.pp fmt t
236236+ | Image i -> Image.pp fmt i
237237+ | Audio a -> Audio.pp fmt a
238238+ | Embedded_resource e -> Embedded_resource.pp fmt e
239239+240240+(* Convenience constructors *)
241241+242242+let text s = Text (Text.make s)
243243+244244+let image ~data ~mime_type = Image (Image.make ~data ~mime_type)
245245+246246+let audio ~data ~mime_type = Audio (Audio.make ~data ~mime_type)
+125
claudeio/lib_mcp/content.mli
···11+(** MCP Content Block types.
22+33+ Content blocks are the building blocks for tool results, prompts, and resource contents.
44+ They support text, images, audio, embedded resources, and tool use/results. *)
55+66+(** {1 Annotations} *)
77+88+module Audience : sig
99+ type t = User | Assistant
1010+ (** Who should see this content *)
1111+1212+ val jsont : t Jsont.t
1313+ val pp : Format.formatter -> t -> unit
1414+end
1515+1616+module Annotations : sig
1717+ type t = {
1818+ audience : Audience.t list option;
1919+ priority : float option;
2020+ unknown : Jsont.json;
2121+ }
2222+ (** Hints about content visibility and importance *)
2323+2424+ val empty : t
2525+ val jsont : t Jsont.t
2626+ val pp : Format.formatter -> t -> unit
2727+end
2828+2929+(** {1 Text Content} *)
3030+3131+module Text : sig
3232+ type t = {
3333+ text : string;
3434+ annotations : Annotations.t option;
3535+ unknown : Jsont.json;
3636+ }
3737+ (** Plain text content *)
3838+3939+ val make : string -> t
4040+ val jsont : t Jsont.t
4141+ val pp : Format.formatter -> t -> unit
4242+end
4343+4444+(** {1 Image Content} *)
4545+4646+module Image : sig
4747+ type t = {
4848+ data : string; (** Base64-encoded image data *)
4949+ mime_type : string; (** e.g. "image/png" *)
5050+ annotations : Annotations.t option;
5151+ unknown : Jsont.json;
5252+ }
5353+ (** Image content (base64-encoded) *)
5454+5555+ val make : data:string -> mime_type:string -> t
5656+ val jsont : t Jsont.t
5757+ val pp : Format.formatter -> t -> unit
5858+end
5959+6060+(** {1 Audio Content} *)
6161+6262+module Audio : sig
6363+ type t = {
6464+ data : string; (** Base64-encoded audio data *)
6565+ mime_type : string; (** e.g. "audio/mp3" *)
6666+ annotations : Annotations.t option;
6767+ unknown : Jsont.json;
6868+ }
6969+ (** Audio content (base64-encoded) *)
7070+7171+ val make : data:string -> mime_type:string -> t
7272+ val jsont : t Jsont.t
7373+ val pp : Format.formatter -> t -> unit
7474+end
7575+7676+(** {1 Embedded Resource} *)
7777+7878+module Embedded_resource : sig
7979+ type resource = {
8080+ uri : string;
8181+ mime_type : string option;
8282+ text : string option;
8383+ blob : string option; (** Base64-encoded binary data *)
8484+ unknown : Jsont.json;
8585+ }
8686+ (** Resource contents *)
8787+8888+ type t = {
8989+ resource : resource;
9090+ annotations : Annotations.t option;
9191+ unknown : Jsont.json;
9292+ }
9393+ (** Embedded resource content *)
9494+9595+ val make_text : uri:string -> text:string -> ?mime_type:string -> unit -> t
9696+ val make_blob : uri:string -> blob:string -> mime_type:string -> t
9797+ val jsont : t Jsont.t
9898+ val pp : Format.formatter -> t -> unit
9999+end
100100+101101+(** {1 Content Block} *)
102102+103103+type block =
104104+ | Text of Text.t
105105+ | Image of Image.t
106106+ | Audio of Audio.t
107107+ | Embedded_resource of Embedded_resource.t
108108+(** Content block variants *)
109109+110110+val block_jsont : block Jsont.t
111111+(** Codec for content blocks (discriminated by "type" field) *)
112112+113113+val pp_block : Format.formatter -> block -> unit
114114+(** Pretty-print a content block *)
115115+116116+(** {1 Convenience Constructors} *)
117117+118118+val text : string -> block
119119+(** Create a text content block *)
120120+121121+val image : data:string -> mime_type:string -> block
122122+(** Create an image content block *)
123123+124124+val audio : data:string -> mime_type:string -> block
125125+(** Create an audio content block *)
···11+(** JSON-RPC 2.0 protocol implementation *)
22+33+(* Protocol Version *)
44+55+type jsonrpc = [ `V2 ]
66+77+let jsonrpc_jsont = Jsont.enum ["2.0", `V2]
88+99+(* Request/Response Identifiers *)
1010+1111+module Id = struct
1212+ type t = [ `String of string | `Number of float | `Null ]
1313+1414+ let jsont : t Jsont.t =
1515+ let null = Jsont.null `Null in
1616+ let string =
1717+ let dec s = `String s in
1818+ let enc = function `String s -> s | _ -> assert false in
1919+ Jsont.map ~dec ~enc Jsont.string
2020+ in
2121+ let number =
2222+ let dec n = `Number n in
2323+ let enc = function `Number n -> n | _ -> assert false in
2424+ Jsont.map ~dec ~enc Jsont.number
2525+ in
2626+ let enc = function
2727+ | `Null -> null | `String _ -> string | `Number _ -> number
2828+ in
2929+ Jsont.any ~dec_null:null ~dec_string:string ~dec_number:number ~enc ()
3030+3131+ let to_string = function
3232+ | `String s -> s
3333+ | `Number n -> string_of_float n
3434+ | `Null -> "null"
3535+3636+ let compare a b = match a, b with
3737+ | `Null, `Null -> 0
3838+ | `Null, _ -> -1
3939+ | _, `Null -> 1
4040+ | `String s1, `String s2 -> String.compare s1 s2
4141+ | `String _, _ -> -1
4242+ | _, `String _ -> 1
4343+ | `Number n1, `Number n2 -> Float.compare n1 n2
4444+4545+ let pp fmt = function
4646+ | `String s -> Format.fprintf fmt "%S" s
4747+ | `Number n -> Format.fprintf fmt "%g" n
4848+ | `Null -> Format.fprintf fmt "null"
4949+end
5050+5151+(* Error Codes *)
5252+5353+module Error_code = struct
5454+ type t =
5555+ | Parse_error
5656+ | Invalid_request
5757+ | Method_not_found
5858+ | Invalid_params
5959+ | Internal_error
6060+ | Connection_closed
6161+ | Server_error of int
6262+ | Other of int
6363+6464+ let to_int = function
6565+ | Parse_error -> -32700
6666+ | Invalid_request -> -32600
6767+ | Method_not_found -> -32601
6868+ | Invalid_params -> -32602
6969+ | Internal_error -> -32603
7070+ | Connection_closed -> -32000
7171+ | Server_error n -> n
7272+ | Other n -> n
7373+7474+ let of_int = function
7575+ | -32700 -> Parse_error
7676+ | -32600 -> Invalid_request
7777+ | -32601 -> Method_not_found
7878+ | -32602 -> Invalid_params
7979+ | -32603 -> Internal_error
8080+ | -32000 -> Connection_closed
8181+ | n when n >= -32099 && n <= -32001 -> Server_error n
8282+ | n -> Other n
8383+8484+ let jsont : t Jsont.t =
8585+ let dec n = of_int n in
8686+ let enc code = to_int code in
8787+ Jsont.map ~dec ~enc Jsont.int
8888+8989+ let pp fmt code =
9090+ Format.fprintf fmt "%d" (to_int code)
9191+end
9292+9393+(* Error Data *)
9494+9595+module Error_data = struct
9696+ type t = {
9797+ code : Error_code.t;
9898+ message : string;
9999+ data : Jsont.json option;
100100+ unknown : Jsont.json;
101101+ }
102102+103103+ let make ~code ~message ?data () =
104104+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
105105+ { code; message; data; unknown }
106106+107107+ let jsont : t Jsont.t =
108108+ let make code message data unknown = { code; message; data; unknown } in
109109+ Jsont.Object.map ~kind:"ErrorData" make
110110+ |> Jsont.Object.mem "code" Error_code.jsont ~enc:(fun e -> e.code)
111111+ |> Jsont.Object.mem "message" Jsont.string ~enc:(fun e -> e.message)
112112+ |> Jsont.Object.opt_mem "data" Jsont.json ~enc:(fun e -> e.data)
113113+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun e -> e.unknown)
114114+ |> Jsont.Object.finish
115115+116116+ let pp fmt err =
117117+ Format.fprintf fmt "{code=%a, message=%S}" Error_code.pp err.code err.message
118118+end
119119+120120+(* Params *)
121121+122122+type params = Jsont.json
123123+124124+let params_jsont =
125125+ let enc = function
126126+ | Jsont.Object _ | Jsont.Array _ -> Jsont.json
127127+ | j ->
128128+ let meta = Jsont.Meta.none in
129129+ let fnd = Jsont.Sort.to_string (Jsont.Json.sort j) in
130130+ Jsont.Error.expected meta "object or array" ~fnd
131131+ in
132132+ let kind = "JSON-RPC params" in
133133+ Jsont.any ~kind ~dec_array:Jsont.json ~dec_object:Jsont.json ~enc ()
134134+135135+(* Request Message *)
136136+137137+module Request = struct
138138+ type t = {
139139+ jsonrpc : jsonrpc;
140140+ method_ : string;
141141+ params : params option;
142142+ id : Id.t option;
143143+ unknown : Jsont.json;
144144+ }
145145+146146+ let make ~method_ ?params ?id () =
147147+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
148148+ { jsonrpc = `V2; method_; params; id; unknown }
149149+150150+ let jsont : t Jsont.t =
151151+ let make jsonrpc method_ params id unknown =
152152+ { jsonrpc; method_; params; id; unknown }
153153+ in
154154+ Jsont.Object.map ~kind:"JSONRPCRequest" make
155155+ |> Jsont.Object.mem "jsonrpc" jsonrpc_jsont ~enc:(fun r -> r.jsonrpc)
156156+ |> Jsont.Object.mem "method" Jsont.string ~enc:(fun r -> r.method_)
157157+ |> Jsont.Object.opt_mem "params" params_jsont ~enc:(fun r -> r.params)
158158+ |> Jsont.Object.opt_mem "id" Id.jsont ~enc:(fun r -> r.id)
159159+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
160160+ |> Jsont.Object.finish
161161+162162+ let pp fmt req =
163163+ let id_str = match req.id with
164164+ | Some id -> Id.to_string id
165165+ | None -> "none"
166166+ in
167167+ Format.fprintf fmt "{method=%S, id=%s}" req.method_ id_str
168168+end
169169+170170+(* Response Message *)
171171+172172+module Response = struct
173173+ type t = {
174174+ jsonrpc : jsonrpc;
175175+ value : (Jsont.json, Error_data.t) result;
176176+ id : Id.t;
177177+ unknown : Jsont.json;
178178+ }
179179+180180+ let make_result ~id ~result =
181181+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
182182+ { jsonrpc = `V2; value = Ok result; id; unknown }
183183+184184+ let make_error ~id ~error =
185185+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
186186+ { jsonrpc = `V2; value = Error error; id; unknown }
187187+188188+ let response_result r = match r.value with Ok v -> Some v | Error _ -> None
189189+ let response_error r = match r.value with Ok _ -> None | Error e -> Some e
190190+191191+ let response jsonrpc result error id : t =
192192+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
193193+ let err_both () =
194194+ Jsont.Error.msgf Jsont.Meta.none "Both %a and %a members are defined"
195195+ Jsont.Repr.pp_code "result" Jsont.Repr.pp_code "error"
196196+ in
197197+ let err_none () =
198198+ Jsont.Error.msgf Jsont.Meta.none "Missing either %a or %a member"
199199+ Jsont.Repr.pp_code "result" Jsont.Repr.pp_code "error"
200200+ in
201201+ match result, error with
202202+ | Some result, None -> { jsonrpc; value = Ok result; id; unknown }
203203+ | None, Some error -> { jsonrpc; value = Error error; id; unknown }
204204+ | Some _ , Some _ -> err_both ()
205205+ | None, None -> err_none ()
206206+207207+ let jsont : t Jsont.t =
208208+ let make jsonrpc result error id unknown =
209209+ let resp = response jsonrpc result error id in
210210+ { resp with unknown }
211211+ in
212212+ Jsont.Object.map ~kind:"JSONRPCResponse" make
213213+ |> Jsont.Object.mem "jsonrpc" jsonrpc_jsont ~enc:(fun r -> r.jsonrpc)
214214+ |> Jsont.Object.opt_mem "result" Jsont.json ~enc:response_result
215215+ |> Jsont.Object.opt_mem "error" Error_data.jsont ~enc:response_error
216216+ |> Jsont.Object.mem "id" Id.jsont ~enc:(fun r -> r.id)
217217+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
218218+ |> Jsont.Object.finish
219219+220220+ let pp fmt resp =
221221+ let result_str = match resp.value with
222222+ | Ok _ -> "Ok(...)"
223223+ | Error err -> Format.asprintf "Error(%a)" Error_data.pp err
224224+ in
225225+ Format.fprintf fmt "{id=%a, %s}" Id.pp resp.id result_str
226226+end
227227+228228+(* Message Union *)
229229+230230+module Message = struct
231231+ type t =
232232+ | Request of Request.t
233233+ | Response of Response.t
234234+235235+ let classify json =
236236+ (* Detect message type by presence of fields:
237237+ - "method" -> Request
238238+ - "result" or "error" -> Response *)
239239+ match json with
240240+ | Jsont.Object (members, _) ->
241241+ let has_method = List.exists (fun ((name, _), _) -> name = "method") members in
242242+ let has_result_or_error =
243243+ List.exists (fun ((name, _), _) ->
244244+ name = "result" || name = "error"
245245+ ) members
246246+ in
247247+ if has_method then
248248+ match Jsont.Json.decode Request.jsont json with
249249+ | Ok req -> Request req
250250+ | Error msg -> failwith ("Failed to decode request: " ^ msg)
251251+ else if has_result_or_error then
252252+ match Jsont.Json.decode Response.jsont json with
253253+ | Ok resp -> Response resp
254254+ | Error msg -> failwith ("Failed to decode response: " ^ msg)
255255+ else
256256+ failwith "Invalid JSON-RPC message: missing method or result/error"
257257+ | _ ->
258258+ failwith "Invalid JSON-RPC message: not an object"
259259+260260+ let jsont : t Jsont.t =
261261+ let enc = function
262262+ | Request req ->
263263+ (match Jsont.Json.encode Request.jsont req with
264264+ | Ok json -> json
265265+ | Error msg -> failwith ("Failed to encode request: " ^ msg))
266266+ | Response resp ->
267267+ (match Jsont.Json.encode Response.jsont resp with
268268+ | Ok json -> json
269269+ | Error msg -> failwith ("Failed to encode response: " ^ msg))
270270+ in
271271+ let dec json =
272272+ classify json
273273+ in
274274+ Jsont.map ~kind:"JSONRPCMessage" ~dec ~enc Jsont.json
275275+276276+ let pp fmt = function
277277+ | Request req -> Format.fprintf fmt "Request(%a)" Request.pp req
278278+ | Response resp -> Format.fprintf fmt "Response(%a)" Response.pp resp
279279+end
+161
claudeio/lib_mcp/jsonrpc.mli
···11+(** JSON-RPC 2.0 protocol implementation using jsont.
22+33+ Based on the JSON-RPC 2.0 specification: https://www.jsonrpc.org/
44+55+ This module provides type-safe encoding/decoding of JSON-RPC messages
66+ with forward-compatible unknown field preservation. *)
77+88+(** {1 Protocol Version} *)
99+1010+type jsonrpc = [ `V2 ]
1111+(** JSON-RPC protocol version *)
1212+1313+val jsonrpc_jsont : jsonrpc Jsont.t
1414+(** Codec for protocol version *)
1515+1616+(** {1 Request/Response Identifiers} *)
1717+1818+module Id : sig
1919+ type t = [ `String of string | `Number of float | `Null ]
2020+ (** Request/response correlation ID.
2121+ Can be a string, number, or null. *)
2222+2323+ val jsont : t Jsont.t
2424+ (** Codec for IDs *)
2525+2626+ val to_string : t -> string
2727+ (** Convert ID to string representation *)
2828+2929+ val compare : t -> t -> int
3030+ (** Compare IDs for ordering *)
3131+3232+ val pp : Format.formatter -> t -> unit
3333+ (** Pretty-print an ID *)
3434+end
3535+3636+(** {1 Error Codes} *)
3737+3838+module Error_code : sig
3939+ type t =
4040+ | Parse_error (** -32700: Invalid JSON *)
4141+ | Invalid_request (** -32600: Invalid Request object *)
4242+ | Method_not_found (** -32601: Method does not exist *)
4343+ | Invalid_params (** -32602: Invalid method parameters *)
4444+ | Internal_error (** -32603: Internal JSON-RPC error *)
4545+ | Connection_closed (** -32000: MCP-specific: connection closed *)
4646+ | Server_error of int (** -32099 to -32000: Server error *)
4747+ | Other of int (** Implementation-defined error *)
4848+4949+ val jsont : t Jsont.t
5050+ (** Codec for error codes *)
5151+5252+ val to_int : t -> int
5353+ (** Convert error code to integer *)
5454+5555+ val of_int : int -> t
5656+ (** Convert integer to error code *)
5757+5858+ val pp : Format.formatter -> t -> unit
5959+ (** Pretty-print an error code *)
6060+end
6161+6262+(** {1 Error Data} *)
6363+6464+module Error_data : sig
6565+ type t = {
6666+ code : Error_code.t;
6767+ message : string;
6868+ data : Jsont.json option;
6969+ unknown : Jsont.json;
7070+ }
7171+ (** Error information *)
7272+7373+ val make : code:Error_code.t -> message:string -> ?data:Jsont.json -> unit -> t
7474+ (** Create error data *)
7575+7676+ val jsont : t Jsont.t
7777+ (** Codec for error data *)
7878+7979+ val pp : Format.formatter -> t -> unit
8080+ (** Pretty-print error data *)
8181+end
8282+8383+(** {1 Params} *)
8484+8585+type params = Jsont.json
8686+(** Parameters for requests (must be Array or Object) *)
8787+8888+val params_jsont : params Jsont.t
8989+(** Codec for params (validates array or object) *)
9090+9191+(** {1 Request Message} *)
9292+9393+module Request : sig
9494+ type t = {
9595+ jsonrpc : jsonrpc;
9696+ method_ : string;
9797+ params : params option;
9898+ id : Id.t option;
9999+ unknown : Jsont.json;
100100+ }
101101+ (** JSON-RPC request.
102102+ - If [id] is [Some _], expects a response
103103+ - If [id] is [None], it's a notification (no response) *)
104104+105105+ val make :
106106+ method_:string ->
107107+ ?params:params ->
108108+ ?id:Id.t ->
109109+ unit ->
110110+ t
111111+ (** Create a request *)
112112+113113+ val jsont : t Jsont.t
114114+ (** Codec for requests *)
115115+116116+ val pp : Format.formatter -> t -> unit
117117+ (** Pretty-print a request *)
118118+end
119119+120120+(** {1 Response Message} *)
121121+122122+module Response : sig
123123+ type t = {
124124+ jsonrpc : jsonrpc;
125125+ value : (Jsont.json, Error_data.t) result;
126126+ id : Id.t;
127127+ unknown : Jsont.json;
128128+ }
129129+ (** JSON-RPC response.
130130+ Either contains [Ok result] or [Error error]. *)
131131+132132+ val make_result : id:Id.t -> result:Jsont.json -> t
133133+ (** Create a successful response *)
134134+135135+ val make_error : id:Id.t -> error:Error_data.t -> t
136136+ (** Create an error response *)
137137+138138+ val jsont : t Jsont.t
139139+ (** Codec for responses *)
140140+141141+ val pp : Format.formatter -> t -> unit
142142+ (** Pretty-print a response *)
143143+end
144144+145145+(** {1 Message Union} *)
146146+147147+module Message : sig
148148+ type t =
149149+ | Request of Request.t
150150+ | Response of Response.t
151151+ (** Union of all JSON-RPC message types *)
152152+153153+ val jsont : t Jsont.t
154154+ (** Codec for messages *)
155155+156156+ val classify : Jsont.json -> t
157157+ (** Classify a JSON value as a specific message type *)
158158+159159+ val pp : Format.formatter -> t -> unit
160160+ (** Pretty-print a message *)
161161+end
···11+(** Model Context Protocol (MCP) OCaml Implementation.
22+33+ This library provides a type-safe, Eio-based implementation of the Model Context Protocol,
44+ using jsont for JSON serialization with forward-compatible unknown field preservation.
55+66+ {1 Quick Start}
77+88+ The MCP library is organized into several modules:
99+1010+ - {!Jsonrpc}: JSON-RPC 2.0 protocol layer
1111+ - {!Content}: Content block types (text, image, audio, resources)
1212+ - {!Capabilities}: Client and server capability negotiation
1313+1414+ {1 Example}
1515+1616+ {[
1717+ open Mcp
1818+1919+ (* Create client capabilities *)
2020+ let client_caps = Capabilities.Client.make
2121+ ~sampling:(Capabilities.Sampling.make ~tools:true ())
2222+ ()
2323+2424+ (* Create content blocks *)
2525+ let text_block = Content.text "Hello, MCP!"
2626+ let image_block = Content.image ~data:"..." ~mime_type:"image/png"
2727+ ]}
2828+2929+ {1 Design Principles}
3030+3131+ - {b Type Safety}: All protocol types use jsont codecs for bidirectional JSON serialization
3232+ - {b Forward Compatibility}: Unknown fields are preserved in all types
3333+ - {b Eio Integration}: Uses Eio for structured concurrency
3434+ - {b Protocol Compliance}: Follows MCP specification exactly
3535+3636+ {1 Modules} *)
3737+3838+(** JSON-RPC 2.0 protocol implementation *)
3939+module Jsonrpc : module type of Jsonrpc
4040+4141+(** MCP content block types *)
4242+module Content : module type of Content
4343+4444+(** Client and server capability negotiation *)
4545+module Capabilities : module type of Capabilities
4646+4747+(** MCP protocol messages (initialize, resources, tools, prompts, logging, etc.) *)
4848+module Messages : module type of Messages
4949+5050+(** Bidirectional JSON-RPC session management *)
5151+module Session : module type of Session
5252+5353+(** Transport layer for JSON-RPC communication *)
5454+module Transport : module type of Transport
5555+5656+(** Stdio transport implementation *)
5757+module Transport_stdio : module type of Transport_stdio
5858+5959+(** High-level MCP server session API *)
6060+module Server_session : module type of Server_session
6161+6262+(** High-level MCP client session API *)
6363+module Client_session : module type of Client_session
+900
claudeio/lib_mcp/messages.ml
···11+(** MCP Protocol Messages *)
22+33+(* Protocol Version *)
44+55+type protocol_version = string
66+77+let protocol_version_jsont = Jsont.string
88+99+(* Initialize Protocol *)
1010+1111+module Initialize = struct
1212+ type request_params = {
1313+ protocol_version : protocol_version;
1414+ capabilities : Capabilities.Client.t;
1515+ client_info : Capabilities.Implementation.t;
1616+ unknown : Jsont.json;
1717+ }
1818+1919+ let make_request_params ~protocol_version ~capabilities ~client_info () =
2020+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
2121+ { protocol_version; capabilities; client_info; unknown }
2222+2323+ let request_params_jsont : request_params Jsont.t =
2424+ let make protocol_version capabilities client_info unknown =
2525+ { protocol_version; capabilities; client_info; unknown }
2626+ in
2727+ Jsont.Object.map ~kind:"InitializeRequestParams" make
2828+ |> Jsont.Object.mem "protocolVersion" protocol_version_jsont
2929+ ~enc:(fun p -> p.protocol_version)
3030+ |> Jsont.Object.mem "capabilities" Capabilities.Client.jsont
3131+ ~enc:(fun p -> p.capabilities)
3232+ |> Jsont.Object.mem "clientInfo" Capabilities.Implementation.jsont
3333+ ~enc:(fun p -> p.client_info)
3434+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun p -> p.unknown)
3535+ |> Jsont.Object.finish
3636+3737+ type result = {
3838+ protocol_version : protocol_version;
3939+ capabilities : Capabilities.Server.t;
4040+ server_info : Capabilities.Implementation.t;
4141+ instructions : string option;
4242+ unknown : Jsont.json;
4343+ }
4444+4545+ let make_result ~protocol_version ~capabilities ~server_info ?instructions () =
4646+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
4747+ { protocol_version; capabilities; server_info; instructions; unknown }
4848+4949+ let result_jsont : result Jsont.t =
5050+ let make protocol_version capabilities server_info instructions unknown =
5151+ { protocol_version; capabilities; server_info; instructions; unknown }
5252+ in
5353+ Jsont.Object.map ~kind:"InitializeResult" make
5454+ |> Jsont.Object.mem "protocolVersion" protocol_version_jsont
5555+ ~enc:(fun r -> r.protocol_version)
5656+ |> Jsont.Object.mem "capabilities" Capabilities.Server.jsont
5757+ ~enc:(fun r -> r.capabilities)
5858+ |> Jsont.Object.mem "serverInfo" Capabilities.Implementation.jsont
5959+ ~enc:(fun r -> r.server_info)
6060+ |> Jsont.Object.opt_mem "instructions" Jsont.string
6161+ ~enc:(fun r -> r.instructions)
6262+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
6363+ |> Jsont.Object.finish
6464+6565+ let method_ = "initialize"
6666+end
6767+6868+module Initialized = struct
6969+ type notification = {
7070+ unknown : Jsont.json;
7171+ }
7272+7373+ let make_notification () =
7474+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
7575+ { unknown }
7676+7777+ let notification_jsont : notification Jsont.t =
7878+ let make unknown = { unknown } in
7979+ Jsont.Object.map ~kind:"InitializedNotification" make
8080+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
8181+ |> Jsont.Object.finish
8282+8383+ let method_ = "notifications/initialized"
8484+end
8585+8686+module Ping = struct
8787+ type params = {
8888+ unknown : Jsont.json;
8989+ }
9090+9191+ let make_params () =
9292+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
9393+ { unknown }
9494+9595+ let params_jsont : params Jsont.t =
9696+ let make unknown = { unknown } in
9797+ Jsont.Object.map ~kind:"PingParams" make
9898+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun p -> p.unknown)
9999+ |> Jsont.Object.finish
100100+101101+ type result = {
102102+ unknown : Jsont.json;
103103+ }
104104+105105+ let make_result () =
106106+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
107107+ { unknown }
108108+109109+ let result_jsont : result Jsont.t =
110110+ let make unknown = { unknown } in
111111+ Jsont.Object.map ~kind:"PingResult" make
112112+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
113113+ |> Jsont.Object.finish
114114+115115+ let method_ = "ping"
116116+end
117117+118118+(* Resources *)
119119+120120+module Resources = struct
121121+ type resource = {
122122+ uri : string;
123123+ name : string;
124124+ description : string option;
125125+ mime_type : string option;
126126+ unknown : Jsont.json;
127127+ }
128128+129129+ let make_resource ~uri ~name ?description ?mime_type () =
130130+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
131131+ { uri; name; description; mime_type; unknown }
132132+133133+ let resource_jsont : resource Jsont.t =
134134+ let make uri name description mime_type unknown =
135135+ { uri; name; description; mime_type; unknown }
136136+ in
137137+ Jsont.Object.map ~kind:"Resource" make
138138+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
139139+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun r -> r.name)
140140+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun r -> r.description)
141141+ |> Jsont.Object.opt_mem "mimeType" Jsont.string ~enc:(fun r -> r.mime_type)
142142+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
143143+ |> Jsont.Object.finish
144144+145145+ type resource_template = {
146146+ uri_template : string;
147147+ name : string;
148148+ description : string option;
149149+ mime_type : string option;
150150+ unknown : Jsont.json;
151151+ }
152152+153153+ let make_resource_template ~uri_template ~name ?description ?mime_type () =
154154+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
155155+ { uri_template; name; description; mime_type; unknown }
156156+157157+ let resource_template_jsont : resource_template Jsont.t =
158158+ let make uri_template name description mime_type unknown =
159159+ { uri_template; name; description; mime_type; unknown }
160160+ in
161161+ Jsont.Object.map ~kind:"ResourceTemplate" make
162162+ |> Jsont.Object.mem "uriTemplate" Jsont.string ~enc:(fun t -> t.uri_template)
163163+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun t -> t.name)
164164+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun t -> t.description)
165165+ |> Jsont.Object.opt_mem "mimeType" Jsont.string ~enc:(fun t -> t.mime_type)
166166+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun t -> t.unknown)
167167+ |> Jsont.Object.finish
168168+169169+ type resource_contents = {
170170+ uri : string;
171171+ mime_type : string option;
172172+ text : string option;
173173+ blob : string option;
174174+ unknown : Jsont.json;
175175+ }
176176+177177+ let make_text_contents ~uri ~text ?mime_type () =
178178+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
179179+ { uri; mime_type; text = Some text; blob = None; unknown }
180180+181181+ let make_blob_contents ~uri ~blob ~mime_type =
182182+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
183183+ { uri; mime_type = Some mime_type; text = None; blob = Some blob; unknown }
184184+185185+ let resource_contents_jsont : resource_contents Jsont.t =
186186+ let make uri mime_type text blob unknown =
187187+ { uri; mime_type; text; blob; unknown }
188188+ in
189189+ Jsont.Object.map ~kind:"ResourceContents" make
190190+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun c -> c.uri)
191191+ |> Jsont.Object.opt_mem "mimeType" Jsont.string ~enc:(fun c -> c.mime_type)
192192+ |> Jsont.Object.opt_mem "text" Jsont.string ~enc:(fun c -> c.text)
193193+ |> Jsont.Object.opt_mem "blob" Jsont.string ~enc:(fun c -> c.blob)
194194+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun c -> c.unknown)
195195+ |> Jsont.Object.finish
196196+197197+ type list_request = {
198198+ cursor : string option;
199199+ unknown : Jsont.json;
200200+ }
201201+202202+ let make_list_request ?cursor () =
203203+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
204204+ { cursor; unknown }
205205+206206+ let list_request_jsont : list_request Jsont.t =
207207+ let make cursor unknown = { cursor; unknown } in
208208+ Jsont.Object.map ~kind:"ResourcesListRequest" make
209209+ |> Jsont.Object.opt_mem "cursor" Jsont.string ~enc:(fun r -> r.cursor)
210210+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
211211+ |> Jsont.Object.finish
212212+213213+ type list_result = {
214214+ resources : resource list;
215215+ next_cursor : string option;
216216+ unknown : Jsont.json;
217217+ }
218218+219219+ let make_list_result ~resources ?next_cursor () =
220220+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
221221+ { resources; next_cursor; unknown }
222222+223223+ let list_result_jsont : list_result Jsont.t =
224224+ let make resources next_cursor unknown =
225225+ { resources; next_cursor; unknown }
226226+ in
227227+ Jsont.Object.map ~kind:"ResourcesListResult" make
228228+ |> Jsont.Object.mem "resources" (Jsont.list resource_jsont)
229229+ ~enc:(fun r -> r.resources)
230230+ |> Jsont.Object.opt_mem "nextCursor" Jsont.string
231231+ ~enc:(fun r -> r.next_cursor)
232232+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
233233+ |> Jsont.Object.finish
234234+235235+ type read_request = {
236236+ uri : string;
237237+ unknown : Jsont.json;
238238+ }
239239+240240+ let make_read_request ~uri =
241241+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
242242+ { uri; unknown }
243243+244244+ let read_request_jsont : read_request Jsont.t =
245245+ let make uri unknown = { uri; unknown } in
246246+ Jsont.Object.map ~kind:"ResourcesReadRequest" make
247247+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
248248+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
249249+ |> Jsont.Object.finish
250250+251251+ type read_result = {
252252+ contents : resource_contents list;
253253+ unknown : Jsont.json;
254254+ }
255255+256256+ let make_read_result ~contents =
257257+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
258258+ { contents; unknown }
259259+260260+ let read_result_jsont : read_result Jsont.t =
261261+ let make contents unknown = { contents; unknown } in
262262+ Jsont.Object.map ~kind:"ResourcesReadResult" make
263263+ |> Jsont.Object.mem "contents" (Jsont.list resource_contents_jsont)
264264+ ~enc:(fun r -> r.contents)
265265+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
266266+ |> Jsont.Object.finish
267267+268268+ type subscribe_request = {
269269+ uri : string;
270270+ unknown : Jsont.json;
271271+ }
272272+273273+ let make_subscribe_request ~uri =
274274+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
275275+ { uri; unknown }
276276+277277+ let subscribe_request_jsont : subscribe_request Jsont.t =
278278+ let make uri unknown = { uri; unknown } in
279279+ Jsont.Object.map ~kind:"ResourcesSubscribeRequest" make
280280+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
281281+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
282282+ |> Jsont.Object.finish
283283+284284+ type unsubscribe_request = {
285285+ uri : string;
286286+ unknown : Jsont.json;
287287+ }
288288+289289+ let make_unsubscribe_request ~uri =
290290+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
291291+ { uri; unknown }
292292+293293+ let unsubscribe_request_jsont : unsubscribe_request Jsont.t =
294294+ let make uri unknown = { uri; unknown } in
295295+ Jsont.Object.map ~kind:"ResourcesUnsubscribeRequest" make
296296+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
297297+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
298298+ |> Jsont.Object.finish
299299+300300+ type updated_notification = {
301301+ uri : string;
302302+ unknown : Jsont.json;
303303+ }
304304+305305+ let make_updated_notification ~uri =
306306+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
307307+ { uri; unknown }
308308+309309+ let updated_notification_jsont : updated_notification Jsont.t =
310310+ let make uri unknown = { uri; unknown } in
311311+ Jsont.Object.map ~kind:"ResourceUpdatedNotification" make
312312+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun n -> n.uri)
313313+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
314314+ |> Jsont.Object.finish
315315+316316+ type list_changed_notification = {
317317+ unknown : Jsont.json;
318318+ }
319319+320320+ let make_list_changed_notification () =
321321+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
322322+ { unknown }
323323+324324+ let list_changed_notification_jsont : list_changed_notification Jsont.t =
325325+ let make unknown = { unknown } in
326326+ Jsont.Object.map ~kind:"ResourceListChangedNotification" make
327327+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
328328+ |> Jsont.Object.finish
329329+330330+ let list_method = "resources/list"
331331+ let read_method = "resources/read"
332332+ let subscribe_method = "resources/subscribe"
333333+ let unsubscribe_method = "resources/unsubscribe"
334334+ let updated_notification_method = "notifications/resources/updated"
335335+ let list_changed_notification_method = "notifications/resources/list_changed"
336336+end
337337+338338+(* Tools *)
339339+340340+module Tools = struct
341341+ type tool = {
342342+ name : string;
343343+ description : string option;
344344+ input_schema : Jsont.json;
345345+ unknown : Jsont.json;
346346+ }
347347+348348+ let make_tool ~name ?description ~input_schema () =
349349+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
350350+ { name; description; input_schema; unknown }
351351+352352+ let tool_jsont : tool Jsont.t =
353353+ let make name description input_schema unknown =
354354+ { name; description; input_schema; unknown }
355355+ in
356356+ Jsont.Object.map ~kind:"Tool" make
357357+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun t -> t.name)
358358+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun t -> t.description)
359359+ |> Jsont.Object.mem "inputSchema" Jsont.json ~enc:(fun t -> t.input_schema)
360360+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun t -> t.unknown)
361361+ |> Jsont.Object.finish
362362+363363+ type list_request = {
364364+ cursor : string option;
365365+ unknown : Jsont.json;
366366+ }
367367+368368+ let make_list_request ?cursor () =
369369+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
370370+ { cursor; unknown }
371371+372372+ let list_request_jsont : list_request Jsont.t =
373373+ let make cursor unknown = { cursor; unknown } in
374374+ Jsont.Object.map ~kind:"ToolsListRequest" make
375375+ |> Jsont.Object.opt_mem "cursor" Jsont.string ~enc:(fun r -> r.cursor)
376376+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
377377+ |> Jsont.Object.finish
378378+379379+ type list_result = {
380380+ tools : tool list;
381381+ next_cursor : string option;
382382+ unknown : Jsont.json;
383383+ }
384384+385385+ let make_list_result ~tools ?next_cursor () =
386386+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
387387+ { tools; next_cursor; unknown }
388388+389389+ let list_result_jsont : list_result Jsont.t =
390390+ let make tools next_cursor unknown =
391391+ { tools; next_cursor; unknown }
392392+ in
393393+ Jsont.Object.map ~kind:"ToolsListResult" make
394394+ |> Jsont.Object.mem "tools" (Jsont.list tool_jsont)
395395+ ~enc:(fun r -> r.tools)
396396+ |> Jsont.Object.opt_mem "nextCursor" Jsont.string
397397+ ~enc:(fun r -> r.next_cursor)
398398+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
399399+ |> Jsont.Object.finish
400400+401401+ type call_request = {
402402+ name : string;
403403+ arguments : Jsont.json option;
404404+ unknown : Jsont.json;
405405+ }
406406+407407+ let make_call_request ~name ?arguments () =
408408+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
409409+ { name; arguments; unknown }
410410+411411+ let call_request_jsont : call_request Jsont.t =
412412+ let make name arguments unknown = { name; arguments; unknown } in
413413+ Jsont.Object.map ~kind:"ToolsCallRequest" make
414414+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun r -> r.name)
415415+ |> Jsont.Object.opt_mem "arguments" Jsont.json ~enc:(fun r -> r.arguments)
416416+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
417417+ |> Jsont.Object.finish
418418+419419+ type call_result = {
420420+ content : Content.block list;
421421+ is_error : bool option;
422422+ unknown : Jsont.json;
423423+ }
424424+425425+ let make_call_result ~content ?is_error () =
426426+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
427427+ { content; is_error; unknown }
428428+429429+ let call_result_jsont : call_result Jsont.t =
430430+ let make content is_error unknown =
431431+ { content; is_error; unknown }
432432+ in
433433+ Jsont.Object.map ~kind:"ToolsCallResult" make
434434+ |> Jsont.Object.mem "content" (Jsont.list Content.block_jsont)
435435+ ~enc:(fun r -> r.content)
436436+ |> Jsont.Object.opt_mem "isError" Jsont.bool ~enc:(fun r -> r.is_error)
437437+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
438438+ |> Jsont.Object.finish
439439+440440+ type list_changed_notification = {
441441+ unknown : Jsont.json;
442442+ }
443443+444444+ let make_list_changed_notification () =
445445+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
446446+ { unknown }
447447+448448+ let list_changed_notification_jsont : list_changed_notification Jsont.t =
449449+ let make unknown = { unknown } in
450450+ Jsont.Object.map ~kind:"ToolsListChangedNotification" make
451451+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
452452+ |> Jsont.Object.finish
453453+454454+ let list_method = "tools/list"
455455+ let call_method = "tools/call"
456456+ let list_changed_notification_method = "notifications/tools/list_changed"
457457+end
458458+459459+(* Prompts *)
460460+461461+module Prompts = struct
462462+ type prompt_argument = {
463463+ name : string;
464464+ description : string option;
465465+ required : bool option;
466466+ unknown : Jsont.json;
467467+ }
468468+469469+ let make_prompt_argument ~name ?description ?required () =
470470+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
471471+ { name; description; required; unknown }
472472+473473+ let prompt_argument_jsont : prompt_argument Jsont.t =
474474+ let make name description required unknown =
475475+ { name; description; required; unknown }
476476+ in
477477+ Jsont.Object.map ~kind:"PromptArgument" make
478478+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun a -> a.name)
479479+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun a -> a.description)
480480+ |> Jsont.Object.opt_mem "required" Jsont.bool ~enc:(fun a -> a.required)
481481+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun a -> a.unknown)
482482+ |> Jsont.Object.finish
483483+484484+ type prompt = {
485485+ name : string;
486486+ description : string option;
487487+ arguments : prompt_argument list option;
488488+ unknown : Jsont.json;
489489+ }
490490+491491+ let make_prompt ~name ?description ?arguments () =
492492+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
493493+ { name; description; arguments; unknown }
494494+495495+ let prompt_jsont : prompt Jsont.t =
496496+ let make name description arguments unknown =
497497+ { name; description; arguments; unknown }
498498+ in
499499+ Jsont.Object.map ~kind:"Prompt" make
500500+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun p -> p.name)
501501+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun p -> p.description)
502502+ |> Jsont.Object.opt_mem "arguments" (Jsont.list prompt_argument_jsont)
503503+ ~enc:(fun p -> p.arguments)
504504+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun p -> p.unknown)
505505+ |> Jsont.Object.finish
506506+507507+ type role = User | Assistant
508508+509509+ let role_jsont : role Jsont.t =
510510+ Jsont.enum [
511511+ "user", User;
512512+ "assistant", Assistant;
513513+ ]
514514+515515+ type prompt_message = {
516516+ role : role;
517517+ content : Content.block list;
518518+ unknown : Jsont.json;
519519+ }
520520+521521+ let make_prompt_message ~role ~content () =
522522+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
523523+ { role; content; unknown }
524524+525525+ let prompt_message_jsont : prompt_message Jsont.t =
526526+ let make role content unknown = { role; content; unknown } in
527527+ Jsont.Object.map ~kind:"PromptMessage" make
528528+ |> Jsont.Object.mem "role" role_jsont ~enc:(fun m -> m.role)
529529+ |> Jsont.Object.mem "content" (Jsont.list Content.block_jsont)
530530+ ~enc:(fun m -> m.content)
531531+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun m -> m.unknown)
532532+ |> Jsont.Object.finish
533533+534534+ type list_request = {
535535+ cursor : string option;
536536+ unknown : Jsont.json;
537537+ }
538538+539539+ let make_list_request ?cursor () =
540540+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
541541+ { cursor; unknown }
542542+543543+ let list_request_jsont : list_request Jsont.t =
544544+ let make cursor unknown = { cursor; unknown } in
545545+ Jsont.Object.map ~kind:"PromptsListRequest" make
546546+ |> Jsont.Object.opt_mem "cursor" Jsont.string ~enc:(fun r -> r.cursor)
547547+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
548548+ |> Jsont.Object.finish
549549+550550+ type list_result = {
551551+ prompts : prompt list;
552552+ next_cursor : string option;
553553+ unknown : Jsont.json;
554554+ }
555555+556556+ let make_list_result ~prompts ?next_cursor () =
557557+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
558558+ { prompts; next_cursor; unknown }
559559+560560+ let list_result_jsont : list_result Jsont.t =
561561+ let make prompts next_cursor unknown =
562562+ { prompts; next_cursor; unknown }
563563+ in
564564+ Jsont.Object.map ~kind:"PromptsListResult" make
565565+ |> Jsont.Object.mem "prompts" (Jsont.list prompt_jsont)
566566+ ~enc:(fun r -> r.prompts)
567567+ |> Jsont.Object.opt_mem "nextCursor" Jsont.string
568568+ ~enc:(fun r -> r.next_cursor)
569569+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
570570+ |> Jsont.Object.finish
571571+572572+ (* Arguments as object with string keys *)
573573+ let arguments_jsont : (string * string) list Jsont.t =
574574+ let enc_obj args =
575575+ let pairs = List.map (fun (k, v) ->
576576+ ((k, Jsont.Meta.none), Jsont.String (v, Jsont.Meta.none))
577577+ ) args in
578578+ Jsont.Object (pairs, Jsont.Meta.none)
579579+ in
580580+ let dec_obj = function
581581+ | Jsont.Object (members, _) ->
582582+ List.map (fun ((k, _), v) ->
583583+ match v with
584584+ | Jsont.String (s, _) -> (k, s)
585585+ | _ -> Jsont.Error.msgf Jsont.Meta.none
586586+ "Argument values must be strings"
587587+ ) members
588588+ | _ ->
589589+ Jsont.Error.msgf Jsont.Meta.none "Arguments must be an object"
590590+ in
591591+ Jsont.map ~kind:"PromptArguments" ~dec:dec_obj ~enc:enc_obj Jsont.json
592592+593593+ type get_request = {
594594+ name : string;
595595+ arguments : (string * string) list option;
596596+ unknown : Jsont.json;
597597+ }
598598+599599+ let make_get_request ~name ?arguments () =
600600+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
601601+ { name; arguments; unknown }
602602+603603+ let get_request_jsont : get_request Jsont.t =
604604+ let make name arguments unknown = { name; arguments; unknown } in
605605+ Jsont.Object.map ~kind:"PromptsGetRequest" make
606606+ |> Jsont.Object.mem "name" Jsont.string ~enc:(fun r -> r.name)
607607+ |> Jsont.Object.opt_mem "arguments" arguments_jsont ~enc:(fun r -> r.arguments)
608608+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
609609+ |> Jsont.Object.finish
610610+611611+ type get_result = {
612612+ description : string option;
613613+ messages : prompt_message list;
614614+ unknown : Jsont.json;
615615+ }
616616+617617+ let make_get_result ?description ~messages () =
618618+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
619619+ { description; messages; unknown }
620620+621621+ let get_result_jsont : get_result Jsont.t =
622622+ let make description messages unknown =
623623+ { description; messages; unknown }
624624+ in
625625+ Jsont.Object.map ~kind:"PromptsGetResult" make
626626+ |> Jsont.Object.opt_mem "description" Jsont.string ~enc:(fun r -> r.description)
627627+ |> Jsont.Object.mem "messages" (Jsont.list prompt_message_jsont)
628628+ ~enc:(fun r -> r.messages)
629629+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
630630+ |> Jsont.Object.finish
631631+632632+ type list_changed_notification = {
633633+ unknown : Jsont.json;
634634+ }
635635+636636+ let make_list_changed_notification () =
637637+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
638638+ { unknown }
639639+640640+ let list_changed_notification_jsont : list_changed_notification Jsont.t =
641641+ let make unknown = { unknown } in
642642+ Jsont.Object.map ~kind:"PromptsListChangedNotification" make
643643+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
644644+ |> Jsont.Object.finish
645645+646646+ let list_method = "prompts/list"
647647+ let get_method = "prompts/get"
648648+ let list_changed_notification_method = "notifications/prompts/list_changed"
649649+end
650650+651651+(* Logging *)
652652+653653+module Logging = struct
654654+ type level =
655655+ | Debug
656656+ | Info
657657+ | Notice
658658+ | Warning
659659+ | Error
660660+ | Critical
661661+ | Alert
662662+ | Emergency
663663+664664+ let level_jsont : level Jsont.t =
665665+ Jsont.enum [
666666+ "debug", Debug;
667667+ "info", Info;
668668+ "notice", Notice;
669669+ "warning", Warning;
670670+ "error", Error;
671671+ "critical", Critical;
672672+ "alert", Alert;
673673+ "emergency", Emergency;
674674+ ]
675675+676676+ let level_to_string = function
677677+ | Debug -> "debug"
678678+ | Info -> "info"
679679+ | Notice -> "notice"
680680+ | Warning -> "warning"
681681+ | Error -> "error"
682682+ | Critical -> "critical"
683683+ | Alert -> "alert"
684684+ | Emergency -> "emergency"
685685+686686+ type notification = {
687687+ level : level;
688688+ logger : string option;
689689+ data : Jsont.json option;
690690+ unknown : Jsont.json;
691691+ }
692692+693693+ let make_notification ~level ?logger ?data () =
694694+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
695695+ { level; logger; data; unknown }
696696+697697+ let notification_jsont : notification Jsont.t =
698698+ let make level logger data unknown =
699699+ { level; logger; data; unknown }
700700+ in
701701+ Jsont.Object.map ~kind:"LoggingNotification" make
702702+ |> Jsont.Object.mem "level" level_jsont ~enc:(fun n -> n.level)
703703+ |> Jsont.Object.opt_mem "logger" Jsont.string ~enc:(fun n -> n.logger)
704704+ |> Jsont.Object.opt_mem "data" Jsont.json ~enc:(fun n -> n.data)
705705+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
706706+ |> Jsont.Object.finish
707707+708708+ let method_ = "notifications/message"
709709+end
710710+711711+(* Completions *)
712712+713713+module Completions = struct
714714+ type completion_ref = {
715715+ ref_type : string;
716716+ uri : string;
717717+ unknown : Jsont.json;
718718+ }
719719+720720+ let make_completion_ref ~ref_type ~uri () =
721721+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
722722+ { ref_type; uri; unknown }
723723+724724+ let completion_ref_jsont : completion_ref Jsont.t =
725725+ let make ref_type uri unknown = { ref_type; uri; unknown } in
726726+ Jsont.Object.map ~kind:"CompletionRef" make
727727+ |> Jsont.Object.mem "type" Jsont.string ~enc:(fun r -> r.ref_type)
728728+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
729729+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
730730+ |> Jsont.Object.finish
731731+732732+ type request = {
733733+ ref_ : completion_ref;
734734+ argument : string option;
735735+ unknown : Jsont.json;
736736+ }
737737+738738+ let make_request ~ref_ ?argument () =
739739+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
740740+ { ref_; argument; unknown }
741741+742742+ let request_jsont : request Jsont.t =
743743+ let make ref_ argument unknown = { ref_; argument; unknown } in
744744+ Jsont.Object.map ~kind:"CompletionRequest" make
745745+ |> Jsont.Object.mem "ref" completion_ref_jsont ~enc:(fun r -> r.ref_)
746746+ |> Jsont.Object.opt_mem "argument" Jsont.string ~enc:(fun r -> r.argument)
747747+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
748748+ |> Jsont.Object.finish
749749+750750+ type result = {
751751+ completion : string list;
752752+ total : int option;
753753+ has_more : bool option;
754754+ unknown : Jsont.json;
755755+ }
756756+757757+ let make_result ~completion ?total ?has_more () =
758758+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
759759+ { completion; total; has_more; unknown }
760760+761761+ let result_jsont : result Jsont.t =
762762+ let make completion total has_more unknown =
763763+ { completion; total; has_more; unknown }
764764+ in
765765+ Jsont.Object.map ~kind:"CompletionResult" make
766766+ |> Jsont.Object.mem "completion" (Jsont.list Jsont.string)
767767+ ~enc:(fun r -> r.completion)
768768+ |> Jsont.Object.opt_mem "total" Jsont.int ~enc:(fun r -> r.total)
769769+ |> Jsont.Object.opt_mem "hasMore" Jsont.bool ~enc:(fun r -> r.has_more)
770770+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
771771+ |> Jsont.Object.finish
772772+773773+ let method_ = "completion/complete"
774774+end
775775+776776+(* Roots *)
777777+778778+module Roots = struct
779779+ type root = {
780780+ uri : string;
781781+ name : string option;
782782+ unknown : Jsont.json;
783783+ }
784784+785785+ let make_root ~uri ?name () =
786786+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
787787+ { uri; name; unknown }
788788+789789+ let root_jsont : root Jsont.t =
790790+ let make uri name unknown = { uri; name; unknown } in
791791+ Jsont.Object.map ~kind:"Root" make
792792+ |> Jsont.Object.mem "uri" Jsont.string ~enc:(fun r -> r.uri)
793793+ |> Jsont.Object.opt_mem "name" Jsont.string ~enc:(fun r -> r.name)
794794+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
795795+ |> Jsont.Object.finish
796796+797797+ type list_request = {
798798+ unknown : Jsont.json;
799799+ }
800800+801801+ let make_list_request () =
802802+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
803803+ { unknown }
804804+805805+ let list_request_jsont : list_request Jsont.t =
806806+ let make unknown = { unknown } in
807807+ Jsont.Object.map ~kind:"RootsListRequest" make
808808+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
809809+ |> Jsont.Object.finish
810810+811811+ type list_result = {
812812+ roots : root list;
813813+ unknown : Jsont.json;
814814+ }
815815+816816+ let make_list_result ~roots =
817817+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
818818+ { roots; unknown }
819819+820820+ let list_result_jsont : list_result Jsont.t =
821821+ let make roots unknown = { roots; unknown } in
822822+ Jsont.Object.map ~kind:"RootsListResult" make
823823+ |> Jsont.Object.mem "roots" (Jsont.list root_jsont)
824824+ ~enc:(fun r -> r.roots)
825825+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun r -> r.unknown)
826826+ |> Jsont.Object.finish
827827+828828+ type list_changed_notification = {
829829+ unknown : Jsont.json;
830830+ }
831831+832832+ let make_list_changed_notification () =
833833+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
834834+ { unknown }
835835+836836+ let list_changed_notification_jsont : list_changed_notification Jsont.t =
837837+ let make unknown = { unknown } in
838838+ Jsont.Object.map ~kind:"RootsListChangedNotification" make
839839+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
840840+ |> Jsont.Object.finish
841841+842842+ let list_method = "roots/list"
843843+ let list_changed_notification_method = "notifications/roots/list_changed"
844844+end
845845+846846+(* Progress *)
847847+848848+module Progress = struct
849849+ type notification = {
850850+ progress_token : string;
851851+ progress : float;
852852+ total : float option;
853853+ unknown : Jsont.json;
854854+ }
855855+856856+ let make_notification ~progress_token ~progress ?total () =
857857+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
858858+ { progress_token; progress; total; unknown }
859859+860860+ let notification_jsont : notification Jsont.t =
861861+ let make progress_token progress total unknown =
862862+ { progress_token; progress; total; unknown }
863863+ in
864864+ Jsont.Object.map ~kind:"ProgressNotification" make
865865+ |> Jsont.Object.mem "progressToken" Jsont.string
866866+ ~enc:(fun n -> n.progress_token)
867867+ |> Jsont.Object.mem "progress" Jsont.number ~enc:(fun n -> n.progress)
868868+ |> Jsont.Object.opt_mem "total" Jsont.number ~enc:(fun n -> n.total)
869869+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
870870+ |> Jsont.Object.finish
871871+872872+ let method_ = "notifications/progress"
873873+end
874874+875875+(* Cancellation *)
876876+877877+module Cancellation = struct
878878+ type notification = {
879879+ request_id : Jsonrpc.Id.t;
880880+ reason : string option;
881881+ unknown : Jsont.json;
882882+ }
883883+884884+ let make_notification ~request_id ?reason () =
885885+ let unknown = Jsont.Object ([], Jsont.Meta.none) in
886886+ { request_id; reason; unknown }
887887+888888+ let notification_jsont : notification Jsont.t =
889889+ let make request_id reason unknown =
890890+ { request_id; reason; unknown }
891891+ in
892892+ Jsont.Object.map ~kind:"CancellationNotification" make
893893+ |> Jsont.Object.mem "requestId" Jsonrpc.Id.jsont
894894+ ~enc:(fun n -> n.request_id)
895895+ |> Jsont.Object.opt_mem "reason" Jsont.string ~enc:(fun n -> n.reason)
896896+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun n -> n.unknown)
897897+ |> Jsont.Object.finish
898898+899899+ let method_ = "notifications/cancelled"
900900+end
+660
claudeio/lib_mcp/messages.mli
···11+(** MCP Protocol Messages.
22+33+ This module provides all protocol message types for the Model Context Protocol (MCP).
44+ It includes initialization, resources, tools, prompts, logging, and other protocol messages.
55+66+ All types include unknown field preservation for forward compatibility. *)
77+88+(** {1 Protocol Version} *)
99+1010+type protocol_version = string
1111+(** MCP protocol version string (e.g., "2024-11-05") *)
1212+1313+val protocol_version_jsont : protocol_version Jsont.t
1414+1515+(** {1 Initialize Protocol} *)
1616+1717+module Initialize : sig
1818+ (** Initialize request parameters *)
1919+ type request_params = {
2020+ protocol_version : protocol_version;
2121+ capabilities : Capabilities.Client.t;
2222+ client_info : Capabilities.Implementation.t;
2323+ unknown : Jsont.json;
2424+ }
2525+2626+ val make_request_params :
2727+ protocol_version:protocol_version ->
2828+ capabilities:Capabilities.Client.t ->
2929+ client_info:Capabilities.Implementation.t ->
3030+ unit ->
3131+ request_params
3232+3333+ val request_params_jsont : request_params Jsont.t
3434+3535+ (** Initialize result *)
3636+ type result = {
3737+ protocol_version : protocol_version;
3838+ capabilities : Capabilities.Server.t;
3939+ server_info : Capabilities.Implementation.t;
4040+ instructions : string option;
4141+ unknown : Jsont.json;
4242+ }
4343+4444+ val make_result :
4545+ protocol_version:protocol_version ->
4646+ capabilities:Capabilities.Server.t ->
4747+ server_info:Capabilities.Implementation.t ->
4848+ ?instructions:string ->
4949+ unit ->
5050+ result
5151+5252+ val result_jsont : result Jsont.t
5353+5454+ val method_ : string
5555+ (** Method name: "initialize" *)
5656+end
5757+5858+module Initialized : sig
5959+ (** Initialized notification (sent after initialize completes) *)
6060+ type notification = {
6161+ unknown : Jsont.json;
6262+ }
6363+6464+ val make_notification : unit -> notification
6565+ val notification_jsont : notification Jsont.t
6666+6767+ val method_ : string
6868+ (** Method name: "notifications/initialized" *)
6969+end
7070+7171+module Ping : sig
7272+ (** Ping request (keepalive) *)
7373+ type params = {
7474+ unknown : Jsont.json;
7575+ }
7676+7777+ val make_params : unit -> params
7878+ val params_jsont : params Jsont.t
7979+8080+ (** Ping result (empty object) *)
8181+ type result = {
8282+ unknown : Jsont.json;
8383+ }
8484+8585+ val make_result : unit -> result
8686+ val result_jsont : result Jsont.t
8787+8888+ val method_ : string
8989+ (** Method name: "ping" *)
9090+end
9191+9292+(** {1 Resources} *)
9393+9494+module Resources : sig
9595+ (** Resource descriptor *)
9696+ type resource = {
9797+ uri : string;
9898+ name : string;
9999+ description : string option;
100100+ mime_type : string option;
101101+ unknown : Jsont.json;
102102+ }
103103+104104+ val make_resource :
105105+ uri:string ->
106106+ name:string ->
107107+ ?description:string ->
108108+ ?mime_type:string ->
109109+ unit ->
110110+ resource
111111+112112+ val resource_jsont : resource Jsont.t
113113+114114+ (** Resource template (URI template with placeholders) *)
115115+ type resource_template = {
116116+ uri_template : string;
117117+ name : string;
118118+ description : string option;
119119+ mime_type : string option;
120120+ unknown : Jsont.json;
121121+ }
122122+123123+ val make_resource_template :
124124+ uri_template:string ->
125125+ name:string ->
126126+ ?description:string ->
127127+ ?mime_type:string ->
128128+ unit ->
129129+ resource_template
130130+131131+ val resource_template_jsont : resource_template Jsont.t
132132+133133+ (** Resource contents (from read request) *)
134134+ type resource_contents = {
135135+ uri : string;
136136+ mime_type : string option;
137137+ text : string option;
138138+ blob : string option; (** Base64-encoded binary data *)
139139+ unknown : Jsont.json;
140140+ }
141141+142142+ val make_text_contents :
143143+ uri:string ->
144144+ text:string ->
145145+ ?mime_type:string ->
146146+ unit ->
147147+ resource_contents
148148+149149+ val make_blob_contents :
150150+ uri:string ->
151151+ blob:string ->
152152+ mime_type:string ->
153153+ resource_contents
154154+155155+ val resource_contents_jsont : resource_contents Jsont.t
156156+157157+ (** List resources request *)
158158+ type list_request = {
159159+ cursor : string option;
160160+ unknown : Jsont.json;
161161+ }
162162+163163+ val make_list_request : ?cursor:string -> unit -> list_request
164164+ val list_request_jsont : list_request Jsont.t
165165+166166+ (** List resources result *)
167167+ type list_result = {
168168+ resources : resource list;
169169+ next_cursor : string option;
170170+ unknown : Jsont.json;
171171+ }
172172+173173+ val make_list_result :
174174+ resources:resource list ->
175175+ ?next_cursor:string ->
176176+ unit ->
177177+ list_result
178178+179179+ val list_result_jsont : list_result Jsont.t
180180+181181+ (** Read resource request *)
182182+ type read_request = {
183183+ uri : string;
184184+ unknown : Jsont.json;
185185+ }
186186+187187+ val make_read_request : uri:string -> read_request
188188+ val read_request_jsont : read_request Jsont.t
189189+190190+ (** Read resource result *)
191191+ type read_result = {
192192+ contents : resource_contents list;
193193+ unknown : Jsont.json;
194194+ }
195195+196196+ val make_read_result : contents:resource_contents list -> read_result
197197+ val read_result_jsont : read_result Jsont.t
198198+199199+ (** Subscribe to resource updates *)
200200+ type subscribe_request = {
201201+ uri : string;
202202+ unknown : Jsont.json;
203203+ }
204204+205205+ val make_subscribe_request : uri:string -> subscribe_request
206206+ val subscribe_request_jsont : subscribe_request Jsont.t
207207+208208+ (** Unsubscribe from resource updates *)
209209+ type unsubscribe_request = {
210210+ uri : string;
211211+ unknown : Jsont.json;
212212+ }
213213+214214+ val make_unsubscribe_request : uri:string -> unsubscribe_request
215215+ val unsubscribe_request_jsont : unsubscribe_request Jsont.t
216216+217217+ (** Resource updated notification *)
218218+ type updated_notification = {
219219+ uri : string;
220220+ unknown : Jsont.json;
221221+ }
222222+223223+ val make_updated_notification : uri:string -> updated_notification
224224+ val updated_notification_jsont : updated_notification Jsont.t
225225+226226+ (** Resource list changed notification *)
227227+ type list_changed_notification = {
228228+ unknown : Jsont.json;
229229+ }
230230+231231+ val make_list_changed_notification : unit -> list_changed_notification
232232+ val list_changed_notification_jsont : list_changed_notification Jsont.t
233233+234234+ val list_method : string
235235+ (** Method name: "resources/list" *)
236236+237237+ val read_method : string
238238+ (** Method name: "resources/read" *)
239239+240240+ val subscribe_method : string
241241+ (** Method name: "resources/subscribe" *)
242242+243243+ val unsubscribe_method : string
244244+ (** Method name: "resources/unsubscribe" *)
245245+246246+ val updated_notification_method : string
247247+ (** Method name: "notifications/resources/updated" *)
248248+249249+ val list_changed_notification_method : string
250250+ (** Method name: "notifications/resources/list_changed" *)
251251+end
252252+253253+(** {1 Tools} *)
254254+255255+module Tools : sig
256256+ (** Tool descriptor *)
257257+ type tool = {
258258+ name : string;
259259+ description : string option;
260260+ input_schema : Jsont.json; (** JSON Schema for tool inputs *)
261261+ unknown : Jsont.json;
262262+ }
263263+264264+ val make_tool :
265265+ name:string ->
266266+ ?description:string ->
267267+ input_schema:Jsont.json ->
268268+ unit ->
269269+ tool
270270+271271+ val tool_jsont : tool Jsont.t
272272+273273+ (** List tools request *)
274274+ type list_request = {
275275+ cursor : string option;
276276+ unknown : Jsont.json;
277277+ }
278278+279279+ val make_list_request : ?cursor:string -> unit -> list_request
280280+ val list_request_jsont : list_request Jsont.t
281281+282282+ (** List tools result *)
283283+ type list_result = {
284284+ tools : tool list;
285285+ next_cursor : string option;
286286+ unknown : Jsont.json;
287287+ }
288288+289289+ val make_list_result :
290290+ tools:tool list ->
291291+ ?next_cursor:string ->
292292+ unit ->
293293+ list_result
294294+295295+ val list_result_jsont : list_result Jsont.t
296296+297297+ (** Call tool request *)
298298+ type call_request = {
299299+ name : string;
300300+ arguments : Jsont.json option;
301301+ unknown : Jsont.json;
302302+ }
303303+304304+ val make_call_request :
305305+ name:string ->
306306+ ?arguments:Jsont.json ->
307307+ unit ->
308308+ call_request
309309+310310+ val call_request_jsont : call_request Jsont.t
311311+312312+ (** Call tool result *)
313313+ type call_result = {
314314+ content : Content.block list;
315315+ is_error : bool option;
316316+ unknown : Jsont.json;
317317+ }
318318+319319+ val make_call_result :
320320+ content:Content.block list ->
321321+ ?is_error:bool ->
322322+ unit ->
323323+ call_result
324324+325325+ val call_result_jsont : call_result Jsont.t
326326+327327+ (** Tool list changed notification *)
328328+ type list_changed_notification = {
329329+ unknown : Jsont.json;
330330+ }
331331+332332+ val make_list_changed_notification : unit -> list_changed_notification
333333+ val list_changed_notification_jsont : list_changed_notification Jsont.t
334334+335335+ val list_method : string
336336+ (** Method name: "tools/list" *)
337337+338338+ val call_method : string
339339+ (** Method name: "tools/call" *)
340340+341341+ val list_changed_notification_method : string
342342+ (** Method name: "notifications/tools/list_changed" *)
343343+end
344344+345345+(** {1 Prompts} *)
346346+347347+module Prompts : sig
348348+ (** Prompt argument descriptor *)
349349+ type prompt_argument = {
350350+ name : string;
351351+ description : string option;
352352+ required : bool option;
353353+ unknown : Jsont.json;
354354+ }
355355+356356+ val make_prompt_argument :
357357+ name:string ->
358358+ ?description:string ->
359359+ ?required:bool ->
360360+ unit ->
361361+ prompt_argument
362362+363363+ val prompt_argument_jsont : prompt_argument Jsont.t
364364+365365+ (** Prompt descriptor *)
366366+ type prompt = {
367367+ name : string;
368368+ description : string option;
369369+ arguments : prompt_argument list option;
370370+ unknown : Jsont.json;
371371+ }
372372+373373+ val make_prompt :
374374+ name:string ->
375375+ ?description:string ->
376376+ ?arguments:prompt_argument list ->
377377+ unit ->
378378+ prompt
379379+380380+ val prompt_jsont : prompt Jsont.t
381381+382382+ (** Prompt message role *)
383383+ type role = User | Assistant
384384+385385+ val role_jsont : role Jsont.t
386386+387387+ (** Prompt message *)
388388+ type prompt_message = {
389389+ role : role;
390390+ content : Content.block list;
391391+ unknown : Jsont.json;
392392+ }
393393+394394+ val make_prompt_message :
395395+ role:role ->
396396+ content:Content.block list ->
397397+ unit ->
398398+ prompt_message
399399+400400+ val prompt_message_jsont : prompt_message Jsont.t
401401+402402+ (** List prompts request *)
403403+ type list_request = {
404404+ cursor : string option;
405405+ unknown : Jsont.json;
406406+ }
407407+408408+ val make_list_request : ?cursor:string -> unit -> list_request
409409+ val list_request_jsont : list_request Jsont.t
410410+411411+ (** List prompts result *)
412412+ type list_result = {
413413+ prompts : prompt list;
414414+ next_cursor : string option;
415415+ unknown : Jsont.json;
416416+ }
417417+418418+ val make_list_result :
419419+ prompts:prompt list ->
420420+ ?next_cursor:string ->
421421+ unit ->
422422+ list_result
423423+424424+ val list_result_jsont : list_result Jsont.t
425425+426426+ (** Get prompt request *)
427427+ type get_request = {
428428+ name : string;
429429+ arguments : (string * string) list option; (** Key-value pairs *)
430430+ unknown : Jsont.json;
431431+ }
432432+433433+ val make_get_request :
434434+ name:string ->
435435+ ?arguments:(string * string) list ->
436436+ unit ->
437437+ get_request
438438+439439+ val get_request_jsont : get_request Jsont.t
440440+441441+ (** Get prompt result *)
442442+ type get_result = {
443443+ description : string option;
444444+ messages : prompt_message list;
445445+ unknown : Jsont.json;
446446+ }
447447+448448+ val make_get_result :
449449+ ?description:string ->
450450+ messages:prompt_message list ->
451451+ unit ->
452452+ get_result
453453+454454+ val get_result_jsont : get_result Jsont.t
455455+456456+ (** Prompt list changed notification *)
457457+ type list_changed_notification = {
458458+ unknown : Jsont.json;
459459+ }
460460+461461+ val make_list_changed_notification : unit -> list_changed_notification
462462+ val list_changed_notification_jsont : list_changed_notification Jsont.t
463463+464464+ val list_method : string
465465+ (** Method name: "prompts/list" *)
466466+467467+ val get_method : string
468468+ (** Method name: "prompts/get" *)
469469+470470+ val list_changed_notification_method : string
471471+ (** Method name: "notifications/prompts/list_changed" *)
472472+end
473473+474474+(** {1 Logging} *)
475475+476476+module Logging : sig
477477+ (** Log level *)
478478+ type level =
479479+ | Debug
480480+ | Info
481481+ | Notice
482482+ | Warning
483483+ | Error
484484+ | Critical
485485+ | Alert
486486+ | Emergency
487487+488488+ val level_jsont : level Jsont.t
489489+ val level_to_string : level -> string
490490+491491+ (** Logging message notification *)
492492+ type notification = {
493493+ level : level;
494494+ logger : string option;
495495+ data : Jsont.json option;
496496+ unknown : Jsont.json;
497497+ }
498498+499499+ val make_notification :
500500+ level:level ->
501501+ ?logger:string ->
502502+ ?data:Jsont.json ->
503503+ unit ->
504504+ notification
505505+506506+ val notification_jsont : notification Jsont.t
507507+508508+ val method_ : string
509509+ (** Method name: "notifications/message" *)
510510+end
511511+512512+(** {1 Completions} *)
513513+514514+module Completions : sig
515515+ (** Completion reference (argument or resource URI) *)
516516+ type completion_ref = {
517517+ ref_type : string; (** "ref/prompt" or "ref/resource" *)
518518+ uri : string;
519519+ unknown : Jsont.json;
520520+ }
521521+522522+ val make_completion_ref :
523523+ ref_type:string ->
524524+ uri:string ->
525525+ unit ->
526526+ completion_ref
527527+528528+ val completion_ref_jsont : completion_ref Jsont.t
529529+530530+ (** Completion request *)
531531+ type request = {
532532+ ref_ : completion_ref;
533533+ argument : string option;
534534+ unknown : Jsont.json;
535535+ }
536536+537537+ val make_request :
538538+ ref_:completion_ref ->
539539+ ?argument:string ->
540540+ unit ->
541541+ request
542542+543543+ val request_jsont : request Jsont.t
544544+545545+ (** Completion result *)
546546+ type result = {
547547+ completion : string list;
548548+ total : int option;
549549+ has_more : bool option;
550550+ unknown : Jsont.json;
551551+ }
552552+553553+ val make_result :
554554+ completion:string list ->
555555+ ?total:int ->
556556+ ?has_more:bool ->
557557+ unit ->
558558+ result
559559+560560+ val result_jsont : result Jsont.t
561561+562562+ val method_ : string
563563+ (** Method name: "completion/complete" *)
564564+end
565565+566566+(** {1 Roots} *)
567567+568568+module Roots : sig
569569+ (** Root descriptor *)
570570+ type root = {
571571+ uri : string;
572572+ name : string option;
573573+ unknown : Jsont.json;
574574+ }
575575+576576+ val make_root :
577577+ uri:string ->
578578+ ?name:string ->
579579+ unit ->
580580+ root
581581+582582+ val root_jsont : root Jsont.t
583583+584584+ (** List roots request *)
585585+ type list_request = {
586586+ unknown : Jsont.json;
587587+ }
588588+589589+ val make_list_request : unit -> list_request
590590+ val list_request_jsont : list_request Jsont.t
591591+592592+ (** List roots result *)
593593+ type list_result = {
594594+ roots : root list;
595595+ unknown : Jsont.json;
596596+ }
597597+598598+ val make_list_result : roots:root list -> list_result
599599+ val list_result_jsont : list_result Jsont.t
600600+601601+ (** Roots list changed notification *)
602602+ type list_changed_notification = {
603603+ unknown : Jsont.json;
604604+ }
605605+606606+ val make_list_changed_notification : unit -> list_changed_notification
607607+ val list_changed_notification_jsont : list_changed_notification Jsont.t
608608+609609+ val list_method : string
610610+ (** Method name: "roots/list" *)
611611+612612+ val list_changed_notification_method : string
613613+ (** Method name: "notifications/roots/list_changed" *)
614614+end
615615+616616+(** {1 Progress} *)
617617+618618+module Progress : sig
619619+ (** Progress notification *)
620620+ type notification = {
621621+ progress_token : string; (** Unique token identifying the operation *)
622622+ progress : float; (** Progress value (0.0 to 1.0) *)
623623+ total : float option; (** Optional total value *)
624624+ unknown : Jsont.json;
625625+ }
626626+627627+ val make_notification :
628628+ progress_token:string ->
629629+ progress:float ->
630630+ ?total:float ->
631631+ unit ->
632632+ notification
633633+634634+ val notification_jsont : notification Jsont.t
635635+636636+ val method_ : string
637637+ (** Method name: "notifications/progress" *)
638638+end
639639+640640+(** {1 Cancellation} *)
641641+642642+module Cancellation : sig
643643+ (** Cancel request notification *)
644644+ type notification = {
645645+ request_id : Jsonrpc.Id.t;
646646+ reason : string option;
647647+ unknown : Jsont.json;
648648+ }
649649+650650+ val make_notification :
651651+ request_id:Jsonrpc.Id.t ->
652652+ ?reason:string ->
653653+ unit ->
654654+ notification
655655+656656+ val notification_jsont : notification Jsont.t
657657+658658+ val method_ : string
659659+ (** Method name: "notifications/cancelled" *)
660660+end
+371
claudeio/lib_mcp/server_session.ml
···11+(** High-level MCP server session API *)
22+33+(** {1 Types} *)
44+55+type config = {
66+ server_info : Capabilities.Implementation.t;
77+ server_capabilities : Capabilities.Server.t;
88+ instructions : string option;
99+}
1010+1111+type handlers = {
1212+ list_resources : (cursor:string option -> Messages.Resources.list_result) option;
1313+ list_resource_templates : (cursor:string option -> Messages.Resources.list_result) option;
1414+ read_resource : (uri:string -> Messages.Resources.read_result) option;
1515+ subscribe_resource : (uri:string -> unit) option;
1616+ unsubscribe_resource : (uri:string -> unit) option;
1717+ list_tools : (cursor:string option -> Messages.Tools.list_result) option;
1818+ call_tool : (name:string -> arguments:Jsont.json option -> Messages.Tools.call_result) option;
1919+ list_prompts : (cursor:string option -> Messages.Prompts.list_result) option;
2020+ get_prompt : (name:string -> arguments:(string * string) list option -> Messages.Prompts.get_result) option;
2121+ complete : (ref_:Messages.Completions.completion_ref -> argument:string -> Messages.Completions.result) option;
2222+ ping : (unit -> unit) option;
2323+}
2424+2525+type t = {
2626+ session : Session.t;
2727+ config : config;
2828+ handlers : handlers;
2929+ mutable client_capabilities : Capabilities.Client.t option;
3030+ mutable client_info : Capabilities.Implementation.t option;
3131+ mutable protocol_version : string option;
3232+ mutable initialized : bool;
3333+}
3434+3535+(** {1 Helper Functions} *)
3636+3737+let encode_json jsont value =
3838+ match Jsont.Json.encode jsont value with
3939+ | Ok json -> json
4040+ | Error e -> failwith ("Failed to encode JSON: " ^ e)
4141+4242+let decode_json jsont json =
4343+ match Jsont.Json.decode jsont json with
4444+ | Ok value -> value
4545+ | Error e -> failwith ("Failed to decode JSON: " ^ e)
4646+4747+let method_not_found method_ =
4848+ let error = Jsonrpc.Error_data.make
4949+ ~code:Method_not_found
5050+ ~message:(Printf.sprintf "Method not found: %s" method_)
5151+ ()
5252+ in
5353+ raise (Session.Remote_error error)
5454+5555+let invalid_params method_ msg =
5656+ let error = Jsonrpc.Error_data.make
5757+ ~code:Invalid_params
5858+ ~message:(Printf.sprintf "Invalid params for %s: %s" method_ msg)
5959+ ()
6060+ in
6161+ raise (Session.Remote_error error)
6262+6363+(** {1 Request Handler} *)
6464+6565+let handle_request t ~method_ ~params =
6666+ (* Ensure initialization has completed for non-init requests *)
6767+ if method_ <> Messages.Initialize.method_ && not t.initialized then begin
6868+ let error = Jsonrpc.Error_data.make
6969+ ~code:Internal_error
7070+ ~message:"Server not initialized"
7171+ ()
7272+ in
7373+ raise (Session.Remote_error error)
7474+ end;
7575+7676+ (* Route to appropriate handler *)
7777+ match method_ with
7878+ | m when m = Messages.Initialize.method_ ->
7979+ (* Handle initialization *)
8080+ let req_params = match params with
8181+ | Some p -> decode_json Messages.Initialize.request_params_jsont p
8282+ | None -> invalid_params method_ "missing params"
8383+ in
8484+8585+ (* Store client info *)
8686+ t.client_capabilities <- Some req_params.capabilities;
8787+ t.client_info <- Some req_params.client_info;
8888+ t.protocol_version <- Some req_params.protocol_version;
8989+9090+ (* Build response *)
9191+ let result = Messages.Initialize.make_result
9292+ ~protocol_version:req_params.protocol_version
9393+ ~capabilities:t.config.server_capabilities
9494+ ~server_info:t.config.server_info
9595+ ?instructions:t.config.instructions
9696+ ()
9797+ in
9898+ encode_json Messages.Initialize.result_jsont result
9999+100100+ | m when m = Messages.Ping.method_ ->
101101+ let handler = t.handlers.ping in
102102+ (match handler with
103103+ | None -> method_not_found method_
104104+ | Some h ->
105105+ h ();
106106+ let result = Messages.Ping.make_result () in
107107+ encode_json Messages.Ping.result_jsont result)
108108+109109+ | m when m = Messages.Resources.list_method ->
110110+ let handler = t.handlers.list_resources in
111111+ (match handler with
112112+ | None -> method_not_found method_
113113+ | Some h ->
114114+ let req = match params with
115115+ | Some p -> decode_json Messages.Resources.list_request_jsont p
116116+ | None -> Messages.Resources.make_list_request ()
117117+ in
118118+ let result = h ~cursor:req.cursor in
119119+ encode_json Messages.Resources.list_result_jsont result)
120120+121121+ | m when m = Messages.Resources.read_method ->
122122+ let handler = t.handlers.read_resource in
123123+ (match handler with
124124+ | None -> method_not_found method_
125125+ | Some h ->
126126+ let req = match params with
127127+ | Some p -> decode_json Messages.Resources.read_request_jsont p
128128+ | None -> invalid_params method_ "missing params"
129129+ in
130130+ let result = h ~uri:req.uri in
131131+ encode_json Messages.Resources.read_result_jsont result)
132132+133133+ | m when m = Messages.Resources.subscribe_method ->
134134+ let handler = t.handlers.subscribe_resource in
135135+ (match handler with
136136+ | None -> method_not_found method_
137137+ | Some h ->
138138+ let req = match params with
139139+ | Some p -> decode_json Messages.Resources.subscribe_request_jsont p
140140+ | None -> invalid_params method_ "missing params"
141141+ in
142142+ h ~uri:req.uri;
143143+ Jsont.Object ([], Jsont.Meta.none)) (* Empty response *)
144144+145145+ | m when m = Messages.Resources.unsubscribe_method ->
146146+ let handler = t.handlers.unsubscribe_resource in
147147+ (match handler with
148148+ | None -> method_not_found method_
149149+ | Some h ->
150150+ let req = match params with
151151+ | Some p -> decode_json Messages.Resources.unsubscribe_request_jsont p
152152+ | None -> invalid_params method_ "missing params"
153153+ in
154154+ h ~uri:req.uri;
155155+ Jsont.Object ([], Jsont.Meta.none)) (* Empty response *)
156156+157157+ | m when m = Messages.Tools.list_method ->
158158+ let handler = t.handlers.list_tools in
159159+ (match handler with
160160+ | None -> method_not_found method_
161161+ | Some h ->
162162+ let req = match params with
163163+ | Some p -> decode_json Messages.Tools.list_request_jsont p
164164+ | None -> Messages.Tools.make_list_request ()
165165+ in
166166+ let result = h ~cursor:req.cursor in
167167+ encode_json Messages.Tools.list_result_jsont result)
168168+169169+ | m when m = Messages.Tools.call_method ->
170170+ let handler = t.handlers.call_tool in
171171+ (match handler with
172172+ | None -> method_not_found method_
173173+ | Some h ->
174174+ let req = match params with
175175+ | Some p -> decode_json Messages.Tools.call_request_jsont p
176176+ | None -> invalid_params method_ "missing params"
177177+ in
178178+ let result = h ~name:req.name ~arguments:req.arguments in
179179+ encode_json Messages.Tools.call_result_jsont result)
180180+181181+ | m when m = Messages.Prompts.list_method ->
182182+ let handler = t.handlers.list_prompts in
183183+ (match handler with
184184+ | None -> method_not_found method_
185185+ | Some h ->
186186+ let req = match params with
187187+ | Some p -> decode_json Messages.Prompts.list_request_jsont p
188188+ | None -> Messages.Prompts.make_list_request ()
189189+ in
190190+ let result = h ~cursor:req.cursor in
191191+ encode_json Messages.Prompts.list_result_jsont result)
192192+193193+ | m when m = Messages.Prompts.get_method ->
194194+ let handler = t.handlers.get_prompt in
195195+ (match handler with
196196+ | None -> method_not_found method_
197197+ | Some h ->
198198+ let req = match params with
199199+ | Some p -> decode_json Messages.Prompts.get_request_jsont p
200200+ | None -> invalid_params method_ "missing params"
201201+ in
202202+ let result = h ~name:req.name ~arguments:req.arguments in
203203+ encode_json Messages.Prompts.get_result_jsont result)
204204+205205+ | m when m = Messages.Completions.method_ ->
206206+ let handler = t.handlers.complete in
207207+ (match handler with
208208+ | None -> method_not_found method_
209209+ | Some h ->
210210+ let req = match params with
211211+ | Some p -> decode_json Messages.Completions.request_jsont p
212212+ | None -> invalid_params method_ "missing params"
213213+ in
214214+ let argument = match req.argument with
215215+ | Some a -> a
216216+ | None -> ""
217217+ in
218218+ let result = h ~ref_:req.ref_ ~argument in
219219+ encode_json Messages.Completions.result_jsont result)
220220+221221+ | _ ->
222222+ method_not_found method_
223223+224224+(** {1 Notification Handler} *)
225225+226226+let handle_notification t ~method_ ~params =
227227+ match method_ with
228228+ | m when m = Messages.Initialized.method_ ->
229229+ (* Client has confirmed initialization *)
230230+ let _notif = match params with
231231+ | Some p -> decode_json Messages.Initialized.notification_jsont p
232232+ | None -> Messages.Initialized.make_notification ()
233233+ in
234234+ t.initialized <- true
235235+236236+ | _ ->
237237+ (* Ignore unknown notifications *)
238238+ ()
239239+240240+(** {1 Public API} *)
241241+242242+let create ~sw ~transport ?timeout ?clock config handlers =
243243+ (* Create session with handlers *)
244244+ let t_ref = ref None in
245245+246246+ let request_handler ~method_ ~params =
247247+ match !t_ref with
248248+ | None -> failwith "Server session not initialized"
249249+ | Some t -> handle_request t ~method_ ~params
250250+ in
251251+252252+ let notification_handler ~method_ ~params =
253253+ match !t_ref with
254254+ | None -> ()
255255+ | Some t -> handle_notification t ~method_ ~params
256256+ in
257257+258258+ let session_config = {
259259+ Session.transport;
260260+ request_handler;
261261+ notification_handler;
262262+ timeout;
263263+ clock;
264264+ } in
265265+266266+ let session = Session.create ~sw session_config in
267267+268268+ let t = {
269269+ session;
270270+ config;
271271+ handlers;
272272+ client_capabilities = None;
273273+ client_info = None;
274274+ protocol_version = None;
275275+ initialized = false;
276276+ } in
277277+278278+ t_ref := Some t;
279279+280280+ t
281281+282282+let client_capabilities t =
283283+ match t.client_capabilities with
284284+ | Some c -> c
285285+ | None -> invalid_arg "Server_session.client_capabilities: not initialized"
286286+287287+let client_info t =
288288+ match t.client_info with
289289+ | Some i -> i
290290+ | None -> invalid_arg "Server_session.client_info: not initialized"
291291+292292+let protocol_version t =
293293+ match t.protocol_version with
294294+ | Some v -> v
295295+ | None -> invalid_arg "Server_session.protocol_version: not initialized"
296296+297297+(** {1 Sending Notifications} *)
298298+299299+let send_notification t method_ params_jsont params =
300300+ let params_json = encode_json params_jsont params in
301301+ Session.send_notification t.session ~method_ ~params:params_json ()
302302+303303+let send_resource_updated t ~uri =
304304+ let notif = Messages.Resources.make_updated_notification ~uri in
305305+ send_notification t
306306+ Messages.Resources.updated_notification_method
307307+ Messages.Resources.updated_notification_jsont
308308+ notif
309309+310310+let send_resource_list_changed t =
311311+ let notif = Messages.Resources.make_list_changed_notification () in
312312+ send_notification t
313313+ Messages.Resources.list_changed_notification_method
314314+ Messages.Resources.list_changed_notification_jsont
315315+ notif
316316+317317+let send_tool_list_changed t =
318318+ let notif = Messages.Tools.make_list_changed_notification () in
319319+ send_notification t
320320+ Messages.Tools.list_changed_notification_method
321321+ Messages.Tools.list_changed_notification_jsont
322322+ notif
323323+324324+let send_prompt_list_changed t =
325325+ let notif = Messages.Prompts.make_list_changed_notification () in
326326+ send_notification t
327327+ Messages.Prompts.list_changed_notification_method
328328+ Messages.Prompts.list_changed_notification_jsont
329329+ notif
330330+331331+let send_roots_list_changed t =
332332+ let notif = Messages.Roots.make_list_changed_notification () in
333333+ send_notification t
334334+ Messages.Roots.list_changed_notification_method
335335+ Messages.Roots.list_changed_notification_jsont
336336+ notif
337337+338338+let send_log_message t ~level ?logger ~data () =
339339+ let notif = Messages.Logging.make_notification ~level ?logger ~data () in
340340+ send_notification t
341341+ Messages.Logging.method_
342342+ Messages.Logging.notification_jsont
343343+ notif
344344+345345+let send_progress t ~progress_token ~progress ?total () =
346346+ let notif = Messages.Progress.make_notification ~progress_token ~progress ?total () in
347347+ send_notification t
348348+ Messages.Progress.method_
349349+ Messages.Progress.notification_jsont
350350+ notif
351351+352352+(** {1 Requesting from Client} *)
353353+354354+let request_roots_list t =
355355+ match client_capabilities t with
356356+ | { roots = None; _ } -> None
357357+ | { roots = Some _; _ } ->
358358+ let req = Messages.Roots.make_list_request () in
359359+ let params = encode_json Messages.Roots.list_request_jsont req in
360360+ let result_json = Session.send_request t.session
361361+ ~method_:Messages.Roots.list_method
362362+ ~params
363363+ ()
364364+ in
365365+ let result = decode_json Messages.Roots.list_result_jsont result_json in
366366+ Some result
367367+368368+(** {1 Session Management} *)
369369+370370+let close t =
371371+ Session.close t.session
+208
claudeio/lib_mcp/server_session.mli
···11+(** High-level MCP server session API.
22+33+ This module provides a convenient server-side API for hosting MCP servers.
44+ It handles the initialization handshake, routes incoming requests to handlers,
55+ and provides helpers for sending notifications to clients.
66+77+ {1 Example Usage}
88+99+ {[
1010+ let config = {
1111+ server_info = Capabilities.Implementation.make
1212+ ~name:"my-server"
1313+ ~version:"1.0.0";
1414+ server_capabilities = Capabilities.Server.make
1515+ ~tools:(Some (Capabilities.Tools.make ()))
1616+ ();
1717+ instructions = Some "This is my MCP server";
1818+ } in
1919+2020+ let handlers = {
2121+ list_tools = Some (fun ~cursor ->
2222+ Messages.Tools.make_list_result
2323+ ~tools:[
2424+ Messages.Tools.make_tool
2525+ ~name:"example"
2626+ ~description:"An example tool"
2727+ ~input_schema:(`Object [])
2828+ ();
2929+ ]
3030+ ()
3131+ );
3232+ call_tool = Some (fun ~name ~arguments ->
3333+ Messages.Tools.make_call_result
3434+ ~content:[Content.text "Tool result"]
3535+ ()
3636+ );
3737+ (* ... other handlers ... *)
3838+ list_resources = None;
3939+ list_resource_templates = None;
4040+ read_resource = None;
4141+ subscribe_resource = None;
4242+ unsubscribe_resource = None;
4343+ list_prompts = None;
4444+ get_prompt = None;
4545+ complete = None;
4646+ ping = None;
4747+ } in
4848+4949+ Eio.Switch.run @@ fun sw ->
5050+ let server = Server_session.create
5151+ ~sw
5252+ ~transport
5353+ config
5454+ handlers
5555+ in
5656+ (* Server is now running and handling requests *)
5757+ (* Send notifications as needed *)
5858+ Server_session.send_tool_list_changed server
5959+ ]} *)
6060+6161+(** {1 Types} *)
6262+6363+type t
6464+(** Server session handle *)
6565+6666+(** {1 Configuration} *)
6767+6868+type config = {
6969+ server_info : Capabilities.Implementation.t;
7070+ (** Server implementation information (name, version) *)
7171+ server_capabilities : Capabilities.Server.t;
7272+ (** Server capabilities to advertise to client *)
7373+ instructions : string option;
7474+ (** Optional instructions for using the server *)
7575+}
7676+(** Server configuration *)
7777+7878+(** {1 Request Handlers} *)
7979+8080+type handlers = {
8181+ (* Resources *)
8282+ list_resources : (cursor:string option -> Messages.Resources.list_result) option;
8383+ (** Handler for resources/list requests *)
8484+8585+ list_resource_templates : (cursor:string option -> Messages.Resources.list_result) option;
8686+ (** Handler for resources/templates/list requests *)
8787+8888+ read_resource : (uri:string -> Messages.Resources.read_result) option;
8989+ (** Handler for resources/read requests *)
9090+9191+ subscribe_resource : (uri:string -> unit) option;
9292+ (** Handler for resources/subscribe requests *)
9393+9494+ unsubscribe_resource : (uri:string -> unit) option;
9595+ (** Handler for resources/unsubscribe requests *)
9696+9797+ (* Tools *)
9898+ list_tools : (cursor:string option -> Messages.Tools.list_result) option;
9999+ (** Handler for tools/list requests *)
100100+101101+ call_tool : (name:string -> arguments:Jsont.json option -> Messages.Tools.call_result) option;
102102+ (** Handler for tools/call requests *)
103103+104104+ (* Prompts *)
105105+ list_prompts : (cursor:string option -> Messages.Prompts.list_result) option;
106106+ (** Handler for prompts/list requests *)
107107+108108+ get_prompt : (name:string -> arguments:(string * string) list option -> Messages.Prompts.get_result) option;
109109+ (** Handler for prompts/get requests *)
110110+111111+ (* Completions *)
112112+ complete : (ref_:Messages.Completions.completion_ref -> argument:string -> Messages.Completions.result) option;
113113+ (** Handler for completion/complete requests *)
114114+115115+ (* Ping *)
116116+ ping : (unit -> unit) option;
117117+ (** Handler for ping requests *)
118118+}
119119+(** Request handler callbacks.
120120+ Set to [None] to indicate the method is not supported.
121121+ If a request is received for an unsupported method, a METHOD_NOT_FOUND error is returned. *)
122122+123123+(** {1 Server Creation} *)
124124+125125+val create :
126126+ sw:Eio.Switch.t ->
127127+ transport:Transport.t ->
128128+ ?timeout:float ->
129129+ ?clock:Session.clock ->
130130+ config ->
131131+ handlers ->
132132+ t
133133+(** Create and initialize a server session.
134134+135135+ This function:
136136+ 1. Creates an underlying Session
137137+ 2. Waits for the Initialize request from the client
138138+ 3. Returns the Initialize response with server capabilities
139139+ 4. Waits for the Initialized notification
140140+ 5. Returns a ready-to-use server session
141141+142142+ The server will then handle incoming requests by routing them to the provided handlers.
143143+144144+ @param sw Switch for the session background fibers
145145+ @param transport Transport layer for communication
146146+ @param timeout Optional request timeout in seconds
147147+ @param clock Optional clock for timeout handling (required if timeout is set)
148148+ @raise Invalid_argument if initialization fails or times out *)
149149+150150+(** {1 Client Information} *)
151151+152152+val client_capabilities : t -> Capabilities.Client.t
153153+(** Get the client's advertised capabilities *)
154154+155155+val client_info : t -> Capabilities.Implementation.t
156156+(** Get the client's implementation information *)
157157+158158+val protocol_version : t -> string
159159+(** Get the negotiated protocol version *)
160160+161161+(** {1 Sending Notifications} *)
162162+163163+val send_resource_updated : t -> uri:string -> unit
164164+(** Send a notification that a resource has been updated.
165165+ Only works if client supports resource subscriptions.
166166+ @param uri The URI of the updated resource *)
167167+168168+val send_resource_list_changed : t -> unit
169169+(** Send a notification that the resource list has changed.
170170+ Only works if client supports resource list_changed capability. *)
171171+172172+val send_tool_list_changed : t -> unit
173173+(** Send a notification that the tool list has changed.
174174+ Only works if server advertised tools capability. *)
175175+176176+val send_prompt_list_changed : t -> unit
177177+(** Send a notification that the prompt list has changed.
178178+ Only works if server advertised prompts capability. *)
179179+180180+val send_roots_list_changed : t -> unit
181181+(** Send a notification that the roots list has changed.
182182+ Only works if client supports roots capability. *)
183183+184184+val send_log_message : t -> level:Messages.Logging.level -> ?logger:string -> data:Jsont.json -> unit -> unit
185185+(** Send a log message notification.
186186+ Only works if server advertised logging capability.
187187+ @param level Log level
188188+ @param logger Optional logger name
189189+ @param data Log message data (any JSON value) *)
190190+191191+val send_progress : t -> progress_token:string -> progress:float -> ?total:float -> unit -> unit
192192+(** Send a progress notification.
193193+ @param progress_token Unique token identifying the operation
194194+ @param progress Progress value (0.0 to 1.0)
195195+ @param total Optional total value *)
196196+197197+(** {1 Requesting from Client} *)
198198+199199+val request_roots_list : t -> Messages.Roots.list_result option
200200+(** Request the list of roots from the client.
201201+ Returns [None] if the client doesn't support the roots capability.
202202+ @raise Session.Timeout if the request times out
203203+ @raise Session.Remote_error if the client returns an error *)
204204+205205+(** {1 Session Management} *)
206206+207207+val close : t -> unit
208208+(** Close the server session and underlying transport *)
+254
claudeio/lib_mcp/session.ml
···11+(** Bidirectional JSON-RPC session management with request/response correlation *)
22+33+(** {1 Handlers} *)
44+55+type request_handler =
66+ method_:string ->
77+ params:Jsont.json option ->
88+ Jsont.json
99+1010+type notification_handler =
1111+ method_:string ->
1212+ params:Jsont.json option ->
1313+ unit
1414+1515+(** {1 Configuration} *)
1616+1717+type clock = C : _ Eio.Time.clock -> clock
1818+1919+type config = {
2020+ transport : Transport.t;
2121+ request_handler : request_handler;
2222+ notification_handler : notification_handler;
2323+ timeout : float option;
2424+ clock : clock option;
2525+ (** Clock for timeout handling. Required if timeout is set. *)
2626+}
2727+2828+(** {1 Exceptions} *)
2929+3030+exception Timeout of string
3131+exception Session_closed
3232+exception Unknown_response of Jsonrpc.Id.t
3333+exception Remote_error of Jsonrpc.Error_data.t
3434+3535+(** {1 Internal Types} *)
3636+3737+type response_result =
3838+ | Success of Jsont.json
3939+ | Error of exn
4040+4141+type pending_request = {
4242+ id : Jsonrpc.Id.t;
4343+ resolver : response_result Eio.Promise.u;
4444+ mutable cancelled : bool; (* Flag to indicate request was completed *)
4545+}
4646+4747+type t = {
4848+ transport : Transport.t;
4949+ mutable next_id : int;
5050+ pending : (Jsonrpc.Id.t, pending_request) Hashtbl.t;
5151+ request_handler : request_handler;
5252+ notification_handler : notification_handler;
5353+ timeout : float option;
5454+ clock : clock option;
5555+ sw : Eio.Switch.t;
5656+ mutable closed : bool;
5757+}
5858+5959+(** {1 Helper Functions} *)
6060+6161+let encode_message msg =
6262+ match Jsont.Json.encode Jsonrpc.Message.jsont msg with
6363+ | Ok json -> json
6464+ | Error e -> failwith ("Failed to encode message: " ^ e)
6565+6666+let send_json t json =
6767+ if t.closed then raise Session_closed;
6868+ Transport.send t.transport json
6969+7070+(** Handle an incoming request by calling the user's handler and sending response *)
7171+let handle_request t req =
7272+ let open Jsonrpc in
7373+ let id = match req.Request.id with
7474+ | Some id -> id
7575+ | None ->
7676+ (* This is a notification, not a request - no response needed *)
7777+ t.notification_handler
7878+ ~method_:req.Request.method_
7979+ ~params:req.Request.params;
8080+ raise Exit (* Exit this handler without sending response *)
8181+ in
8282+8383+ try
8484+ (* Call user's request handler *)
8585+ let result = t.request_handler
8686+ ~method_:req.Request.method_
8787+ ~params:req.Request.params
8888+ in
8989+ (* Send success response *)
9090+ let response = Response.make_result ~id ~result in
9191+ let msg = Message.Response response in
9292+ let json = encode_message msg in
9393+ send_json t json
9494+ with
9595+ | Exit -> () (* Notification, no response *)
9696+ | exn ->
9797+ (* Convert exception to error response *)
9898+ let error = Error_data.make
9999+ ~code:Internal_error
100100+ ~message:(Printexc.to_string exn)
101101+ ()
102102+ in
103103+ let response = Response.make_error ~id ~error in
104104+ let msg = Message.Response response in
105105+ let json = encode_message msg in
106106+ send_json t json
107107+108108+(** Resolve a pending request with a response *)
109109+let resolve_response t resp =
110110+ let open Jsonrpc in
111111+ let id = resp.Response.id in
112112+ match Hashtbl.find_opt t.pending id with
113113+ | None ->
114114+ (* Received response for unknown request ID *)
115115+ raise (Unknown_response id)
116116+ | Some pending ->
117117+ Hashtbl.remove t.pending id;
118118+ (* Mark as cancelled so timeout doesn't fire *)
119119+ pending.cancelled <- true;
120120+ (* Resolve the promise with result or error *)
121121+ (match resp.Response.value with
122122+ | Ok result ->
123123+ Eio.Promise.resolve pending.resolver (Success result)
124124+ | Stdlib.Result.Error error ->
125125+ Eio.Promise.resolve pending.resolver (Error (Remote_error error)))
126126+127127+(** Background receive loop - reads messages and routes them *)
128128+let rec receive_loop t =
129129+ if t.closed then () else
130130+ match Transport.receive t.transport with
131131+ | None ->
132132+ (* Transport closed *)
133133+ t.closed <- true;
134134+ (* Cancel all pending requests *)
135135+ Hashtbl.iter (fun _ pending ->
136136+ Eio.Promise.resolve pending.resolver (Error Session_closed)
137137+ ) t.pending;
138138+ Hashtbl.clear t.pending
139139+ | Some json ->
140140+ (try
141141+ let msg = Jsonrpc.Message.classify json in
142142+ match msg with
143143+ | Request req ->
144144+ (* Handle request in new fibre so it doesn't block receive loop *)
145145+ Eio.Fiber.fork_promise ~sw:t.sw (fun () ->
146146+ handle_request t req
147147+ ) |> ignore
148148+ | Response resp ->
149149+ (* Resolve pending promise *)
150150+ resolve_response t resp
151151+ with exn ->
152152+ (* Log error but continue receive loop *)
153153+ Printf.eprintf "Error in receive loop: %s\n%!" (Printexc.to_string exn)
154154+ );
155155+ receive_loop t
156156+157157+(** {1 Public API} *)
158158+159159+let create ~sw (config : config) : t =
160160+ (* Validate that clock is provided if timeout is set *)
161161+ (match config.timeout with
162162+ | Some _ when config.clock = None ->
163163+ invalid_arg "Session.create: clock must be provided when timeout is set"
164164+ | _ -> ());
165165+166166+ let t = {
167167+ transport = config.transport;
168168+ next_id = 1;
169169+ pending = Hashtbl.create 16;
170170+ request_handler = config.request_handler;
171171+ notification_handler = config.notification_handler;
172172+ timeout = config.timeout;
173173+ clock = config.clock;
174174+ sw;
175175+ closed = false;
176176+ } in
177177+178178+ (* Start background receive loop *)
179179+ Eio.Fiber.fork ~sw (fun () -> receive_loop t);
180180+181181+ t
182182+183183+let send_request t ~method_ ?params () =
184184+ if t.closed then raise Session_closed;
185185+186186+ (* Generate unique request ID *)
187187+ let id = `Number (float_of_int t.next_id) in
188188+ t.next_id <- t.next_id + 1;
189189+190190+ (* Create promise for response *)
191191+ let promise, resolver = Eio.Promise.create () in
192192+193193+ (* Register pending request *)
194194+ let pending = {
195195+ id;
196196+ resolver;
197197+ cancelled = false;
198198+ } in
199199+ Hashtbl.add t.pending id pending;
200200+201201+ (* Setup timeout if configured *)
202202+ (match t.timeout, t.clock with
203203+ | None, _ | _, None -> ()
204204+ | Some timeout_sec, Some (C clock) ->
205205+ (* Start timeout fiber *)
206206+ Eio.Fiber.fork ~sw:t.sw (fun () ->
207207+ Eio.Time.sleep clock timeout_sec;
208208+ (* Timeout expired - check if request is still pending and not cancelled *)
209209+ if not pending.cancelled then begin
210210+ match Hashtbl.find_opt t.pending id with
211211+ | Some _ ->
212212+ Hashtbl.remove t.pending id;
213213+ let msg = Printf.sprintf "Request timeout after %.1fs: %s" timeout_sec method_ in
214214+ Eio.Promise.resolve pending.resolver (Error (Timeout msg))
215215+ | None ->
216216+ (* Request already completed, nothing to do *)
217217+ ()
218218+ end
219219+ )
220220+ );
221221+222222+ (* Send request *)
223223+ let req = Jsonrpc.Request.make ~method_ ?params ~id () in
224224+ let msg = Jsonrpc.Message.Request req in
225225+ let json = encode_message msg in
226226+ send_json t json;
227227+228228+ (* Wait for response *)
229229+ match Eio.Promise.await promise with
230230+ | Success result -> result
231231+ | Error exn -> raise exn
232232+233233+let send_notification t ~method_ ?params () =
234234+ if t.closed then raise Session_closed;
235235+236236+ (* Create notification (request with no ID) *)
237237+ let req = Jsonrpc.Request.make ~method_ ?params () in
238238+ let msg = Jsonrpc.Message.Request req in
239239+ let json = encode_message msg in
240240+ send_json t json
241241+242242+let close t =
243243+ if not t.closed then begin
244244+ t.closed <- true;
245245+ (* Cancel all pending requests *)
246246+ Hashtbl.iter (fun _ pending ->
247247+ Eio.Promise.resolve pending.resolver (Error Session_closed)
248248+ ) t.pending;
249249+ Hashtbl.clear t.pending;
250250+ (* Close transport *)
251251+ Transport.close t.transport
252252+ end
253253+254254+let is_closed t = t.closed
+140
claudeio/lib_mcp/session.mli
···11+(** Bidirectional JSON-RPC session management with request/response correlation.
22+33+ This module provides a high-level session abstraction over a transport layer,
44+ handling request ID generation, response correlation via promises, and
55+ bidirectional message routing using Eio structured concurrency.
66+77+ {1 Architecture}
88+99+ Sessions run a background receive loop in an Eio fiber that continuously
1010+ reads from the transport and routes messages:
1111+ - Incoming requests → dispatched to request_handler in new fiber
1212+ - Incoming responses → resolve pending promises
1313+ - Incoming notifications → dispatched to notification_handler
1414+1515+ Outgoing messages (requests and notifications) are sent directly on the
1616+ transport. Requests return promises that are resolved when the corresponding
1717+ response arrives.
1818+1919+ {1 Example Usage}
2020+2121+ {[
2222+ Eio_main.run @@ fun env ->
2323+ let config = {
2424+ transport;
2525+ request_handler = (fun ~method_ ~params ->
2626+ (* Handle incoming requests *)
2727+ match method_ with
2828+ | "ping" -> `String "pong"
2929+ | _ -> failwith "Unknown method"
3030+ );
3131+ notification_handler = (fun ~method_ ~params ->
3232+ (* Handle incoming notifications *)
3333+ Printf.printf "Notification: %s\n" method_
3434+ );
3535+ timeout = Some 30.0; (* 30 second timeout *)
3636+ clock = Some (C (Eio.Stdenv.clock env));
3737+ }
3838+ in
3939+4040+ Eio.Switch.run @@ fun sw ->
4141+ let session = Session.create ~sw config in
4242+4343+ (* Send a request and wait for response *)
4444+ let response = Session.send_request session
4545+ ~method_:"initialize"
4646+ ~params:(`Object [("version", `String "1.0")])
4747+ ()
4848+ in
4949+5050+ (* Send a notification (no response expected) *)
5151+ Session.send_notification session
5252+ ~method_:"progress"
5353+ ~params:(`Object [("percent", `Number 50.0)])
5454+ ()
5555+ ]} *)
5656+5757+(** {1 Handlers} *)
5858+5959+type request_handler =
6060+ method_:string ->
6161+ params:Jsont.json option ->
6262+ Jsont.json
6363+(** Handler for incoming requests. Should return the result value.
6464+ May raise exceptions which will be converted to JSON-RPC errors. *)
6565+6666+type notification_handler =
6767+ method_:string ->
6868+ params:Jsont.json option ->
6969+ unit
7070+(** Handler for incoming notifications. No response is expected. *)
7171+7272+(** {1 Configuration} *)
7373+7474+type clock = C : _ Eio.Time.clock -> clock
7575+(** Wrapper for existential clock type *)
7676+7777+type config = {
7878+ transport : Transport.t;
7979+ (** Transport layer for sending/receiving JSON messages *)
8080+ request_handler : request_handler;
8181+ (** Handler for incoming requests *)
8282+ notification_handler : notification_handler;
8383+ (** Handler for incoming notifications *)
8484+ timeout : float option;
8585+ (** Request timeout in seconds. [None] means no timeout. *)
8686+ clock : clock option;
8787+ (** Clock for timeout handling. Required if [timeout] is set. *)
8888+}
8989+(** Session configuration *)
9090+9191+(** {1 Session Management} *)
9292+9393+type t
9494+(** Session handle *)
9595+9696+exception Timeout of string
9797+(** Raised when a request times out *)
9898+9999+exception Session_closed
100100+(** Raised when attempting to use a closed session *)
101101+102102+exception Unknown_response of Jsonrpc.Id.t
103103+(** Raised when receiving a response for an unknown request ID *)
104104+105105+exception Remote_error of Jsonrpc.Error_data.t
106106+(** Raised when the remote side returns an error response *)
107107+108108+val create :
109109+ sw:Eio.Switch.t ->
110110+ config ->
111111+ t
112112+(** Create and start a session with a background receive loop.
113113+ The receive loop runs in a background fiber attached to [sw]. *)
114114+115115+val send_request :
116116+ t ->
117117+ method_:string ->
118118+ ?params:Jsont.json ->
119119+ unit ->
120120+ Jsont.json
121121+(** Send a request and await the response.
122122+ Raises [Timeout] if the request times out.
123123+ Raises [Remote_error] if the server returns an error.
124124+ Raises [Session_closed] if the session is closed. *)
125125+126126+val send_notification :
127127+ t ->
128128+ method_:string ->
129129+ ?params:Jsont.json ->
130130+ unit ->
131131+ unit
132132+(** Send a notification (no response expected).
133133+ Raises [Session_closed] if the session is closed. *)
134134+135135+val close : t -> unit
136136+(** Close the session and underlying transport.
137137+ This will cancel all pending requests. *)
138138+139139+val is_closed : t -> bool
140140+(** Check if the session is closed *)
+29
claudeio/lib_mcp/transport.ml
···11+(** Abstract transport layer for MCP communication *)
22+33+exception Connection_error of string
44+55+(** Internal module type that transport implementations must satisfy *)
66+module type TRANSPORT = sig
77+ type state
88+99+ val send : state -> Jsont.json -> unit
1010+ val receive : state -> Jsont.json option
1111+ val close : state -> unit
1212+ val is_closed : state -> bool
1313+end
1414+1515+(** The abstract transport type - hides the concrete implementation *)
1616+type t = T : (module TRANSPORT with type state = 'a) * 'a -> t
1717+ [@@warning "-37"] (* Constructor T is used in transport implementations *)
1818+1919+let send (T ((module M), state)) json =
2020+ M.send state json
2121+2222+let receive (T ((module M), state)) =
2323+ M.receive state
2424+2525+let close (T ((module M), state)) =
2626+ M.close state
2727+2828+let is_closed (T ((module M), state)) =
2929+ M.is_closed state
+34
claudeio/lib_mcp/transport.mli
···11+(** Abstract transport layer for MCP communication *)
22+33+(** Module type that transport implementations must satisfy *)
44+module type TRANSPORT = sig
55+ type state
66+77+ val send : state -> Jsont.json -> unit
88+ val receive : state -> Jsont.json option
99+ val close : state -> unit
1010+ val is_closed : state -> bool
1111+end
1212+1313+(** The abstract transport type for sending/receiving JSON messages *)
1414+type t = T : (module TRANSPORT with type state = 'a) * 'a -> t
1515+1616+(** [send t json] sends a JSON message through the transport.
1717+ @raise Connection_error if the transport is closed or sending fails *)
1818+val send : t -> Jsont.json -> unit
1919+2020+(** [receive t] receives a JSON message from the transport (blocking).
2121+ Returns [None] on EOF or when the transport is closed.
2222+ @raise Connection_error if receiving fails for reasons other than EOF *)
2323+val receive : t -> Jsont.json option
2424+2525+(** [close t] closes the transport and releases all associated resources.
2626+ This is idempotent - calling close multiple times is safe. *)
2727+val close : t -> unit
2828+2929+(** [is_closed t] checks if the transport is closed.
3030+ Returns [true] if the transport has been closed, [false] otherwise. *)
3131+val is_closed : t -> bool
3232+3333+(** Exception raised when transport operations fail *)
3434+exception Connection_error of string
+179
claudeio/lib_mcp/transport_stdio.ml
···11+(** Stdio transport implementation for MCP *)
22+33+let src = Logs.Src.create "mcp.transport.stdio" ~doc:"MCP stdio transport"
44+module Log = (val Logs.src_log src : Logs.LOG)
55+66+exception Process_spawn_error of string
77+88+(** Parameters for creating a stdio transport *)
99+type params = {
1010+ command : string;
1111+ args : string list;
1212+ env : (string * string) list option;
1313+ max_buffer_size : int option;
1414+}
1515+1616+(** Wrapper for existential process type *)
1717+type process = P : _ Eio.Process.t -> process
1818+1919+(** Internal state for stdio transport *)
2020+type state = {
2121+ process : process;
2222+ stdin : Eio.Flow.sink_ty Eio.Resource.t;
2323+ stdin_close : [`Close | `Flow] Eio.Resource.t;
2424+ stdout : Eio.Buf_read.t;
2525+ mutable closed : bool;
2626+ sw : Eio.Switch.t;
2727+}
2828+2929+(** Send a JSON message by encoding to a line-delimited string *)
3030+let send state json =
3131+ if state.closed then
3232+ raise (Transport.Connection_error "Transport is closed");
3333+3434+ let data = match Jsont_bytesrw.encode_string' Jsont.json json with
3535+ | Ok s -> s
3636+ | Error err ->
3737+ let msg = Jsont.Error.to_string err in
3838+ raise (Transport.Connection_error ("JSON encoding failed: " ^ msg))
3939+ in
4040+4141+ Log.debug (fun m -> m "Sending: %s" data);
4242+4343+ try
4444+ Eio.Flow.write state.stdin [Cstruct.of_string (data ^ "\n")]
4545+ with
4646+ | exn ->
4747+ Log.err (fun m -> m "Failed to send message: %s" (Printexc.to_string exn));
4848+ raise (Transport.Connection_error
4949+ (Printf.sprintf "Failed to send message: %s" (Printexc.to_string exn)))
5050+5151+(** Receive a JSON message by reading a line and decoding *)
5252+let receive state =
5353+ if state.closed then
5454+ None
5555+ else
5656+ try
5757+ match Eio.Buf_read.line state.stdout with
5858+ | line ->
5959+ Log.debug (fun m -> m "Received: %s" line);
6060+ (match Jsont_bytesrw.decode_string' Jsont.json line with
6161+ | Ok json -> Some json
6262+ | Error err ->
6363+ let msg = Jsont.Error.to_string err in
6464+ Log.err (fun m -> m "JSON decoding failed: %s" msg);
6565+ raise (Transport.Connection_error ("JSON decoding failed: " ^ msg)))
6666+ | exception End_of_file ->
6767+ Log.debug (fun m -> m "Received EOF");
6868+ state.closed <- true;
6969+ None
7070+ with
7171+ | Transport.Connection_error _ as e -> raise e
7272+ | exn ->
7373+ Log.err (fun m -> m "Failed to receive message: %s" (Printexc.to_string exn));
7474+ raise (Transport.Connection_error
7575+ (Printf.sprintf "Failed to receive message: %s" (Printexc.to_string exn)))
7676+7777+(** Close the transport and cleanup resources *)
7878+let close state =
7979+ if not state.closed then begin
8080+ state.closed <- true;
8181+ try
8282+ Eio.Flow.close state.stdin_close;
8383+ let (P process) = state.process in
8484+ Eio.Process.await_exn process
8585+ with _ -> ()
8686+ end
8787+8888+(** Check if transport is closed *)
8989+let is_closed state =
9090+ state.closed
9191+9292+(** The transport module implementation *)
9393+module Stdio_transport : Transport.TRANSPORT with type state = state = struct
9494+ type nonrec state = state
9595+9696+ let send = send
9797+ let receive = receive
9898+ let close = close
9999+ let is_closed = is_closed
100100+end
101101+102102+(** Create a new stdio transport *)
103103+let create ~sw ~process_mgr params =
104104+ (* Build command arguments *)
105105+ let cmd = params.command :: params.args in
106106+107107+ (* Build environment - preserve essential vars and add custom ones *)
108108+ let home = try Unix.getenv "HOME" with Not_found -> "/tmp" in
109109+ let path = try Unix.getenv "PATH" with Not_found -> "/usr/bin:/bin" in
110110+111111+ (* Preserve other potentially important environment variables *)
112112+ let preserve_vars = [
113113+ "USER"; "LOGNAME"; "SHELL"; "TERM";
114114+ "XDG_CONFIG_HOME"; "XDG_DATA_HOME"; "XDG_CACHE_HOME";
115115+ ] in
116116+117117+ let preserved = List.filter_map (fun var ->
118118+ try Some (Printf.sprintf "%s=%s" var (Unix.getenv var))
119119+ with Not_found -> None
120120+ ) preserve_vars in
121121+122122+ let base_env = [
123123+ Printf.sprintf "HOME=%s" home;
124124+ Printf.sprintf "PATH=%s" path;
125125+ ] @ preserved in
126126+127127+ let custom_env = match params.env with
128128+ | None -> []
129129+ | Some vars -> List.map (fun (k, v) -> Printf.sprintf "%s=%s" k v) vars
130130+ in
131131+132132+ let env = Array.of_list (base_env @ custom_env) in
133133+134134+ Log.debug (fun m -> m "Environment: HOME=%s, PATH=%s" home path);
135135+ Log.info (fun m -> m "Spawning command: %s" (String.concat " " cmd));
136136+137137+ (* Create pipes for stdin/stdout *)
138138+ let stdin_r, stdin_w = Eio.Process.pipe ~sw process_mgr in
139139+ let stdout_r, stdout_w = Eio.Process.pipe ~sw process_mgr in
140140+141141+ (* Spawn the process *)
142142+ let process =
143143+ try
144144+ Eio.Process.spawn ~sw process_mgr
145145+ ~env
146146+ ~stdin:(stdin_r :> Eio.Flow.source_ty Eio.Resource.t)
147147+ ~stdout:(stdout_w :> Eio.Flow.sink_ty Eio.Resource.t)
148148+ cmd
149149+ with
150150+ | exn ->
151151+ Log.err (fun m -> m "Failed to spawn process: %s" (Printexc.to_string exn));
152152+ raise (Process_spawn_error
153153+ (Printf.sprintf "Failed to spawn process: %s" (Printexc.to_string exn)))
154154+ in
155155+156156+ (* Setup stdin for writing *)
157157+ let stdin = (stdin_w :> Eio.Flow.sink_ty Eio.Resource.t) in
158158+ let stdin_close = (stdin_w :> [`Close | `Flow] Eio.Resource.t) in
159159+160160+ (* Setup stdout for reading with buffering *)
161161+ let max_size = match params.max_buffer_size with
162162+ | Some size -> size
163163+ | None -> 1_000_000 (* Default 1MB *)
164164+ in
165165+ let stdout = Eio.Buf_read.of_flow ~max_size
166166+ (stdout_r :> Eio.Flow.source_ty Eio.Resource.t) in
167167+168168+ (* Create the state *)
169169+ let state = {
170170+ process = P process;
171171+ stdin;
172172+ stdin_close;
173173+ stdout;
174174+ closed = false;
175175+ sw;
176176+ } in
177177+178178+ (* Wrap in abstract transport type *)
179179+ Transport.T ((module Stdio_transport), state)
+38
claudeio/lib_mcp/transport_stdio.mli
···11+(** Stdio transport implementation for MCP *)
22+33+(** Parameters for creating a stdio transport *)
44+type params = {
55+ command : string;
66+ (** The command to execute (executable path or name in PATH) *)
77+88+ args : string list;
99+ (** Command-line arguments to pass to the command *)
1010+1111+ env : (string * string) list option;
1212+ (** Optional environment variables to set. If [None], inherits parent environment.
1313+ If [Some vars], these are ADDED to essential preserved variables (HOME, PATH, etc.) *)
1414+1515+ max_buffer_size : int option;
1616+ (** Maximum buffer size for reading from stdout. Defaults to 1MB if [None] *)
1717+}
1818+1919+(** [create ~sw ~process_mgr params] creates a new stdio transport by spawning
2020+ a subprocess with the given parameters.
2121+2222+ The subprocess communicates via line-delimited JSON on stdin/stdout:
2323+ - Each message is a single JSON object on one line
2424+ - Lines are terminated with newline ('\n')
2525+ - The transport handles encoding/decoding automatically
2626+2727+ @param sw The Eio switch that manages the subprocess lifetime
2828+ @param process_mgr The Eio process manager for spawning subprocesses
2929+ @param params Configuration parameters for the subprocess
3030+ @raise Transport.Connection_error if subprocess spawning fails *)
3131+val create :
3232+ sw:Eio.Switch.t ->
3333+ process_mgr:_ Eio.Process.mgr ->
3434+ params ->
3535+ Transport.t
3636+3737+(** Exception raised when subprocess spawning fails *)
3838+exception Process_spawn_error of string
+30
claudeio/mcp.opam
···11+# This file is generated by dune, edit dune-project instead
22+opam-version: "2.0"
33+synopsis: "Model Context Protocol (MCP) implementation in OCaml"
44+description:
55+ "An Eio-based OCaml library implementing the Model Context Protocol for connecting AI assistants with tools and data sources"
66+depends: [
77+ "ocaml"
88+ "dune" {>= "3.0"}
99+ "eio"
1010+ "fmt"
1111+ "logs"
1212+ "jsont" {>= "0.2.0"}
1313+ "jsont_bytesrw" {>= "0.2.0"}
1414+ "alcotest" {with-test}
1515+ "odoc" {with-doc}
1616+]
1717+build: [
1818+ ["dune" "subst"] {dev}
1919+ [
2020+ "dune"
2121+ "build"
2222+ "-p"
2323+ name
2424+ "-j"
2525+ jobs
2626+ "@install"
2727+ "@runtest" {with-test}
2828+ "@doc" {with-doc}
2929+ ]
3030+]