rubydex 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/THIRD_PARTY_LICENSES.html +132 -1378
- data/exe/rdx +28 -2
- data/ext/rubydex/extconf.rb +0 -8
- data/ext/rubydex/graph.c +43 -29
- data/lib/rubydex/mcp_server/protocol.rb +156 -0
- data/lib/rubydex/mcp_server/tools/base_tool.rb +109 -0
- data/lib/rubydex/mcp_server/tools/codebase_stats_tool.rb +30 -0
- data/lib/rubydex/mcp_server/tools/find_constant_references_tool.rb +46 -0
- data/lib/rubydex/mcp_server/tools/get_declaration_tool.rb +68 -0
- data/lib/rubydex/mcp_server/tools/get_descendants_tool.rb +46 -0
- data/lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb +55 -0
- data/lib/rubydex/mcp_server/tools/search_declarations_tool.rb +55 -0
- data/lib/rubydex/mcp_server.rb +219 -0
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +8 -8
- data/rust/Cargo.lock +1 -555
- data/rust/Cargo.toml +0 -1
- data/rust/rubydex/src/config.rs +54 -34
- data/rust/rubydex/src/listing.rs +159 -102
- data/rust/rubydex/src/main.rs +148 -5
- data/rust/rubydex/src/model/declaration.rs +14 -14
- data/rust/rubydex/src/model/graph.rs +92 -9
- data/rust/rubydex/src/model/ids.rs +30 -0
- data/rust/rubydex/src/model/string_ref.rs +12 -4
- data/rust/rubydex/src/operation/applier.rs +0 -2
- data/rust/rubydex/src/query.rs +83 -15
- data/rust/rubydex/src/resolution.rs +196 -139
- data/rust/rubydex/tests/cli.rs +66 -0
- data/rust/rubydex-sys/src/declaration_api.rs +2 -2
- data/rust/rubydex-sys/src/graph_api.rs +71 -56
- metadata +11 -10
- data/exe/rubydex_mcp +0 -17
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/rust/rubydex-mcp/Cargo.toml +0 -34
- data/rust/rubydex-mcp/src/main.rs +0 -48
- data/rust/rubydex-mcp/src/server.rs +0 -1148
- data/rust/rubydex-mcp/src/tools.rs +0 -49
- data/rust/rubydex-mcp/tests/mcp.rs +0 -302
data/rust/rubydex/tests/cli.rs
CHANGED
|
@@ -19,6 +19,9 @@ fn prints_help() {
|
|
|
19
19
|
.success()
|
|
20
20
|
.stdout(predicate::str::contains("A Static Analysis Toolkit for Ruby"))
|
|
21
21
|
.stdout(predicate::str::contains("Usage:"))
|
|
22
|
+
.stdout(predicate::str::contains(
|
|
23
|
+
"If the first path is a directory, it is used as the workspace root for rubydex.toml",
|
|
24
|
+
))
|
|
22
25
|
.stdout(predicate::str::contains("--stats"))
|
|
23
26
|
.stdout(predicate::str::contains("--dot"))
|
|
24
27
|
.stdout(predicate::str::contains("--stop-after"));
|
|
@@ -52,6 +55,69 @@ fn paths_argument_variants() {
|
|
|
52
55
|
});
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
#[test]
|
|
59
|
+
fn single_directory_argument_is_workspace_root_for_config() {
|
|
60
|
+
with_context(|context| {
|
|
61
|
+
context.write("included.rb", "class Included\nend\n");
|
|
62
|
+
context.write("excluded/skipped.rb", "class Skipped\nend\n");
|
|
63
|
+
context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
|
|
64
|
+
|
|
65
|
+
rdx(&[context.absolute_path().to_str().unwrap()])
|
|
66
|
+
.success()
|
|
67
|
+
.stderr(predicate::str::is_empty())
|
|
68
|
+
.stdout(predicate::str::contains("Indexed 2 files"));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#[test]
|
|
73
|
+
fn first_directory_argument_is_workspace_root_for_config() {
|
|
74
|
+
with_context(|context| {
|
|
75
|
+
context.write("included/kept.rb", "class Included\nend\n");
|
|
76
|
+
context.write("excluded/skipped.rb", "class Skipped\nend\n");
|
|
77
|
+
context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
|
|
78
|
+
|
|
79
|
+
rdx(&[
|
|
80
|
+
context.absolute_path().to_str().unwrap(),
|
|
81
|
+
context.absolute_path_to("included").to_str().unwrap(),
|
|
82
|
+
context.absolute_path_to("excluded").to_str().unwrap(),
|
|
83
|
+
])
|
|
84
|
+
.success()
|
|
85
|
+
.stderr(predicate::str::is_empty())
|
|
86
|
+
.stdout(predicate::str::contains("Indexed 2 files"));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn single_file_argument_is_not_used_as_workspace_root() {
|
|
92
|
+
with_context(|context| {
|
|
93
|
+
context.write("included.rb", "class Included\nend\n");
|
|
94
|
+
context.write("excluded/skipped.rb", "class Skipped\nend\n");
|
|
95
|
+
context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
|
|
96
|
+
|
|
97
|
+
rdx(&[context.absolute_path_to("excluded/skipped.rb").to_str().unwrap()])
|
|
98
|
+
.success()
|
|
99
|
+
.stderr(predicate::str::is_empty())
|
|
100
|
+
.stdout(predicate::str::contains("Indexed 2 files"));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#[test]
|
|
105
|
+
fn first_file_argument_is_not_used_as_workspace_root() {
|
|
106
|
+
with_context(|context| {
|
|
107
|
+
context.write("included.rb", "class Included\nend\n");
|
|
108
|
+
context.write("excluded/skipped.rb", "class Skipped\nend\n");
|
|
109
|
+
context.write("rubydex.toml", "exclude = [\"excluded\"]\n");
|
|
110
|
+
|
|
111
|
+
rdx(&[
|
|
112
|
+
context.absolute_path_to("included.rb").to_str().unwrap(),
|
|
113
|
+
context.absolute_path_to("excluded").to_str().unwrap(),
|
|
114
|
+
])
|
|
115
|
+
.success()
|
|
116
|
+
.stderr(predicate::str::is_empty())
|
|
117
|
+
.stdout(predicate::str::contains("Indexed 3 files"));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
55
121
|
#[test]
|
|
56
122
|
fn prints_index_metrics() {
|
|
57
123
|
with_context(|context| {
|
|
@@ -9,7 +9,7 @@ use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
|
|
|
9
9
|
use crate::graph_api::{GraphPointer, with_graph};
|
|
10
10
|
use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
|
|
11
11
|
use crate::utils;
|
|
12
|
-
use rubydex::model::ids::{DeclarationId, StringId};
|
|
12
|
+
use rubydex::model::ids::{DeclarationId, StringId, declaration_id_from_lookup_name};
|
|
13
13
|
|
|
14
14
|
#[repr(C)]
|
|
15
15
|
#[derive(Debug, Clone, Copy)]
|
|
@@ -80,7 +80,7 @@ pub(crate) unsafe fn decl_id_from_char_ptr(ptr: *const c_char) -> Option<Declara
|
|
|
80
80
|
if s.is_empty() {
|
|
81
81
|
return None;
|
|
82
82
|
}
|
|
83
|
-
Some(
|
|
83
|
+
Some(declaration_id_from_lookup_name(&s))
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/// An iterator over declaration IDs
|
|
@@ -11,7 +11,7 @@ use rubydex::errors::Errors;
|
|
|
11
11
|
use rubydex::indexing::LanguageId;
|
|
12
12
|
use rubydex::model::encoding::Encoding;
|
|
13
13
|
use rubydex::model::graph::Graph;
|
|
14
|
-
use rubydex::model::ids::{DeclarationId, NameId, UriId};
|
|
14
|
+
use rubydex::model::ids::{DeclarationId, NameId, UriId, declaration_id_from_lookup_name};
|
|
15
15
|
use rubydex::model::keywords;
|
|
16
16
|
use rubydex::model::name::NameRef;
|
|
17
17
|
use rubydex::model::visibility::Visibility;
|
|
@@ -58,64 +58,75 @@ where
|
|
|
58
58
|
result
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/// Searches the graph using exact substring matching
|
|
61
|
+
/// Searches the graph using exact substring matching, returning every declaration whose name matches any of the
|
|
62
|
+
/// queries.
|
|
62
63
|
///
|
|
63
64
|
/// # Safety
|
|
64
65
|
///
|
|
65
|
-
/// Expects
|
|
66
|
+
/// Expects `pointer` to be a valid graph and `c_queries` to point to an array of `count` valid, NUL-terminated C
|
|
67
|
+
/// strings. Returns a null pointer when `count` is zero, `c_queries` is null, or any query is not valid UTF-8.
|
|
66
68
|
#[unsafe(no_mangle)]
|
|
67
69
|
pub unsafe extern "C" fn rdx_graph_declarations_search(
|
|
68
70
|
pointer: GraphPointer,
|
|
69
|
-
|
|
71
|
+
c_queries: *const *const c_char,
|
|
72
|
+
count: usize,
|
|
70
73
|
) -> *mut DeclarationsIter {
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
74
|
+
if count == 0 || c_queries.is_null() {
|
|
75
|
+
return ptr::null_mut();
|
|
76
|
+
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let decl = graph.declarations().get(&id)?;
|
|
81
|
-
Some(CDeclaration::from_declaration(id, decl))
|
|
82
|
-
})
|
|
83
|
-
.collect::<Vec<CDeclaration>>()
|
|
84
|
-
.into_boxed_slice()
|
|
85
|
-
});
|
|
78
|
+
let Ok(queries) = (unsafe { utils::convert_double_pointer_to_vec(c_queries, count) }) else {
|
|
79
|
+
return ptr::null_mut();
|
|
80
|
+
};
|
|
81
|
+
let query_refs: Vec<&str> = queries.iter().map(String::as_str).collect();
|
|
86
82
|
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
let entries = with_graph(pointer, |graph| {
|
|
84
|
+
query::declaration_search(graph, &query_refs, &query::MatchMode::Exact)
|
|
85
|
+
.into_iter()
|
|
86
|
+
.filter_map(|id| {
|
|
87
|
+
let decl = graph.declarations().get(&id)?;
|
|
88
|
+
Some(CDeclaration::from_declaration(id, decl))
|
|
89
|
+
})
|
|
90
|
+
.collect::<Vec<CDeclaration>>()
|
|
91
|
+
.into_boxed_slice()
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
DeclarationsIter::new(entries)
|
|
89
95
|
}
|
|
90
96
|
|
|
91
|
-
/// Searches the graph using fuzzy matching
|
|
97
|
+
/// Searches the graph using fuzzy matching, returning every declaration whose name matches any of the queries.
|
|
92
98
|
///
|
|
93
99
|
/// # Safety
|
|
94
100
|
///
|
|
95
|
-
/// Expects
|
|
101
|
+
/// Expects `pointer` to be a valid graph and `c_queries` to point to an array of `count` valid, NUL-terminated C
|
|
102
|
+
/// strings. Returns a null pointer when `count` is zero, `c_queries` is null, or any query is not valid UTF-8.
|
|
96
103
|
#[unsafe(no_mangle)]
|
|
97
104
|
pub unsafe extern "C" fn rdx_graph_declarations_fuzzy_search(
|
|
98
105
|
pointer: GraphPointer,
|
|
99
|
-
|
|
106
|
+
c_queries: *const *const c_char,
|
|
107
|
+
count: usize,
|
|
100
108
|
) -> *mut DeclarationsIter {
|
|
101
|
-
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
};
|
|
109
|
+
if count == 0 || c_queries.is_null() {
|
|
110
|
+
return ptr::null_mut();
|
|
111
|
+
}
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let decl = graph.declarations().get(&id)?;
|
|
111
|
-
Some(CDeclaration::from_declaration(id, decl))
|
|
112
|
-
})
|
|
113
|
-
.collect::<Vec<CDeclaration>>()
|
|
114
|
-
.into_boxed_slice()
|
|
115
|
-
});
|
|
113
|
+
let Ok(queries) = (unsafe { utils::convert_double_pointer_to_vec(c_queries, count) }) else {
|
|
114
|
+
return ptr::null_mut();
|
|
115
|
+
};
|
|
116
|
+
let query_refs: Vec<&str> = queries.iter().map(String::as_str).collect();
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
let entries = with_graph(pointer, |graph| {
|
|
119
|
+
query::declaration_search(graph, &query_refs, &query::MatchMode::Fuzzy)
|
|
120
|
+
.into_iter()
|
|
121
|
+
.filter_map(|id| {
|
|
122
|
+
let decl = graph.declarations().get(&id)?;
|
|
123
|
+
Some(CDeclaration::from_declaration(id, decl))
|
|
124
|
+
})
|
|
125
|
+
.collect::<Vec<CDeclaration>>()
|
|
126
|
+
.into_boxed_slice()
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
DeclarationsIter::new(entries)
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
/// # Panics
|
|
@@ -158,37 +169,41 @@ pub unsafe extern "C" fn rdx_graph_resolve_constant(
|
|
|
158
169
|
})
|
|
159
170
|
}
|
|
160
171
|
|
|
161
|
-
/// Adds
|
|
172
|
+
/// Adds glob patterns to exclude from file discovery during indexing.
|
|
162
173
|
///
|
|
163
174
|
/// # Panics
|
|
164
175
|
///
|
|
165
|
-
/// Will panic if the given array of C string
|
|
176
|
+
/// Will panic if the given array of C string patterns cannot be converted to a `Vec<String>`.
|
|
166
177
|
///
|
|
167
178
|
/// # Safety
|
|
168
179
|
///
|
|
169
180
|
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
170
|
-
/// - `
|
|
181
|
+
/// - `patterns` must be an array of `count` valid, null-terminated UTF-8 strings.
|
|
171
182
|
#[unsafe(no_mangle)]
|
|
172
|
-
pub unsafe extern "C" fn
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
pub unsafe extern "C" fn rdx_graph_exclude_patterns(
|
|
184
|
+
pointer: GraphPointer,
|
|
185
|
+
patterns: *const *const c_char,
|
|
186
|
+
count: usize,
|
|
187
|
+
) {
|
|
188
|
+
let patterns: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(patterns, count).unwrap() };
|
|
189
|
+
let entries: Vec<Box<str>> = patterns.into_iter().map(String::into_boxed_str).collect();
|
|
190
|
+
with_mut_graph(pointer, |graph| graph.exclude_patterns(entries));
|
|
176
191
|
}
|
|
177
192
|
|
|
178
|
-
/// Returns the currently excluded
|
|
179
|
-
///
|
|
193
|
+
/// Returns the currently excluded glob patterns as an array of C strings. Writes the count to `out_count`. Returns NULL
|
|
194
|
+
/// if no patterns are excluded. Caller must free with `free_c_string_array`.
|
|
180
195
|
///
|
|
181
196
|
/// # Safety
|
|
182
197
|
///
|
|
183
198
|
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
184
199
|
/// - `out_count` must be a valid, writable pointer.
|
|
185
200
|
#[unsafe(no_mangle)]
|
|
186
|
-
pub unsafe extern "C" fn
|
|
201
|
+
pub unsafe extern "C" fn rdx_graph_excluded_patterns(
|
|
187
202
|
pointer: GraphPointer,
|
|
188
203
|
out_count: *mut usize,
|
|
189
204
|
) -> *const *const c_char {
|
|
190
205
|
with_graph(pointer, |graph| {
|
|
191
|
-
let excluded = graph.
|
|
206
|
+
let excluded = graph.excluded_patterns();
|
|
192
207
|
|
|
193
208
|
if excluded.is_empty() {
|
|
194
209
|
unsafe { *out_count = 0 };
|
|
@@ -202,7 +217,7 @@ pub unsafe extern "C" fn rdx_graph_excluded_paths(
|
|
|
202
217
|
// on Windows if a configuration file is using forward slashes. For example:
|
|
203
218
|
//
|
|
204
219
|
// C:\project/vendor/bundle
|
|
205
|
-
let normalized = path.
|
|
220
|
+
let normalized = path.replace(std::path::MAIN_SEPARATOR, "/");
|
|
206
221
|
|
|
207
222
|
CString::new(normalized)
|
|
208
223
|
.ok()
|
|
@@ -302,7 +317,7 @@ pub unsafe extern "C" fn rdx_index_all(
|
|
|
302
317
|
let file_paths: Vec<String> = unsafe { utils::convert_double_pointer_to_vec(file_paths, count).unwrap() };
|
|
303
318
|
|
|
304
319
|
with_mut_graph(pointer, |graph| {
|
|
305
|
-
let (file_paths, listing_errors) = listing::collect_file_paths(file_paths, &graph.
|
|
320
|
+
let (file_paths, listing_errors) = listing::collect_file_paths(file_paths, &graph.excluded_patterns());
|
|
306
321
|
let indexing_errors = indexing::index_files(graph, file_paths, indexing::IndexerBackend::RubyIndexer);
|
|
307
322
|
|
|
308
323
|
let all_errors: Vec<String> = listing_errors
|
|
@@ -498,7 +513,7 @@ pub unsafe extern "C" fn rdx_graph_get_declaration(pointer: GraphPointer, name:
|
|
|
498
513
|
};
|
|
499
514
|
|
|
500
515
|
with_graph(pointer, |graph| {
|
|
501
|
-
let decl_id =
|
|
516
|
+
let decl_id = declaration_id_from_lookup_name(&name_str);
|
|
502
517
|
|
|
503
518
|
if let Some(decl) = graph.declarations().get(&decl_id) {
|
|
504
519
|
Box::into_raw(Box::new(CDeclaration::from_declaration(decl_id, decl))).cast_const()
|
|
@@ -917,7 +932,7 @@ pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
|
|
|
917
932
|
graph,
|
|
918
933
|
CompletionReceiver::NamespaceAccess {
|
|
919
934
|
self_decl_id,
|
|
920
|
-
namespace_decl_id:
|
|
935
|
+
namespace_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
921
936
|
},
|
|
922
937
|
Vec::new(),
|
|
923
938
|
)
|
|
@@ -951,7 +966,7 @@ pub unsafe extern "C" fn rdx_graph_complete_method_call(
|
|
|
951
966
|
graph,
|
|
952
967
|
CompletionReceiver::MethodCall {
|
|
953
968
|
self_decl_id,
|
|
954
|
-
receiver_decl_id:
|
|
969
|
+
receiver_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
955
970
|
},
|
|
956
971
|
Vec::new(),
|
|
957
972
|
)
|
|
@@ -995,7 +1010,7 @@ pub unsafe extern "C" fn rdx_graph_complete_method_argument(
|
|
|
995
1010
|
CompletionReceiver::MethodArgument {
|
|
996
1011
|
self_decl_id,
|
|
997
1012
|
nesting_name_id,
|
|
998
|
-
method_decl_id:
|
|
1013
|
+
method_decl_id: declaration_id_from_lookup_name(&name_str),
|
|
999
1014
|
},
|
|
1000
1015
|
names_to_untrack,
|
|
1001
1016
|
)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubydex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A high-performance static analysis suite for Ruby, built in Rust with
|
|
14
14
|
Ruby APIs
|
|
@@ -16,7 +16,6 @@ email:
|
|
|
16
16
|
- ruby@shopify.com
|
|
17
17
|
executables:
|
|
18
18
|
- rdx
|
|
19
|
-
- rubydex_mcp
|
|
20
19
|
extensions:
|
|
21
20
|
- ext/rubydex/extconf.rb
|
|
22
21
|
extra_rdoc_files: []
|
|
@@ -25,7 +24,6 @@ files:
|
|
|
25
24
|
- README.md
|
|
26
25
|
- THIRD_PARTY_LICENSES.html
|
|
27
26
|
- exe/rdx
|
|
28
|
-
- exe/rubydex_mcp
|
|
29
27
|
- ext/rubydex/declaration.c
|
|
30
28
|
- ext/rubydex/declaration.h
|
|
31
29
|
- ext/rubydex/definition.c
|
|
@@ -48,7 +46,6 @@ files:
|
|
|
48
46
|
- ext/rubydex/utils.c
|
|
49
47
|
- ext/rubydex/utils.h
|
|
50
48
|
- lib/rubydex.rb
|
|
51
|
-
- lib/rubydex/bin/rubydex_mcp.exe
|
|
52
49
|
- lib/rubydex/comment.rb
|
|
53
50
|
- lib/rubydex/declaration.rb
|
|
54
51
|
- lib/rubydex/diagnostic.rb
|
|
@@ -58,6 +55,15 @@ files:
|
|
|
58
55
|
- lib/rubydex/keyword.rb
|
|
59
56
|
- lib/rubydex/keyword_parameter.rb
|
|
60
57
|
- lib/rubydex/location.rb
|
|
58
|
+
- lib/rubydex/mcp_server.rb
|
|
59
|
+
- lib/rubydex/mcp_server/protocol.rb
|
|
60
|
+
- lib/rubydex/mcp_server/tools/base_tool.rb
|
|
61
|
+
- lib/rubydex/mcp_server/tools/codebase_stats_tool.rb
|
|
62
|
+
- lib/rubydex/mcp_server/tools/find_constant_references_tool.rb
|
|
63
|
+
- lib/rubydex/mcp_server/tools/get_declaration_tool.rb
|
|
64
|
+
- lib/rubydex/mcp_server/tools/get_descendants_tool.rb
|
|
65
|
+
- lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb
|
|
66
|
+
- lib/rubydex/mcp_server/tools/search_declarations_tool.rb
|
|
61
67
|
- lib/rubydex/mixin.rb
|
|
62
68
|
- lib/rubydex/reference.rb
|
|
63
69
|
- lib/rubydex/signature.rb
|
|
@@ -68,11 +74,6 @@ files:
|
|
|
68
74
|
- rust/about.toml
|
|
69
75
|
- rust/about_templates/about.hbs
|
|
70
76
|
- rust/about_templates/mingw_licenses.hbs
|
|
71
|
-
- rust/rubydex-mcp/Cargo.toml
|
|
72
|
-
- rust/rubydex-mcp/src/main.rs
|
|
73
|
-
- rust/rubydex-mcp/src/server.rs
|
|
74
|
-
- rust/rubydex-mcp/src/tools.rs
|
|
75
|
-
- rust/rubydex-mcp/tests/mcp.rs
|
|
76
77
|
- rust/rubydex-sys/Cargo.toml
|
|
77
78
|
- rust/rubydex-sys/build.rs
|
|
78
79
|
- rust/rubydex-sys/cbindgen.toml
|
data/exe/rubydex_mcp
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "rbconfig"
|
|
5
|
-
|
|
6
|
-
host_os = RbConfig::CONFIG.fetch("host_os")
|
|
7
|
-
executable = host_os.match?(/mswin|mingw|cygwin/) ? "rubydex_mcp.exe" : "rubydex_mcp"
|
|
8
|
-
binary = File.expand_path("../lib/rubydex/bin/#{executable}", __dir__)
|
|
9
|
-
|
|
10
|
-
unless File.executable?(binary)
|
|
11
|
-
abort(<<~MESSAGE.chomp)
|
|
12
|
-
rubydex_mcp is not available at #{binary}.
|
|
13
|
-
Install a precompiled rubydex gem, or reinstall rubydex with Cargo available so the MCP executable can be built locally.
|
|
14
|
-
MESSAGE
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
exec(binary, *ARGV)
|
|
Binary file
|
data/rust/rubydex-mcp/Cargo.toml
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
[package]
|
|
2
|
-
name = "rubydex-mcp"
|
|
3
|
-
version = "0.2.6"
|
|
4
|
-
edition = "2024"
|
|
5
|
-
rust-version = "1.89.0"
|
|
6
|
-
license = "MIT"
|
|
7
|
-
description = "MCP server exposing Rubydex semantic Ruby code intelligence tools."
|
|
8
|
-
homepage = "https://github.com/Shopify/rubydex"
|
|
9
|
-
repository = "https://github.com/Shopify/rubydex"
|
|
10
|
-
readme = "README.md"
|
|
11
|
-
keywords = ["ruby", "mcp", "static-analysis"]
|
|
12
|
-
categories = ["development-tools"]
|
|
13
|
-
|
|
14
|
-
[[bin]]
|
|
15
|
-
name = "rubydex_mcp"
|
|
16
|
-
path = "src/main.rs"
|
|
17
|
-
|
|
18
|
-
[dependencies]
|
|
19
|
-
rubydex = { version = "0.2.6", path = "../rubydex" }
|
|
20
|
-
clap = { version = "4.5.16", features = ["derive"] }
|
|
21
|
-
rmcp = { version = "1.4", features = ["server", "macros", "transport-io", "schemars"] }
|
|
22
|
-
tokio = { version = "1", features = ["macros", "rt", "io-std"] }
|
|
23
|
-
serde = { version = "1", features = ["derive"] }
|
|
24
|
-
serde_json = "1"
|
|
25
|
-
schemars = "1"
|
|
26
|
-
url = "2"
|
|
27
|
-
|
|
28
|
-
[dev-dependencies]
|
|
29
|
-
rubydex = { version = "0.2.6", path = "../rubydex", features = ["test_utils"] }
|
|
30
|
-
assert_cmd = "2.0"
|
|
31
|
-
serde_json = "1"
|
|
32
|
-
|
|
33
|
-
[lints]
|
|
34
|
-
workspace = true
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
use clap::Parser;
|
|
2
|
-
|
|
3
|
-
mod server;
|
|
4
|
-
mod tools;
|
|
5
|
-
|
|
6
|
-
#[derive(Parser, Debug)]
|
|
7
|
-
#[command(
|
|
8
|
-
name = "rubydex_mcp",
|
|
9
|
-
about = "Rubydex MCP server for AI-assisted Ruby code intelligence",
|
|
10
|
-
version
|
|
11
|
-
)]
|
|
12
|
-
struct Args {
|
|
13
|
-
#[arg(value_name = "PATH", default_value = ".")]
|
|
14
|
-
path: String,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fn main() {
|
|
18
|
-
let args = Args::parse();
|
|
19
|
-
|
|
20
|
-
let root = match std::fs::canonicalize(&args.path) {
|
|
21
|
-
Ok(p) => p
|
|
22
|
-
.into_os_string()
|
|
23
|
-
.into_string()
|
|
24
|
-
.expect("Project path is not valid UTF-8"),
|
|
25
|
-
Err(e) => {
|
|
26
|
-
eprintln!("Warning: failed to canonicalize '{}': {e}", args.path);
|
|
27
|
-
args.path
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Create the server and start indexing in the background.
|
|
32
|
-
let server = server::RubydexServer::new(root.clone());
|
|
33
|
-
server.spawn_indexer(root);
|
|
34
|
-
|
|
35
|
-
// Serve MCP over stdio immediately while indexing runs.
|
|
36
|
-
// We need to do this because Claude Code's default MCP server timeout is 30 seconds,
|
|
37
|
-
// And in big codebases it's possible to exceed that and Claude Code would just consider
|
|
38
|
-
// the server fail to connect.
|
|
39
|
-
let rt = tokio::runtime::Builder::new_current_thread()
|
|
40
|
-
.enable_all()
|
|
41
|
-
.build()
|
|
42
|
-
.expect("Failed to build tokio runtime");
|
|
43
|
-
|
|
44
|
-
if let Err(e) = rt.block_on(server.serve()) {
|
|
45
|
-
eprintln!("MCP server error: {e}");
|
|
46
|
-
std::process::exit(1);
|
|
47
|
-
}
|
|
48
|
-
}
|