MCP tree server: add failure logging to /tmp/mcp-tree.log
Logs timestamps, tool calls, errors, slow calls, stack overflow, OOM. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,25 @@ open Sx_types
|
|||||||
(* Hot-reload: re-exec ourselves when the binary has been rebuilt *)
|
(* Hot-reload: re-exec ourselves when the binary has been rebuilt *)
|
||||||
(* ------------------------------------------------------------------ *)
|
(* ------------------------------------------------------------------ *)
|
||||||
|
|
||||||
|
(* ------------------------------------------------------------------ *)
|
||||||
|
(* File-based logging for debugging MCP failures *)
|
||||||
|
(* ------------------------------------------------------------------ *)
|
||||||
|
|
||||||
|
let log_file = "/tmp/mcp-tree.log"
|
||||||
|
|
||||||
|
let log_msg fmt =
|
||||||
|
Printf.ksprintf (fun msg ->
|
||||||
|
let oc = open_out_gen [Open_append; Open_creat; Open_wronly] 0o644 log_file in
|
||||||
|
let t = Unix.gettimeofday () in
|
||||||
|
let tm = Unix.localtime t in
|
||||||
|
Printf.fprintf oc "[%04d-%02d-%02d %02d:%02d:%02d.%03d] %s\n"
|
||||||
|
(1900 + tm.Unix.tm_year) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday
|
||||||
|
tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec
|
||||||
|
(int_of_float (mod_float (t *. 1000.0) 1000.0))
|
||||||
|
msg;
|
||||||
|
close_out oc
|
||||||
|
) fmt
|
||||||
|
|
||||||
let exe_path =
|
let exe_path =
|
||||||
try Unix.readlink "/proc/self/exe"
|
try Unix.readlink "/proc/self/exe"
|
||||||
with _ -> Sys.executable_name
|
with _ -> Sys.executable_name
|
||||||
@@ -2577,10 +2596,28 @@ let handle_sx_request args =
|
|||||||
let handle_tool name args =
|
let handle_tool name args =
|
||||||
match Hashtbl.find_opt tool_handlers name with
|
match Hashtbl.find_opt tool_handlers name with
|
||||||
| Some handler ->
|
| Some handler ->
|
||||||
(try handler args
|
(try
|
||||||
with Invalid_argument msg -> error_result msg
|
let file_arg = try Yojson.Safe.Util.(args |> member "file" |> to_string) with _ -> "" in
|
||||||
| e -> error_result ("Error: " ^ Printexc.to_string e))
|
let t0 = Unix.gettimeofday () in
|
||||||
| None -> error_result ("Unknown tool: " ^ name)
|
let r = handler args in
|
||||||
|
let elapsed = (Unix.gettimeofday () -. t0) *. 1000.0 in
|
||||||
|
log_msg "OK [%s] %s (%.0fms)" name (if file_arg <> "" then "file=" ^ file_arg else "") elapsed;
|
||||||
|
r
|
||||||
|
with Invalid_argument msg ->
|
||||||
|
log_msg "INVALID_ARG [%s]: %s" name msg;
|
||||||
|
error_result msg
|
||||||
|
| Stack_overflow ->
|
||||||
|
log_msg "STACK OVERFLOW [%s]" name;
|
||||||
|
error_result "Stack overflow — file too large or recursive structure"
|
||||||
|
| Out_of_memory ->
|
||||||
|
log_msg "OUT OF MEMORY [%s]" name;
|
||||||
|
error_result "Out of memory"
|
||||||
|
| e ->
|
||||||
|
log_msg "EXCEPTION [%s]: %s" name (Printexc.to_string e);
|
||||||
|
error_result ("Error: " ^ Printexc.to_string e))
|
||||||
|
| None ->
|
||||||
|
log_msg "UNKNOWN TOOL: %s" name;
|
||||||
|
error_result ("Unknown tool: " ^ name)
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
register "sx_read_tree" handle_sx_read_tree;
|
register "sx_read_tree" handle_sx_read_tree;
|
||||||
@@ -2848,6 +2885,7 @@ let dispatch method_name params =
|
|||||||
|
|
||||||
let () =
|
let () =
|
||||||
setup_env ();
|
setup_env ();
|
||||||
|
log_msg "Server started (pid=%d, cwd=%s)" (Unix.getpid ()) (Sys.getcwd ());
|
||||||
try while true do
|
try while true do
|
||||||
let line = input_line stdin in
|
let line = input_line stdin in
|
||||||
if String.length line > 0 then begin
|
if String.length line > 0 then begin
|
||||||
@@ -2857,10 +2895,27 @@ let () =
|
|||||||
let meth = json |> member "method" |> to_string_option |> Option.value ~default:"" in
|
let meth = json |> member "method" |> to_string_option |> Option.value ~default:"" in
|
||||||
let params = json |> member "params" in
|
let params = json |> member "params" in
|
||||||
let id = json |> member "id" in
|
let id = json |> member "id" in
|
||||||
|
let tool_name = if meth = "tools/call" then
|
||||||
|
(try params |> member "name" |> to_string with _ -> "?")
|
||||||
|
else meth in
|
||||||
|
let t0 = Unix.gettimeofday () in
|
||||||
let result =
|
let result =
|
||||||
try dispatch meth params
|
try dispatch meth params
|
||||||
with e -> error_result ("Error: " ^ Printexc.to_string e)
|
with e ->
|
||||||
|
let msg = Printexc.to_string e in
|
||||||
|
log_msg "DISPATCH ERROR [%s]: %s" tool_name msg;
|
||||||
|
error_result ("Error: " ^ msg)
|
||||||
in
|
in
|
||||||
|
let elapsed = (Unix.gettimeofday () -. t0) *. 1000.0 in
|
||||||
|
if elapsed > 5000.0 then
|
||||||
|
log_msg "SLOW [%s]: %.0fms" tool_name elapsed;
|
||||||
|
(* Check for error in result *)
|
||||||
|
(match result with
|
||||||
|
| `Assoc items when List.mem_assoc "isError" items ->
|
||||||
|
log_msg "TOOL ERROR [%s]: %s (%.0fms)" tool_name
|
||||||
|
(try result |> member "content" |> to_list |> List.hd |> member "text" |> to_string with _ -> "?")
|
||||||
|
elapsed
|
||||||
|
| _ -> ());
|
||||||
if id <> `Null then begin
|
if id <> `Null then begin
|
||||||
let resp = `Assoc [
|
let resp = `Assoc [
|
||||||
("jsonrpc", `String "2.0");
|
("jsonrpc", `String "2.0");
|
||||||
@@ -2872,7 +2927,10 @@ let () =
|
|||||||
check_hot_reload ()
|
check_hot_reload ()
|
||||||
end
|
end
|
||||||
with e ->
|
with e ->
|
||||||
Printf.eprintf "[mcp] Request error: %s\n%!" (Printexc.to_string e)
|
let msg = Printexc.to_string e in
|
||||||
|
log_msg "REQUEST PARSE ERROR: %s" msg;
|
||||||
|
Printf.eprintf "[mcp] Request error: %s\n%!" msg
|
||||||
end
|
end
|
||||||
done
|
done
|
||||||
with End_of_file -> ()
|
with End_of_file ->
|
||||||
|
log_msg "Server shutting down (End_of_file)"
|
||||||
|
|||||||
Reference in New Issue
Block a user