diff --git a/compiler/bsc/rescript_compiler_main.ml b/compiler/bsc/rescript_compiler_main.ml
index ec40263bb6..6625b0709a 100644
--- a/compiler/bsc/rescript_compiler_main.ml
+++ b/compiler/bsc/rescript_compiler_main.ml
@@ -52,6 +52,8 @@ let setup_outcome_printer () = Lazy.force Res_outcome_printer.setup
let setup_runtime_path path = Runtime_package.path := path
+let print_suggested_actions_if_any () = Suggested_actions.print_block_if_any ()
+
let process_file sourcefile ?kind ppf =
(* This is a better default then "", it will be changed later
The {!Location.input_name} relies on that we write the binary ast
@@ -114,6 +116,7 @@ let reprint_source_file sourcefile =
if parse_result.invalid then (
Res_diagnostics.print_report parse_result.diagnostics
parse_result.source;
+ print_suggested_actions_if_any ();
exit 1);
Res_compmisc.init_path ();
parse_result.parsetree
@@ -131,6 +134,7 @@ let reprint_source_file sourcefile =
if parse_result.invalid then (
Res_diagnostics.print_report parse_result.diagnostics
parse_result.source;
+ print_suggested_actions_if_any ();
exit 1);
Res_compmisc.init_path ();
parse_result.parsetree
@@ -143,6 +147,7 @@ let reprint_source_file sourcefile =
print_endline
("Invalid input for reprinting ReScript source. Must be a ReScript \
file: " ^ sourcefile);
+ print_suggested_actions_if_any ();
exit 2
in
res
@@ -198,6 +203,7 @@ let bs_version_string = "ReScript " ^ Bs_version.version
let print_version_string () =
print_endline bs_version_string;
+ print_suggested_actions_if_any ();
exit 0
let[@inline] set s : Bsc_args.spec = Unit (Unit_set s)
@@ -358,6 +364,14 @@ let command_line_flags : (string * Bsc_args.spec * string) array =
Clflags.ignore_parse_errors := true;
Js_config.cmi_only := true),
"*internal* Enable editor mode." );
+ ( "-llm-mode",
+ unit_call (fun () ->
+ Js_config.llm_mode := true;
+ Clflags.binary_annotations := false;
+ Clflags.color := Some Never;
+ Location.draw_underline_in_code_frame := true;
+ Warnings.llm_mode := true),
+ "*internal* Enable LLM mode for enhanced tooling" );
( "-ignore-parse-errors",
set Clflags.ignore_parse_errors,
"*internal* continue after parse errors" );
@@ -443,7 +457,11 @@ let _ : unit =
try Bsc_args.parse_exn ~argv:Sys.argv command_line_flags anonymous ~usage with
| Bsc_args.Bad msg ->
Format.eprintf "%s@." msg;
+ print_suggested_actions_if_any ();
exit 2
| x ->
Location.report_exception ppf x;
+ print_suggested_actions_if_any ();
exit 2
+
+let _ = print_suggested_actions_if_any ()
diff --git a/compiler/common/js_config.ml b/compiler/common/js_config.ml
index 24aa8b69f1..c3037e0b6e 100644
--- a/compiler/common/js_config.ml
+++ b/compiler/common/js_config.ml
@@ -72,3 +72,5 @@ let jsx_module_of_string = function
let as_pp = ref false
let self_stack : string Stack.t = Stack.create ()
+
+let llm_mode = ref false
diff --git a/compiler/common/js_config.mli b/compiler/common/js_config.mli
index d6f4bd8ba6..9d7554d3c2 100644
--- a/compiler/common/js_config.mli
+++ b/compiler/common/js_config.mli
@@ -101,3 +101,5 @@ val jsx_module_of_string : string -> jsx_module
val as_pp : bool ref
val self_stack : string Stack.t
+
+val llm_mode : bool ref
diff --git a/compiler/ext/suggested_actions.ml b/compiler/ext/suggested_actions.ml
new file mode 100644
index 0000000000..5cb19187cc
--- /dev/null
+++ b/compiler/ext/suggested_actions.ml
@@ -0,0 +1,64 @@
+type suggested_action =
+ | ApplyAutomaticMigrationsForFullProject
+ | ApplyAutomaticMigrationsForCurrentFile
+
+type record = {loc: Warnings.loc; action: suggested_action}
+
+let suggestions : record list ref = ref []
+
+let file_of_record (r : record) = r.loc.loc_start.pos_fname
+
+let add r =
+ let exists =
+ List.exists
+ (fun x -> x.action = r.action && file_of_record x = file_of_record r)
+ !suggestions
+ in
+ if not exists then suggestions := r :: !suggestions
+
+let clear () = suggestions := []
+
+let has r =
+ List.exists
+ (fun x -> x.action = r.action && file_of_record x = file_of_record r)
+ !suggestions
+
+let action_id_json = function
+ | ApplyAutomaticMigrationsForFullProject ->
+ "\"ApplyAutomaticMigrationsForFullProject\""
+ | ApplyAutomaticMigrationsForCurrentFile ->
+ "\"ApplyAutomaticMigrationsForCurrentFile\""
+
+let print_block_if_any () =
+ match List.rev !suggestions with
+ | [] -> ()
+ | xs ->
+ let tag_name_of_action = function
+ | ApplyAutomaticMigrationsForCurrentFile -> "apply_all_migrations_in_file"
+ | ApplyAutomaticMigrationsForFullProject ->
+ "apply_all_migrations_in_project"
+ in
+ let description_of_action = function
+ | ApplyAutomaticMigrationsForCurrentFile ->
+ "Applies all automatic migrations in the current file."
+ | ApplyAutomaticMigrationsForFullProject ->
+ "Applies all automatic migrations in the project."
+ in
+ let print_entry ({loc; action} : record) =
+ let path =
+ let f = loc.loc_start.pos_fname in
+ if Filename.is_relative f then Ext_path.absolute_cwd_path f else f
+ in
+ let tag = tag_name_of_action action in
+ Printf.printf " <%s>\n" tag;
+ Printf.printf " %s\n"
+ (description_of_action action);
+ print_endline " ";
+ Printf.printf " perform-action %s %s\n" path (action_id_json action);
+ print_endline " ";
+ Printf.printf " %s>\n" tag
+ in
+ print_endline "";
+ List.iter print_entry xs;
+ print_endline "";
+ flush stdout
diff --git a/compiler/ext/suggested_actions.mli b/compiler/ext/suggested_actions.mli
new file mode 100644
index 0000000000..f98281939d
--- /dev/null
+++ b/compiler/ext/suggested_actions.mli
@@ -0,0 +1,10 @@
+type suggested_action =
+ | ApplyAutomaticMigrationsForFullProject
+ | ApplyAutomaticMigrationsForCurrentFile
+
+type record = {loc: Warnings.loc; action: suggested_action}
+
+val add : record -> unit
+val clear : unit -> unit
+val has : record -> bool
+val print_block_if_any : unit -> unit
diff --git a/compiler/ext/warnings.ml b/compiler/ext/warnings.ml
index f25b91b4f8..5debf3cb68 100644
--- a/compiler/ext/warnings.ml
+++ b/compiler/ext/warnings.ml
@@ -296,6 +296,8 @@ let reset () =
let () = reset ()
+let llm_mode = ref false
+
let message = function
| Comment_start -> "this is the start of a comment."
| Comment_not_end -> "this is not the end of a comment."
@@ -310,10 +312,15 @@ let message = function
^
if can_be_automigrated then
"\n\n\
- \ This can be automatically migrated by the ReScript migration tool. \
- Run `rescript-tools migrate-all ` to run all automatic \
- migrations available in your project, or `rescript-tools migrate \
- ` to migrate a single file."
+ \ This can be automatically migrated by the ReScript migration tool. "
+ ^
+ if !llm_mode then
+ "Run the perform-action MCP tool for automatic migration, suggested \
+ below, to fix this automatically."
+ else
+ "Run `rescript-tools migrate-all ` to run all automatic \
+ migrations available in your project, or `rescript-tools migrate \
+ ` to migrate a single file."
else ""
| Fragile_match "" -> "this pattern-matching is fragile."
| Fragile_match s ->
diff --git a/compiler/ext/warnings.mli b/compiler/ext/warnings.mli
index ba1a03ceec..b775472542 100644
--- a/compiler/ext/warnings.mli
+++ b/compiler/ext/warnings.mli
@@ -131,3 +131,5 @@ val loc_to_string : loc -> string
(**
Turn the location into a string with (line,column--line,column) format.
*)
+
+val llm_mode : bool ref
diff --git a/compiler/ml/code_frame.ml b/compiler/ml/code_frame.ml
index 9f75c765ac..0d62a6f9e8 100644
--- a/compiler/ml/code_frame.ml
+++ b/compiler/ml/code_frame.ml
@@ -104,7 +104,7 @@ end
let setup = Color.setup
-type gutter = Number of int | Elided
+type gutter = Number of int | Elided | UnderlinedRow
type highlighted_string = {s: string; start: int; end_: int}
type line = {gutter: gutter; content: highlighted_string list}
@@ -116,7 +116,7 @@ type line = {gutter: gutter; content: highlighted_string list}
- center snippet when it's heavily indented
- ellide intermediate lines when the reported range is huge
*)
-let print ~is_warning ~src ~(start_pos : Lexing.position)
+let print ~is_warning ~draw_underline ~src ~(start_pos : Lexing.position)
~(end_pos : Lexing.position) =
let indent = 2 in
let highlight_line_start_line = start_pos.pos_lnum in
@@ -134,27 +134,22 @@ let print ~is_warning ~src ~(start_pos : Lexing.position)
let max_line_digits_count = digits_count last_shown_line in
(* TODO: change this back to a fixed 100? *)
(* 3 for separator + the 2 spaces around it *)
- let line_width = 78 - max_line_digits_count - indent - 3 in
+ let line_width = max 1 (78 - max_line_digits_count - indent - 3) in
let lines =
- if
- start_line_line_offset >= 0
- && end_line_line_end_offset >= start_line_line_offset
- then
- String.sub src start_line_line_offset
- (end_line_line_end_offset - start_line_line_offset)
- |> String.split_on_char '\n'
- |> filter_mapi (fun i line ->
- let line_number = i + first_shown_line in
- if more_than_5_highlighted_lines then
- if line_number = highlight_line_start_line + 2 then
- Some (Elided, line)
- else if
- line_number > highlight_line_start_line + 2
- && line_number < highlight_line_end_line - 1
- then None
- else Some (Number line_number, line)
- else Some (Number line_number, line))
- else []
+ String.sub src start_line_line_offset
+ (end_line_line_end_offset - start_line_line_offset)
+ |> String.split_on_char '\n'
+ |> filter_mapi (fun i line ->
+ let line_number = i + first_shown_line in
+ if more_than_5_highlighted_lines then
+ if line_number = highlight_line_start_line + 2 then
+ Some (Elided, line)
+ else if
+ line_number > highlight_line_start_line + 2
+ && line_number < highlight_line_end_line - 1
+ then None
+ else Some (Number line_number, line)
+ else Some (Number line_number, line))
in
let leading_space_to_cut =
lines
@@ -178,41 +173,98 @@ let print ~is_warning ~src ~(start_pos : Lexing.position)
String.sub line leading_space_to_cut
(String.length line - leading_space_to_cut)
|> break_long_line line_width
- |> List.mapi (fun i line ->
+ |> List.mapi (fun i chunk ->
match gutter with
- | Elided -> {s = line; start = 0; end_ = 0}
+ | Elided | UnderlinedRow ->
+ {s = chunk; start = 0; end_ = 0}
| Number line_number ->
- let highlight_line_start_offset =
+ let hl_start_off =
start_pos.pos_cnum - start_pos.pos_bol
in
- let highlight_line_end_offset =
- end_pos.pos_cnum - end_pos.pos_bol
+ let hl_end_off = end_pos.pos_cnum - end_pos.pos_bol in
+ (* Offsets within the trimmed line (after leading-space cut) *)
+ let trimmed_hl_start =
+ if line_number = highlight_line_start_line then
+ max 0 (hl_start_off - leading_space_to_cut)
+ else if line_number > highlight_line_start_line then 0
+ else (* before start line *) max_int
in
- let start =
- if i = 0 && line_number = highlight_line_start_line
- then
- highlight_line_start_offset - leading_space_to_cut
+ let trimmed_hl_end =
+ if line_number < highlight_line_start_line then 0
+ else if line_number = highlight_line_start_line
+ && line_number = highlight_line_end_line
+ then max 0 (hl_end_off - leading_space_to_cut)
+ else if line_number = highlight_line_start_line then
+ (* highlight runs through end of this line *)
+ String.length chunk (* placeholder; refined below *)
+ else if line_number < highlight_line_end_line then
+ (* full line highlight for interior lines *)
+ String.length chunk (* placeholder; refined below *)
+ else if line_number = highlight_line_end_line then
+ max 0 (hl_end_off - leading_space_to_cut)
else 0
in
- let end_ =
- if line_number < highlight_line_start_line then 0
- else if
+ (* Map highlight to this chunk using its offset *)
+ let chunk_offset = i * line_width in
+ let clen = String.length chunk in
+ let start_rel =
+ if trimmed_hl_start = max_int then 0
+ else trimmed_hl_start - chunk_offset
+ in
+ let end_rel =
+ if
line_number = highlight_line_start_line
- && line_number = highlight_line_end_line
- then highlight_line_end_offset - leading_space_to_cut
- else if line_number = highlight_line_start_line then
- String.length line
+ && line_number <> highlight_line_end_line
+ && i = 0
+ then clen
else if
line_number > highlight_line_start_line
&& line_number < highlight_line_end_line
- then String.length line
- else if line_number = highlight_line_end_line then
- highlight_line_end_offset - leading_space_to_cut
- else 0
+ then clen
+ else trimmed_hl_end - chunk_offset
in
- {s = line; start; end_})
+ let start = max 0 (min clen start_rel) in
+ let end_ = max 0 (min clen end_rel) in
+ let start, end_ =
+ if start >= end_ then (0, 0) else (start, end_)
+ in
+ {s = chunk; start; end_})
in
- {gutter; content = new_content})
+ if draw_underline then
+ let has_highlight =
+ List.exists (fun {start; end_} -> start < end_) new_content
+ in
+ if has_highlight then
+ let underline_content =
+ List.map
+ (fun {start; end_} ->
+ if start < end_ then
+ let overline_char = "^" in
+ let underline_length = end_ - start in
+ let underline =
+ String.concat ""
+ (List.init underline_length (fun _ -> overline_char))
+ in
+ [
+ {
+ s = String.make start ' ' ^ underline;
+ start = 0;
+ end_ = 0;
+ };
+ ]
+ else [{s = ""; start = 0; end_ = 0}])
+ new_content
+ in
+ [
+ {gutter; content = new_content};
+ {
+ gutter = UnderlinedRow;
+ content = List.flatten underline_content;
+ };
+ ]
+ else [{gutter; content = new_content}]
+ else [{gutter; content = new_content}])
+ |> List.flatten
in
let buf = Buffer.create 100 in
let open Color in
@@ -280,5 +332,11 @@ let print ~is_warning ~src ~(start_pos : Lexing.position)
else NoColor
in
add_ch c ch);
+ add_ch NoColor '\n')
+ | UnderlinedRow ->
+ content
+ |> List.iter (fun line ->
+ draw_gutter NoColor "";
+ line.s |> String.iter (fun ch -> add_ch NoColor ch);
add_ch NoColor '\n'));
Buffer.contents buf
diff --git a/compiler/ml/location.ml b/compiler/ml/location.ml
index fa2e806db0..141bac0505 100644
--- a/compiler/ml/location.ml
+++ b/compiler/ml/location.ml
@@ -39,6 +39,8 @@ let show_filename file = if file = "_none_" then !input_name else file
let print_filename ppf file = Format.fprintf ppf "%s" (show_filename file)
+let draw_underline_in_code_frame = ref false
+
(* return file, line, char from the given position *)
let get_pos_info pos = (pos.pos_fname, pos.pos_lnum, pos.pos_cnum - pos.pos_bol)
@@ -140,8 +142,10 @@ let print ?(src = None) ~message_kind intro ppf (loc : t) =
branch might not be reached (aka no inline file content display) so
we don't wanna end up with two line breaks in the the consequent *)
fprintf ppf "@,%s"
- (Code_frame.print ~is_warning:(message_kind = `warning) ~src
- ~start_pos:loc.loc_start ~end_pos:loc.loc_end)
+ (Code_frame.print
+ ~draw_underline:!draw_underline_in_code_frame
+ ~is_warning:(message_kind = `warning) ~src ~start_pos:loc.loc_start
+ ~end_pos:loc.loc_end)
with
(* this might happen if the file is e.g. "", "_none_" or any of the fake file name placeholders.
we've already printed the location above, so nothing more to do here. *)
@@ -300,6 +304,14 @@ let raise_errorf ?(loc = none) ?(sub = []) ?(if_highlight = "") =
let deprecated ?(can_be_automigrated = false) ?(def = none) ?(use = none) loc
msg =
+ if
+ can_be_automigrated && !Warnings.llm_mode && (not loc.loc_ghost)
+ && loc.loc_start.pos_fname <> "_none_"
+ then (
+ Suggested_actions.add
+ {loc; action = Suggested_actions.ApplyAutomaticMigrationsForCurrentFile};
+ Suggested_actions.add
+ {loc; action = Suggested_actions.ApplyAutomaticMigrationsForFullProject});
prerr_warning loc (Warnings.Deprecated (msg, def, use, can_be_automigrated))
let map_loc f {txt; loc} = {txt = f txt; loc}
diff --git a/compiler/ml/location.mli b/compiler/ml/location.mli
index 76f4db2bd8..400be9a36f 100644
--- a/compiler/ml/location.mli
+++ b/compiler/ml/location.mli
@@ -42,6 +42,8 @@ val set_input_name : string -> unit
val get_pos_info : Lexing.position -> string * int * int (* file, line, char *)
val print_loc : formatter -> t -> unit
+val draw_underline_in_code_frame : bool ref
+
val prerr_warning : t -> Warnings.t -> unit
val warning_printer : (t -> formatter -> Warnings.t -> unit) ref
diff --git a/rewatch/Cargo.lock b/rewatch/Cargo.lock
index f79d2871b9..f61c2b3392 100644
--- a/rewatch/Cargo.lock
+++ b/rewatch/Cargo.lock
@@ -24,6 +24,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "anstream"
version = "0.6.19"
@@ -92,6 +101,29 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -123,6 +155,12 @@ version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
[[package]]
name = "cc"
version = "1.2.28"
@@ -144,6 +182,20 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+[[package]]
+name = "chrono"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link",
+]
+
[[package]]
name = "clap"
version = "4.5.40"
@@ -278,6 +330,32 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "deranged"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
[[package]]
name = "either"
version = "1.15.0"
@@ -331,6 +409,15 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
[[package]]
name = "fsevent-sys"
version = "4.1.0"
@@ -465,6 +552,132 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
+[[package]]
+name = "iana-time-zone"
+version = "0.1.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
+
+[[package]]
+name = "icu_properties"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
+
+[[package]]
+name = "icu_provider"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
[[package]]
name = "indicatif"
version = "0.17.11"
@@ -551,6 +764,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
[[package]]
name = "libc"
version = "0.2.174"
@@ -574,12 +793,94 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+[[package]]
+name = "litemap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "mcp-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a98bb059ac88c0823eca2b83d0971b15c6ea361dba44dcbdd3d301103baab0b6"
+dependencies = [
+ "async-trait",
+ "convert_case",
+ "mcp-spec",
+ "proc-macro2",
+ "quote",
+ "schemars",
+ "serde",
+ "serde_json",
+ "syn",
+]
+
+[[package]]
+name = "mcp-server"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d1d33bbd1f184d136aedf02a21e1342b2a527400a513c07d4fd30846e978039"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "futures",
+ "mcp-macros",
+ "mcp-spec",
+ "pin-project",
+ "schemars",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tower",
+ "tower-service",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "mcp-spec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a294f4fcf9c3a0b3d168937947ecb906feb691089dd9fbbefc9707c8a4557163"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "base64",
+ "chrono",
+ "schemars",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "url",
+]
+
[[package]]
name = "memchr"
version = "2.7.5"
@@ -598,6 +899,17 @@ dependencies = [
"windows-sys 0.48.0",
]
+[[package]]
+name = "mio"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
+dependencies = [
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "nix"
version = "0.30.1"
@@ -623,7 +935,7 @@ dependencies = [
"inotify",
"kqueue",
"libc",
- "mio",
+ "mio 0.8.11",
"serde",
"walkdir",
"windows-sys 0.45.0",
@@ -638,6 +950,30 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "num_cpus"
version = "1.17.0"
@@ -666,6 +1002,55 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.16"
@@ -684,6 +1069,21 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
+[[package]]
+name = "potential_utf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
[[package]]
name = "proc-macro2"
version = "1.0.95"
@@ -783,6 +1183,8 @@ dependencies = [
"futures-timer",
"indicatif",
"log",
+ "mcp-server",
+ "mcp-spec",
"notify",
"num_cpus",
"rayon",
@@ -791,6 +1193,7 @@ dependencies = [
"serde_json",
"sysinfo",
"tempfile",
+ "tokio",
]
[[package]]
@@ -806,6 +1209,12 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
[[package]]
name = "ryu"
version = "1.0.20"
@@ -821,6 +1230,36 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "schemars"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
[[package]]
name = "serde"
version = "1.0.219"
@@ -841,6 +1280,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "serde_derive_internals"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "serde_json"
version = "1.0.140"
@@ -853,6 +1303,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -860,11 +1319,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
-name = "slab"
+name = "signal-hook-registry"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
+dependencies = [
+ "libc",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -882,6 +1372,17 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "sysinfo"
version = "0.29.11"
@@ -919,6 +1420,203 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "time"
+version = "0.3.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+
+[[package]]
+name = "time-macros"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokio"
+version = "1.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio 1.1.0",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -937,12 +1635,36 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
+[[package]]
+name = "url"
+version = "2.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
[[package]]
name = "version_check"
version = "0.9.5"
@@ -982,6 +1704,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
+ "rustversion",
"wasm-bindgen-macro",
]
@@ -1072,6 +1795,65 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
[[package]]
name = "windows-sys"
version = "0.45.0"
@@ -1099,6 +1881,24 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
[[package]]
name = "windows-targets"
version = "0.42.2"
@@ -1138,13 +1938,30 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
+ "windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@@ -1163,6 +1980,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -1181,6 +2004,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -1199,12 +2028,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -1223,6 +2064,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -1241,6 +2088,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -1259,6 +2112,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -1277,6 +2136,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
@@ -1286,6 +2151,35 @@ dependencies = [
"bitflags 2.9.1",
]
+[[package]]
+name = "writeable"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+
+[[package]]
+name = "yoke"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.26"
@@ -1305,3 +2199,57 @@ dependencies = [
"quote",
"syn",
]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/rewatch/Cargo.toml b/rewatch/Cargo.toml
index df5f6cd6bc..11e99208b2 100644
--- a/rewatch/Cargo.toml
+++ b/rewatch/Cargo.toml
@@ -26,6 +26,9 @@ serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93" }
sysinfo = "0.29.10"
tempfile = "3.10.1"
+mcp-server = "0.1.0"
+mcp-spec = "0.1.0"
+tokio = { version = "1", features = ["rt-multi-thread", "io-std"] }
[profile.release]
diff --git a/rewatch/src/build/compile.rs b/rewatch/src/build/compile.rs
index 8048764f09..aaaa8d1825 100644
--- a/rewatch/src/build/compile.rs
+++ b/rewatch/src/build/compile.rs
@@ -436,39 +436,17 @@ pub fn compiler_args(
// Command-line --warn-error flag override (takes precedence over rescript.json config)
warn_error_override: Option,
) -> Result> {
- let bsc_flags = config::flatten_flags(&config.compiler_flags);
- let dependency_paths = get_dependency_paths(config, project_context, packages, is_type_dev);
let module_name = helpers::file_path_to_module_name(file_path, &config.get_namespace());
-
- let namespace_args = match &config.get_namespace() {
- packages::Namespace::NamespaceWithEntry { namespace: _, entry } if &module_name == entry => {
- // if the module is the entry we just want to open the namespace
- vec![
- "-open".to_string(),
- config.get_namespace().to_suffix().unwrap().to_string(),
- ]
- }
- packages::Namespace::Namespace(_)
- | packages::Namespace::NamespaceWithEntry {
- namespace: _,
- entry: _,
- } => {
- vec![
- "-bs-ns".to_string(),
- config.get_namespace().to_suffix().unwrap().to_string(),
- ]
- }
- packages::Namespace::NoNamespace => vec![],
- };
-
- let root_config = project_context.get_root_config();
- let jsx_args = root_config.get_jsx_args();
- let jsx_module_args = root_config.get_jsx_module_args();
- let jsx_mode_args = root_config.get_jsx_mode_args();
- let jsx_preserve_args = root_config.get_jsx_preserve_args();
- let gentype_arg = config.get_gentype_arg();
- let experimental_args = root_config.get_experimental_features_args();
- let warning_args = config.get_warning_args(is_local_dep, warn_error_override);
+ let base_args = base_compile_args(
+ config,
+ file_path,
+ project_context,
+ packages,
+ is_type_dev,
+ is_local_dep,
+ warn_error_override,
+ None,
+ )?;
let read_cmi_args = match has_interface {
true => {
@@ -483,6 +461,7 @@ pub fn compiler_args(
let package_name_arg = vec!["-bs-package-name".to_string(), config.name.to_owned()];
+ let root_config = project_context.get_root_config();
let implementation_args = if is_interface {
debug!("Compiling interface file: {}", &module_name);
vec![]
@@ -517,11 +496,117 @@ pub fn compiler_args(
.collect()
};
+ Ok([
+ base_args,
+ read_cmi_args,
+ // vec!["-warn-error".to_string(), "A".to_string()],
+ // ^^ this one fails for bisect-ppx
+ // this is the default
+ // we should probably parse the right ones from the package config
+ // vec!["-w".to_string(), "a".to_string()],
+ package_name_arg,
+ implementation_args,
+ vec![ast_path.to_string_lossy().to_string()],
+ ]
+ .concat())
+}
+
+pub fn compiler_args_for_diagnostics(
+ config: &config::Config,
+ file_path: &Path,
+ is_interface: bool,
+ has_interface: bool,
+ project_context: &ProjectContext,
+ packages: &Option<&AHashMap>,
+ is_type_dev: bool,
+ is_local_dep: bool,
+ warn_error_override: Option,
+ ppx_flags: Vec,
+) -> Result> {
+ let mut args = base_compile_args(
+ config,
+ file_path,
+ project_context,
+ packages,
+ is_type_dev,
+ is_local_dep,
+ warn_error_override,
+ Some(ppx_flags),
+ )?;
+
+ // Gate -bs-read-cmi by .cmi presence to avoid noisy errors when a project hasn't been built yet.
+ if has_interface && !is_interface {
+ let pkg_root = config
+ .path
+ .parent()
+ .map(|p| p.to_path_buf())
+ .unwrap_or_else(|| Path::new(".").to_path_buf());
+ let ocaml_build_path = packages::get_ocaml_build_path(&pkg_root);
+ let basename = helpers::file_path_to_compiler_asset_basename(file_path, &config.get_namespace());
+ let cmi_exists = ocaml_build_path.join(format!("{basename}.cmi")).exists();
+ if cmi_exists {
+ args.push("-bs-read-cmi".to_string());
+ }
+ }
+
+ args.extend([
+ "-color".to_string(),
+ "never".to_string(),
+ "-ignore-parse-errors".to_string(),
+ "-editor-mode".to_string(),
+ "-llm-mode".to_string(),
+ ]);
+
+ args.push(file_path.to_string_lossy().to_string());
+ Ok(args)
+}
+
+fn base_compile_args(
+ config: &config::Config,
+ file_path: &Path,
+ project_context: &ProjectContext,
+ packages: &Option<&AHashMap>,
+ is_type_dev: bool,
+ is_local_dep: bool,
+ warn_error_override: Option,
+ include_ppx_flags: Option>,
+) -> Result> {
+ let bsc_flags = config::flatten_flags(&config.compiler_flags);
+ let dependency_paths = get_dependency_paths(config, project_context, packages, is_type_dev);
+ let module_name = helpers::file_path_to_module_name(file_path, &config.get_namespace());
+
+ let namespace_args = match &config.get_namespace() {
+ packages::Namespace::NamespaceWithEntry { namespace: _, entry } if &module_name == entry => {
+ vec![
+ "-open".to_string(),
+ config.get_namespace().to_suffix().unwrap().to_string(),
+ ]
+ }
+ packages::Namespace::Namespace(_)
+ | packages::Namespace::NamespaceWithEntry {
+ namespace: _,
+ entry: _,
+ } => {
+ vec![
+ "-bs-ns".to_string(),
+ config.get_namespace().to_suffix().unwrap().to_string(),
+ ]
+ }
+ packages::Namespace::NoNamespace => vec![],
+ };
+
+ let root_config = project_context.get_root_config();
+ let jsx_args = root_config.get_jsx_args();
+ let jsx_module_args = root_config.get_jsx_module_args();
+ let jsx_mode_args = root_config.get_jsx_mode_args();
+ let jsx_preserve_args = root_config.get_jsx_preserve_args();
+ let gentype_arg = config.get_gentype_arg();
+ let experimental_args = root_config.get_experimental_features_args();
+ let warning_args = config.get_warning_args(is_local_dep, warn_error_override);
let runtime_path_args = get_runtime_path_args(config, project_context)?;
Ok(vec![
namespace_args,
- read_cmi_args,
vec![
"-I".to_string(),
Path::new("..").join("ocaml").to_string_lossy().to_string(),
@@ -532,22 +617,11 @@ pub fn compiler_args(
jsx_module_args,
jsx_mode_args,
jsx_preserve_args,
+ include_ppx_flags.unwrap_or_default(),
bsc_flags.to_owned(),
warning_args,
gentype_arg,
experimental_args,
- // vec!["-warn-error".to_string(), "A".to_string()],
- // ^^ this one fails for bisect-ppx
- // this is the default
- // we should probably parse the right ones from the package config
- // vec!["-w".to_string(), "a".to_string()],
- package_name_arg,
- implementation_args,
- // vec![
- // "-I".to_string(),
- // abs_node_modules_path.to_string() + "/rescript/ocaml",
- // ],
- vec![ast_path.to_string_lossy().to_string()],
]
.concat())
}
diff --git a/rewatch/src/build/parse.rs b/rewatch/src/build/parse.rs
index 22ab88ff46..6186055be4 100644
--- a/rewatch/src/build/parse.rs
+++ b/rewatch/src/build/parse.rs
@@ -284,11 +284,7 @@ pub fn parser_args(
let root_config = project_context.get_root_config();
let file = &filename;
let ast_path = helpers::get_ast_path(file);
- let ppx_flags = config::flatten_ppx_flags(
- project_context,
- package_config,
- &filter_ppx_flags(&package_config.ppx_flags, contents),
- )?;
+ let ppx_flags = ppx_flags_for_contents(project_context, package_config, contents)?;
let jsx_args = root_config.get_jsx_args();
let jsx_module_args = root_config.get_jsx_module_args();
let jsx_mode_args = root_config.get_jsx_mode_args();
@@ -322,6 +318,15 @@ pub fn parser_args(
))
}
+pub fn ppx_flags_for_contents(
+ project_context: &ProjectContext,
+ package_config: &Config,
+ contents: &str,
+) -> anyhow::Result> {
+ let filtered = filter_ppx_flags(&package_config.ppx_flags, contents);
+ config::flatten_ppx_flags(project_context, package_config, &filtered)
+}
+
fn generate_ast(
package: Package,
filename: &Path,
diff --git a/rewatch/src/cli.rs b/rewatch/src/cli.rs
index 3b4604ce54..fa77347a14 100644
--- a/rewatch/src/cli.rs
+++ b/rewatch/src/cli.rs
@@ -425,6 +425,8 @@ pub enum Command {
Build(BuildArgs),
/// Build, then start a watcher
Watch(WatchArgs),
+ /// Start a Model Context Protocol (MCP) server over stdio
+ Mcp {},
/// Clean the build artifacts
Clean {
#[command(flatten)]
diff --git a/rewatch/src/lib.rs b/rewatch/src/lib.rs
index a389e8172e..b4b9dc6d16 100644
--- a/rewatch/src/lib.rs
+++ b/rewatch/src/lib.rs
@@ -5,6 +5,7 @@ pub mod config;
pub mod format;
pub mod helpers;
pub mod lock;
+pub mod mcp;
pub mod project_context;
pub mod queue;
pub mod sourcedirs;
diff --git a/rewatch/src/main.rs b/rewatch/src/main.rs
index ba74dbd393..c72f4cf08f 100644
--- a/rewatch/src/main.rs
+++ b/rewatch/src/main.rs
@@ -3,17 +3,23 @@ use console::Term;
use log::LevelFilter;
use std::{io::Write, path::Path};
-use rescript::{build, cli, cmd, format, lock, watcher};
+use rescript::{build, cli, cmd, format, lock, mcp, watcher};
fn main() -> Result<()> {
let cli = cli::parse_with_default().unwrap_or_else(|err| err.exit());
let log_level_filter = cli.verbose.log_level_filter();
+ // Route logs to stderr when running the MCP server to keep stdout as a pure JSON-RPC stream.
+ let logs_to_stdout = !matches!(cli.command, cli::Command::Mcp { .. });
env_logger::Builder::new()
.format(|buf, record| writeln!(buf, "{}:\n{}", record.level(), record.args()))
.filter_level(log_level_filter)
- .target(env_logger::fmt::Target::Stdout)
+ .target(if logs_to_stdout {
+ env_logger::fmt::Target::Stdout
+ } else {
+ env_logger::fmt::Target::Stderr
+ })
.init();
let mut command = cli.command;
@@ -37,6 +43,13 @@ fn main() -> Result<()> {
println!("{}", build::get_compiler_args(Path::new(&path))?);
std::process::exit(0);
}
+ cli::Command::Mcp {} => {
+ if let Err(e) = mcp::run() {
+ println!("{e}");
+ std::process::exit(1);
+ }
+ std::process::exit(0);
+ }
cli::Command::Build(build_args) => {
let _lock = get_lock(&build_args.folder);
diff --git a/rewatch/src/mcp.rs b/rewatch/src/mcp.rs
new file mode 100644
index 0000000000..4ab396470c
--- /dev/null
+++ b/rewatch/src/mcp.rs
@@ -0,0 +1,385 @@
+use anyhow::Result;
+use mcp_server::router::{CapabilitiesBuilder, Router, RouterService};
+use mcp_server::{ByteTransport, Server};
+use mcp_spec::{
+ content::Content, prompt::Prompt, protocol::ServerCapabilities, resource::Resource, tool::Tool,
+};
+use serde_json::json;
+use std::path::Path;
+use std::pin::Pin;
+use std::process::Command;
+
+use crate::{build, helpers, project_context::ProjectContext};
+use serde::Deserialize;
+
+// Suggested actions mirror compiler/ext/suggested_actions.ml
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+enum SuggestedAction {
+ ApplyAutomaticMigrationsForFullProject,
+ ApplyAutomaticMigrationsForCurrentFile,
+}
+
+#[derive(Clone)]
+pub struct RewatchMcp;
+
+impl RewatchMcp {}
+
+impl Router for RewatchMcp {
+ fn name(&self) -> String {
+ "rescript-mcp".to_string()
+ }
+
+ fn instructions(&self) -> String {
+ "ReScript MCP server.\n\n\
+ Tools:\n\
+ - diagnose(path): Quick per-file diagnostics after edits. Prefer this before a full build.\n\
+ No writes. Only this file (not dependents). Returns 'OK' if clean.\n\
+ - perform-action(path, actionId): Applies an action suggested by the compiler/diagnosis."
+ .to_string()
+ }
+
+ fn capabilities(&self) -> ServerCapabilities {
+ CapabilitiesBuilder::new().with_tools(false).build()
+ }
+
+ fn list_tools(&self) -> Vec {
+ vec![
+ Tool::new(
+ "diagnose",
+ "Quick per-file diagnostics (shows compiler warnings, errors) after edits; prefer before full build; no writes; only this file.",
+ json!({
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ "additionalProperties": false
+ }),
+ ),
+ Tool::new(
+ "perform-action",
+ "Applies an action suggested by the compiler/diagnosis.",
+ json!({
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ // Accept either a string or an object so clients can pass raw JSON, not stringified JSON
+ "actionId": {"anyOf": [{"type": "string"}, {"type": "object"}]}
+ },
+ "required": ["path", "actionId"],
+ "additionalProperties": false
+ }),
+ ),
+ ]
+ }
+
+ fn call_tool(
+ &self,
+ tool_name: &str,
+ arguments: serde_json::Value,
+ ) -> Pin<
+ Box<
+ dyn futures::Future