···
4
-
(* Logging utilities *)
6
-
Printf.eprintf "[DEBUG] %s\n" msg;
10
-
Printf.eprintf "[ERROR] %s\n" msg;
14
-
let protocol_version = "2024-11-05"
15
-
let server_info = Implementation.{ name = "ocaml-mcp-capitalizer"; version = "0.1.0" }
16
-
let server_capabilities = `Assoc [
17
-
(* We support tools *)
19
-
("listChanged", `Bool true)
21
-
(* We don't support resources - make this explicit *)
22
-
("resources", `Assoc [
23
-
("listChanged", `Bool false);
24
-
("subscribe", `Bool false)
26
-
(* We don't support prompts - make this explicit *)
27
-
("prompts", `Assoc [
28
-
("listChanged", `Bool false)
32
-
(* Tool implementation *)
33
-
module CapitalizeTool = struct
34
-
let name = "capitalize"
35
-
let description = "Capitalizes the provided text"
36
-
let input_schema = `Assoc [
37
-
("type", `String "object");
38
-
("properties", `Assoc [
40
-
("type", `String "string");
41
-
("description", `String "The text to capitalize")
44
-
("required", `List [`String "text"])
50
-
(match List.assoc_opt "text" fields with
51
-
| Some (`String text) ->
52
-
let capitalized_text = String.uppercase_ascii text in
53
-
let content = TextContent.{
54
-
text = capitalized_text;
58
-
("content", `List [TextContent.yojson_of_t content]);
59
-
("isError", `Bool false)
62
-
let error_content = TextContent.{
63
-
text = "Missing or invalid 'text' parameter";
67
-
("content", `List [TextContent.yojson_of_t error_content]);
68
-
("isError", `Bool true)
71
-
let error_content = TextContent.{
72
-
text = "Invalid arguments format";
76
-
("content", `List [TextContent.yojson_of_t error_content]);
77
-
("isError", `Bool true)
81
-
(* Handle tool listing *)
84
-
("name", `String CapitalizeTool.name);
85
-
("description", `String CapitalizeTool.description);
86
-
("inputSchema", CapitalizeTool.input_schema)
89
-
("tools", `List [tool])
92
-
(* Handle tool calls *)
93
-
let call_tool name args =
94
-
if name = CapitalizeTool.name then
95
-
CapitalizeTool.call args
97
-
let error_content = TextContent.{
98
-
text = Printf.sprintf "Unknown tool: %s" name;
102
-
("content", `List [TextContent.yojson_of_t error_content]);
103
-
("isError", `Bool true)
106
-
(* Handle initialization *)
107
-
let handle_initialize id json =
109
-
log_debug (Printf.sprintf "Processing initialize request with id: %s"
111
-
| `Int i -> string_of_int i
112
-
| `String s -> s));
114
-
log_debug (Printf.sprintf "Initialize params: %s"
116
-
| Some j -> Yojson.Safe.to_string j
117
-
| None -> "null"));
119
-
let _ = match json with
121
-
log_debug "Parsing initialize request params...";
122
-
let req = Initialize.Request.t_of_yojson params in
123
-
log_debug (Printf.sprintf "Client info: %s v%s" req.client_info.name req.client_info.version);
124
-
log_debug (Printf.sprintf "Client protocol version: %s" req.protocol_version);
126
-
(* Check protocol version compatibility *)
127
-
if req.protocol_version <> protocol_version then
128
-
log_debug (Printf.sprintf "Protocol version mismatch: client=%s server=%s - will use server version"
129
-
req.protocol_version protocol_version);
133
-
log_error "Missing params for initialize request";
134
-
raise (Json.Of_json ("Missing params for initialize request", `Null))
137
-
log_debug "Creating initialize response...";
138
-
let result = Initialize.Result.create
139
-
~capabilities:server_capabilities
142
-
~instructions:"This server provides a tool to capitalize text."
146
-
log_debug "Serializing initialize response...";
147
-
let response = create_response ~id ~result:(Initialize.Result.yojson_of_t result) in
148
-
log_debug "Initialize response created successfully";
151
-
| Json.Of_json (msg, _) ->
152
-
log_error (Printf.sprintf "JSON error in initialize: %s" msg);
153
-
create_error ~id ~code:(-32602) ~message:("Invalid params: " ^ msg) ()
155
-
log_error (Printf.sprintf "Exception in initialize: %s" (Printexc.to_string exc));
156
-
log_error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
157
-
create_error ~id ~code:(-32603) ~message:("Internal error: " ^ (Printexc.to_string exc)) ()
159
-
(* Handle tools/list *)
160
-
let handle_list_tools id =
161
-
log_debug "Processing tools/list request";
162
-
let result = list_tools () in
163
-
log_debug (Printf.sprintf "Tools list result: %s" (Yojson.Safe.to_string result));
164
-
create_response ~id ~result
166
-
(* Handle tools/call *)
167
-
let handle_call_tool id json =
169
-
log_debug (Printf.sprintf "Processing tool call request with id: %s"
171
-
| `Int i -> string_of_int i
172
-
| `String s -> s));
174
-
log_debug (Printf.sprintf "Tool call params: %s"
176
-
| Some j -> Yojson.Safe.to_string j
177
-
| None -> "null"));
180
-
| Some (`Assoc params) ->
181
-
let name = match List.assoc_opt "name" params with
182
-
| Some (`String name) ->
183
-
log_debug (Printf.sprintf "Tool name: %s" name);
186
-
log_error "Missing or invalid 'name' parameter in tool call";
187
-
raise (Json.Of_json ("Missing or invalid 'name' parameter", `Assoc params))
189
-
let args = match List.assoc_opt "arguments" params with
191
-
log_debug (Printf.sprintf "Tool arguments: %s" (Yojson.Safe.to_string args));
194
-
log_debug "No arguments provided for tool call, using empty object";
195
-
`Assoc [] (* Empty arguments is valid *)
197
-
log_debug (Printf.sprintf "Calling tool: %s" name);
198
-
let result = call_tool name args in
199
-
log_debug (Printf.sprintf "Tool call result: %s" (Yojson.Safe.to_string result));
200
-
create_response ~id ~result
202
-
log_error "Invalid params format for tools/call";
203
-
create_error ~id ~code:(-32602) ~message:"Invalid params for tools/call" ()
205
-
| Json.Of_json (msg, _) ->
206
-
log_error (Printf.sprintf "JSON error in tool call: %s" msg);
207
-
create_error ~id ~code:(-32602) ~message:("Invalid params: " ^ msg) ()
209
-
log_error (Printf.sprintf "Exception in tool call: %s" (Printexc.to_string exc));
210
-
log_error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
211
-
create_error ~id ~code:(-32603) ~message:("Internal error: " ^ (Printexc.to_string exc)) ()
214
-
let handle_ping id =
215
-
create_response ~id ~result:(`Assoc [])
217
-
(* Process a single message *)
218
-
let process_message message =
220
-
log_debug "Parsing message as JSONRPC message...";
221
-
match JSONRPCMessage.t_of_yojson message with
222
-
| JSONRPCMessage.Request req ->
223
-
log_debug (Printf.sprintf "Received request with method: %s" req.method_);
224
-
(match req.method_ with
226
-
log_debug "Processing initialize request";
227
-
Some (handle_initialize req.id req.params)
229
-
log_debug "Processing tools/list request";
230
-
Some (handle_list_tools req.id)
232
-
log_debug "Processing tools/call request";
233
-
Some (handle_call_tool req.id req.params)
235
-
log_debug "Processing ping request";
236
-
Some (handle_ping req.id)
238
-
log_error (Printf.sprintf "Unknown method received: %s" req.method_);
239
-
Some (create_error ~id:req.id ~code:(-32601) ~message:("Method not found: " ^ req.method_) ()))
240
-
| JSONRPCMessage.Notification notif ->
241
-
log_debug (Printf.sprintf "Received notification with method: %s" notif.method_);
242
-
(match notif.method_ with
243
-
| "notifications/initialized" ->
244
-
log_debug "Client initialization complete - Server is now ready to receive requests";
245
-
log_debug (Printf.sprintf "Notification params: %s"
246
-
(match notif.params with
247
-
| Some p -> Yojson.Safe.to_string p
248
-
| None -> "null"));
251
-
log_debug (Printf.sprintf "Ignoring notification: %s" notif.method_);
253
-
| JSONRPCMessage.Response _ ->
254
-
log_error "Unexpected response message received";
256
-
| JSONRPCMessage.Error _ ->
257
-
log_error "Unexpected error message received";
261
-
log_error (Printf.sprintf "Exception during message processing: %s" (Printexc.to_string exc));
262
-
log_error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()));
263
-
log_error (Printf.sprintf "Message was: %s" (Yojson.Safe.to_string message));
267
-
let rec read_message () =
269
-
log_debug "Attempting to read line from stdin...";
270
-
let line = read_line () in
271
-
if line = "" then (
272
-
log_debug "Empty line received, ignoring";
275
-
log_debug (Printf.sprintf "Raw input: %s" line);
277
-
let json = Yojson.Safe.from_string line in
278
-
log_debug "Successfully parsed JSON";
281
-
| Yojson.Json_error msg ->
282
-
log_error (Printf.sprintf "Error parsing JSON: %s" msg);
283
-
log_error (Printf.sprintf "Input was: %s" line);
288
-
log_debug "End of file received on stdin";
291
-
log_error (Printf.sprintf "System error while reading: %s" msg);
294
-
log_error (Printf.sprintf "Exception while reading: %s" (Printexc.to_string exc));
299
-
(* Enable exception backtraces *)
300
-
Printexc.record_backtrace true;
302
-
(* Enable line buffering for stdout *)
303
-
set_binary_mode_out stdout false;
305
-
log_debug "MCP Capitalizer server started";
306
-
log_debug (Printf.sprintf "Protocol version: %s" protocol_version);
307
-
log_debug (Printf.sprintf "Server info: %s v%s" server_info.name server_info.version);
309
-
(* Print environment info for debugging *)
310
-
log_debug "Environment variables:";
312
-
|> Array.iter (fun s ->
314
-
let i = String.index s '=' in
315
-
let name = String.sub s 0 i in
316
-
if String.length name > 0 then
317
-
log_debug (Printf.sprintf " %s" s)
318
-
with Not_found -> ()
321
-
let rec server_loop count =
322
-
log_debug (Printf.sprintf "Waiting for message #%d..." count);
323
-
match read_message () with
325
-
log_debug (Printf.sprintf "Received message: %s" (Yojson.Safe.to_string json));
326
-
(match process_message json with
328
-
let response_json = JSONRPCMessage.yojson_of_t response in
329
-
let response_str = Yojson.Safe.to_string response_json in
330
-
log_debug (Printf.sprintf "Sending response: %s" response_str);
331
-
(* Make sure we emit properly formatted JSON on a single line with a newline at the end *)
332
-
Printf.printf "%s\n" response_str;
334
-
(* Give the client a moment to process the response *)
336
-
server_loop (count + 1)
338
-
log_debug "No response needed for this message";
339
-
server_loop (count + 1))
341
-
log_debug "End of input stream, terminating server";
345
-
log_debug "Starting server loop...";
346
-
log_debug "Waiting for the initialize request...";
348
-
(* Set up signal handler to gracefully exit *)
349
-
Sys.(set_signal sigint (Signal_handle (fun _ ->
350
-
log_debug "Received interrupt signal, exiting...";
355
-
log_debug "Server terminated normally";
358
-
log_error "Unexpected end of file";
360
-
log_error (Printf.sprintf "System error: %s" msg);
361
-
| Unix.Unix_error(err, func, arg) ->
362
-
log_error (Printf.sprintf "Unix error in %s(%s): %s" func arg (Unix.error_message err));
364
-
log_error (Printf.sprintf "Unhandled exception: %s" (Printexc.to_string exc));
365
-
log_error (Printf.sprintf "Backtrace: %s" (Printexc.get_backtrace()))