···
5
+
(* Process initialize request *)
6
+
let handle_initialize server req =
7
+
Log.debug "Processing initialize request";
8
+
let result = match req.JSONRPCMessage.params with
10
+
let req_data = Initialize.Request.t_of_yojson params in
11
+
Log.debug (Printf.sprintf "Client info: %s v%s"
12
+
req_data.client_info.name req_data.client_info.version);
13
+
Log.debug (Printf.sprintf "Client protocol version: %s" req_data.protocol_version);
15
+
(* Create initialize response *)
16
+
let result = Initialize.Result.create
17
+
~capabilities:(capabilities server)
18
+
~server_info:Implementation.{
20
+
version = version server
22
+
~protocol_version:(protocol_version server)
23
+
~instructions:(Printf.sprintf "This server provides tools for %s." (name server))
26
+
Initialize.Result.yojson_of_t result
28
+
Log.error "Missing params for initialize request";
29
+
`Assoc [("error", `String "Missing params for initialize request")]
31
+
Some (create_response ~id:req.id ~result)
33
+
(* Process tools/list request *)
34
+
let handle_tools_list server (req:JSONRPCMessage.request) =
35
+
Log.debug "Processing tools/list request";
36
+
let tool_list = List.map Tool.to_json (tools server) in
37
+
let result = `Assoc [("tools", `List tool_list)] in
38
+
Some (create_response ~id:req.id ~result)
40
+
(* Process prompts/list request *)
41
+
let handle_prompts_list server (req:JSONRPCMessage.request) =
42
+
Log.debug "Processing prompts/list request";
43
+
let prompt_list = List.map Prompt.to_json (prompts server) in
44
+
let result = `Assoc [("prompts", `List prompt_list)] in
45
+
Some (create_response ~id:req.id ~result)
47
+
(* Process resources/list request *)
48
+
let handle_resources_list server (req:JSONRPCMessage.request) =
49
+
Log.debug "Processing resources/list request";
50
+
let resource_list = List.map Resource.to_json (resources server) in
51
+
let result = `Assoc [("resources", `List resource_list)] in
52
+
Some (create_response ~id:req.id ~result)
54
+
(* Create an error result with text content *)
55
+
let create_error_content err =
56
+
let error_content = TextContent.{
61
+
("content", `List [TextContent.yojson_of_t error_content]);
62
+
("isError", `Bool true)
65
+
(* Extract the tool name from params *)
66
+
let extract_tool_name params =
67
+
match List.assoc_opt "name" params with
68
+
| Some (`String name) ->
69
+
Log.debug (Printf.sprintf "Tool name: %s" name);
72
+
Log.error "Missing or invalid 'name' parameter in tool call";
75
+
(* Extract the tool arguments from params *)
76
+
let extract_tool_arguments params =
77
+
match List.assoc_opt "arguments" params with
79
+
Log.debug (Printf.sprintf "Tool arguments: %s" (Yojson.Safe.to_string args));
82
+
Log.debug "No arguments provided for tool call, using empty object";
83
+
`Assoc [] (* Empty arguments is valid *)
85
+
(* Handle tool execution errors *)
86
+
let handle_tool_execution_error err =
87
+
Log.error (Printf.sprintf "Tool execution failed: %s" err);
88
+
create_error_content (Printf.sprintf "Error executing tool: %s" err)
90
+
(* Handle unknown tool error *)
91
+
let handle_unknown_tool_error name =
92
+
Log.error (Printf.sprintf "Unknown tool: %s" name);
93
+
create_error_content (Printf.sprintf "Unknown tool: %s" name)
95
+
(* Handle general tool execution exception *)
96
+
let handle_tool_execution_exception exn =
97
+
Log.error (Printf.sprintf "Exception executing tool: %s" (Printexc.to_string exn));
98
+
create_error_content (Printf.sprintf "Internal error: %s" (Printexc.to_string exn))
100
+
(* Execute a tool *)
101
+
let execute_tool server ctx name args =
103
+
let tool = List.find (fun t -> t.Tool.name = name) (tools server) in
104
+
Log.debug (Printf.sprintf "Found tool: %s" name);
106
+
(* Call the tool handler *)
107
+
match tool.handler ctx args with
109
+
Log.debug "Tool execution succeeded";
111
+
| Error err -> handle_tool_execution_error err
113
+
| Not_found -> handle_unknown_tool_error name
114
+
| exn -> handle_tool_execution_exception exn
116
+
(* Process tools/call request *)
117
+
let handle_tools_call server req =
118
+
Log.debug "Processing tools/call request";
119
+
match req.JSONRPCMessage.params with
120
+
| Some (`Assoc params) ->
121
+
(match extract_tool_name params with
123
+
let args = extract_tool_arguments params in
125
+
(* Create context for this request *)
126
+
let ctx = Context.create ?request_id:req.progress_token () in
128
+
(* Execute the tool *)
129
+
let result = execute_tool server ctx name args in
130
+
Some (create_response ~id:req.id ~result)
132
+
Some (create_error ~id:req.id ~code:(-32602) ~message:"Missing tool name parameter" ()))
134
+
Log.error "Invalid params format for tools/call";
135
+
Some (create_error ~id:req.id ~code:(-32602) ~message:"Invalid params for tools/call" ())
137
+
(* Process ping request *)
138
+
let handle_ping (req:JSONRPCMessage.request) =
139
+
Log.debug "Processing ping request";
140
+
Some (create_response ~id:req.JSONRPCMessage.id ~result:(`Assoc []))
142
+
(* Handle notifications/initialized *)
143
+
let handle_initialized (notif:JSONRPCMessage.notification) =
144
+
Log.debug "Client initialization complete - Server is now ready to receive requests";
145
+
Log.debug (Printf.sprintf "Notification params: %s"
146
+
(match notif.JSONRPCMessage.params with
147
+
| Some p -> Yojson.Safe.to_string p
148
+
| None -> "null"));
(* Process a single message using the MCP SDK *)
let process_message server message =
···
| JSONRPCMessage.Request req ->
Log.debug (Printf.sprintf "Received request with method: %s" req.method_);
14
-
Log.debug "Processing initialize request";
15
-
let result = match req.params with
17
-
let req_data = Initialize.Request.t_of_yojson params in
18
-
Log.debug (Printf.sprintf "Client info: %s v%s"
19
-
req_data.client_info.name req_data.client_info.version);
20
-
Log.debug (Printf.sprintf "Client protocol version: %s" req_data.protocol_version);
22
-
(* Create initialize response *)
23
-
let result = Initialize.Result.create
24
-
~capabilities:(capabilities server)
25
-
~server_info:Implementation.{
27
-
version = version server
29
-
~protocol_version:(protocol_version server)
30
-
~instructions:(Printf.sprintf "This server provides tools for %s." (name server))
33
-
Initialize.Result.yojson_of_t result
35
-
Log.error "Missing params for initialize request";
36
-
`Assoc [("error", `String "Missing params for initialize request")]
38
-
Some (create_response ~id:req.id ~result)
40
-
Log.debug "Processing tools/list request";
41
-
let tool_list = List.map Tool.to_json (tools server) in
42
-
let result = `Assoc [("tools", `List tool_list)] in
43
-
Some (create_response ~id:req.id ~result)
45
-
Log.debug "Processing tools/call request";
46
-
(match req.params with
47
-
| Some (`Assoc params) ->
48
-
let name = match List.assoc_opt "name" params with
49
-
| Some (`String name) ->
50
-
Log.debug (Printf.sprintf "Tool name: %s" name);
53
-
Log.error "Missing or invalid 'name' parameter in tool call";
56
-
let args = match List.assoc_opt "arguments" params with
58
-
Log.debug (Printf.sprintf "Tool arguments: %s" (Yojson.Safe.to_string args));
61
-
Log.debug "No arguments provided for tool call, using empty object";
62
-
`Assoc [] (* Empty arguments is valid *)
65
-
(* Find the matching tool *)
68
-
let tool = List.find (fun t -> t.Tool.name = name) (tools server) in
69
-
Log.debug (Printf.sprintf "Found tool: %s" name);
71
-
(* Create context for this request *)
72
-
let ctx = Context.create ?request_id:req.progress_token () in
74
-
(* Call the tool handler *)
75
-
match tool.handler ctx args with
77
-
Log.debug (Printf.sprintf "Tool execution succeeded");
80
-
Log.error (Printf.sprintf "Tool execution failed: %s" err);
81
-
let error_content = TextContent.{
82
-
text = Printf.sprintf "Error executing tool: %s" err;
86
-
("content", `List [TextContent.yojson_of_t error_content]);
87
-
("isError", `Bool true)
91
-
Log.error (Printf.sprintf "Unknown tool: %s" name);
92
-
let error_content = TextContent.{
93
-
text = Printf.sprintf "Unknown tool: %s" name;
97
-
("content", `List [TextContent.yojson_of_t error_content]);
98
-
("isError", `Bool true)
101
-
Log.error (Printf.sprintf "Exception executing tool: %s" (Printexc.to_string exn));
102
-
let error_content = TextContent.{
103
-
text = Printf.sprintf "Internal error: %s" (Printexc.to_string exn);
107
-
("content", `List [TextContent.yojson_of_t error_content]);
108
-
("isError", `Bool true)
111
-
Some (create_response ~id:req.id ~result)
113
-
Log.error "Invalid params format for tools/call";
114
-
Some (create_error ~id:req.id ~code:(-32602) ~message:"Invalid params for tools/call" ()))
116
-
Log.debug "Processing ping request";
117
-
Some (create_response ~id:req.id ~result:(`Assoc []))
159
+
| "initialize" -> handle_initialize server req
160
+
| "tools/list" -> handle_tools_list server req
161
+
| "tools/call" -> handle_tools_call server req
162
+
| "prompts/list" -> handle_prompts_list server req
163
+
| "resources/list" -> handle_resources_list server req
164
+
| "ping" -> handle_ping req
Log.error (Printf.sprintf "Unknown method received: %s" req.method_);
Some (create_error ~id:req.id ~code:(-32601) ~message:("Method not found: " ^ req.method_) ()))
| JSONRPCMessage.Notification notif ->
Log.debug (Printf.sprintf "Received notification with method: %s" notif.method_);
(match notif.method_ with
124
-
| "notifications/initialized" ->
125
-
Log.debug "Client initialization complete - Server is now ready to receive requests";
126
-
Log.debug (Printf.sprintf "Notification params: %s"
127
-
(match notif.params with
128
-
| Some p -> Yojson.Safe.to_string p
129
-
| None -> "null"));
171
+
| "notifications/initialized" -> handle_initialized notif
Log.debug (Printf.sprintf "Ignoring notification: %s" notif.method_);
···
Log.error (Printf.sprintf "Message was: %s" (Yojson.Safe.to_string message));
191
+
(* Handle processing for an input line *)
192
+
let process_input_line server line =
193
+
if line = "" then (
194
+
Log.debug "Empty line received, ignoring";
197
+
Log.debug (Printf.sprintf "Raw input: %s" line);
199
+
let json = Yojson.Safe.from_string line in
200
+
Log.debug "Successfully parsed JSON";
202
+
(* Process the message *)
203
+
process_message server json
205
+
| Yojson.Json_error msg -> begin
206
+
Log.error (Printf.sprintf "Error parsing JSON: %s" msg);
207
+
Log.error (Printf.sprintf "Input was: %s" line);
212
+
(* Send a response to the client *)
213
+
let send_response stdout response =
214
+
let response_json = JSONRPCMessage.yojson_of_t response in
215
+
let response_str = Yojson.Safe.to_string response_json in
216
+
Log.debug (Printf.sprintf "Sending response: %s" response_str);
218
+
(* Write the response followed by a newline *)
219
+
Eio.Flow.copy_string response_str stdout;
220
+
Eio.Flow.copy_string "\n" stdout
(* Run the MCP server with the given server configuration *)
let run_server env server =
let stdin = Eio.Stdenv.stdin env in
···
(* Main processing loop *)
166
-
Log.debug (Printf.sprintf "Waiting for message..." );
238
+
Log.debug "Waiting for message...";
let line = Eio.Buf_read.line buf in
168
-
if line = "" then (
169
-
Log.debug "Empty line received, ignoring";
171
-
Log.debug (Printf.sprintf "Raw input: %s" line);
173
-
let json = Yojson.Safe.from_string line in
174
-
Log.debug "Successfully parsed JSON";
176
-
(* Process the message *)
177
-
match process_message server json with
178
-
| Some response -> begin
179
-
(* Send response *)
180
-
let response_json = JSONRPCMessage.yojson_of_t response in
181
-
let response_str = Yojson.Safe.to_string response_json in
182
-
Log.debug (Printf.sprintf "Sending response: %s" response_str);
184
-
(* Write the response followed by a newline *)
185
-
Eio.Flow.copy_string response_str stdout;
186
-
Eio.Flow.copy_string "\n" stdout;
189
-
Log.debug "No response needed for this message";
191
-
| Yojson.Json_error msg -> begin
192
-
Log.error (Printf.sprintf "Error parsing JSON: %s" msg);
193
-
Log.error (Printf.sprintf "Input was: %s" line);
241
+
(* Process the input and send response if needed *)
242
+
match process_input_line server line with
243
+
| Some response -> send_response stdout response
244
+
| None -> Log.debug "No response needed for this message"
199
-
Log.debug "End of file received on stdin";
201
-
| Eio.Exn.Io _ as exn ->
202
-
Log.error (Printf.sprintf "I/O error while reading: %s" (Printexc.to_string exn));
205
-
Log.error (Printf.sprintf "Exception while reading: %s" (Printexc.to_string exc));
248
+
Log.debug "End of file received on stdin";
250
+
| Eio.Exn.Io _ as exn ->
251
+
Log.error (Printf.sprintf "I/O error while reading: %s" (Printexc.to_string exn));
254
+
Log.error (Printf.sprintf "Exception while reading: %s" (Printexc.to_string exn));