1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Parameter + response shapes for the graph-traversal tools `neighbors`, `path`, and
//! `subgraph` (ADR-0003). All three read the shared typed code-graph (ADR-0001) and report
//! per-edge provenance + confidence (ADR-0002), so they share the [`GraphNode`] / [`GraphEdge`]
//! payload. Node references inside edges are indices into the response's `nodes` vec.
use rmcp::schemars;
use serde::{Deserialize, Serialize};
use crate::path::RelPath;
fn default_traverse_edges() -> String {
"all".into()
}
fn default_neighbors_direction() -> String {
"both".into()
}
/// One node in a traversal result. `Symbol` nodes carry a location; `file` and virtual
/// `external` (unresolved name) nodes carry only what they have.
#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
pub struct GraphNode {
/// Symbol name, file basename, or the unresolved identifier for a virtual node.
pub name: String,
/// `"function"`/`"method"`/`"struct"`/… for a symbol, `"file"` for a file node, or
/// `"external"` for a virtual node whose target did not resolve to an indexed definition.
pub kind: String,
/// Owning file. Absent for virtual `external` nodes.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<RelPath>,
/// 0-based row of the symbol definition. Absent for file/external nodes.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_row: Option<u32>,
/// 0-based byte column of the symbol definition. Absent for file/external nodes.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_col: Option<u32>,
/// Hop distance from the nearest root (`neighbors`/`subgraph`). Absent on `path`, where
/// order is the path itself.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depth: Option<u32>,
/// Centrality score (higher = more central): local walk-centrality within the returned
/// neighborhood for `subgraph`, global weighted-degree over the detection graph for
/// `communities`. Absent on tools that don't rank nodes.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub centrality: Option<u64>,
}
/// One typed, provenance-tagged edge. `from`/`to` index into the response's `nodes` vec.
#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
pub struct GraphEdge {
/// Index into `nodes` of the edge source.
pub from: u32,
/// Index into `nodes` of the edge target.
pub to: u32,
/// `"calls"` | `"imports"` | `"inherits"` | `"contains"`.
pub kind: String,
/// `"extracted"` (proven) | `"inferred"` (name-resolved) | `"ambiguous"` (one name → many).
pub provenance: String,
/// Numeric confidence on the fixed ladder: 1.0 / 0.5 / 0.2.
pub confidence: f32,
/// Aggregate multiplicity (e.g. call-site count); 1 for structural edges.
pub weight: u32,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct NeighborsParams {
/// Root symbol name. Every definition site of the name is a root (use `path` to pin one).
#[serde(alias = "needle", alias = "query", alias = "symbol", alias = "q")]
pub name: String,
/// Optional path to disambiguate `name` when several symbols share it.
#[serde(default)]
pub path: Option<RelPath>,
/// `"both"` (default), `"out"` (what the root reaches), or `"in"` (what reaches the root).
/// Accepts the `call_graph` synonyms `callees`/`callers`.
#[serde(default = "default_neighbors_direction")]
pub direction: String,
/// Hop radius. Default 2, capped at 4.
#[serde(default)]
pub depth: Option<u32>,
/// Edge lanes to follow: `"all"` (default; calls+imports+inherits), `"calls"`, `"imports"`,
/// `"inherits"`, `"both"` (calls+imports), or `"contains"`.
#[serde(default = "default_traverse_edges")]
pub edges: String,
/// Minimum edge confidence to traverse (0.0–1.0, clamped). Default 0.0 (keep everything).
#[serde(default)]
pub min_confidence: Option<f32>,
/// Hard cap on nodes returned. Default 100, max 500.
#[serde(default)]
pub max_nodes: Option<u32>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct PathParams {
/// Source symbol name.
#[serde(alias = "source", alias = "start")]
pub from: String,
/// Optional path to disambiguate the source.
#[serde(default)]
pub from_path: Option<RelPath>,
/// Target symbol name.
#[serde(alias = "target", alias = "dest")]
pub to: String,
/// Optional path to disambiguate the target.
#[serde(default)]
pub to_path: Option<RelPath>,
/// Edge lanes the path may cross: `"all"` (default), `"calls"`, `"imports"`, `"inherits"`,
/// or `"both"`. Containment is excluded by default (it yields structurally valid but
/// meaningless routes); set `include_contains` to add it.
#[serde(default = "default_traverse_edges")]
pub edges: String,
/// Include containment (file→symbol) edges in the search. Default false.
#[serde(default)]
pub include_contains: bool,
/// Minimum edge confidence to cross (0.0–1.0, clamped). Default 0.0.
#[serde(default)]
pub min_confidence: Option<f32>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct SubgraphParams {
/// Root symbol name. Every definition site is a root.
#[serde(alias = "needle", alias = "query", alias = "symbol", alias = "q")]
pub name: String,
/// Optional path to disambiguate `name`.
#[serde(default)]
pub path: Option<RelPath>,
/// Hop radius gathered before the centrality cut. Default 2, capped at 4.
#[serde(default)]
pub depth: Option<u32>,
/// Edge lanes to include: `"all"` (default; calls+imports+inherits), `"calls"`, `"imports"`,
/// `"inherits"`, `"both"` (calls+imports), or `"contains"`.
#[serde(default = "default_traverse_edges")]
pub edges: String,
/// Minimum edge confidence to include (0.0–1.0, clamped). Default 0.0.
#[serde(default)]
pub min_confidence: Option<f32>,
/// Keep only this many most-central nodes (roots always kept). Default 30, max 200.
#[serde(default)]
pub max_nodes: Option<u32>,
}
#[derive(Debug, Serialize, schemars::JsonSchema)]
pub struct NeighborsResponse {
/// Echo of the requested root name.
pub root: String,
/// Echo of the resolved direction.
pub direction: String,
/// Reached nodes; roots come first (depth 0).
pub nodes: Vec<GraphNode>,
/// Typed, provenance-tagged edges among the returned nodes.
pub edges: Vec<GraphEdge>,
/// True when a cap stopped the walk before the neighborhood was exhausted.
pub truncated: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notice: Option<super::types::LifecycleNotice>,
/// Server-side handler latency in microseconds (excludes transport).
#[serde(default)]
pub elapsed_us: u64,
}
#[derive(Debug, Serialize, schemars::JsonSchema)]
pub struct PathResponse {
/// Echo of the requested source name.
pub from: String,
/// Echo of the requested target name.
pub to: String,
/// True when a route was found.
pub found: bool,
/// Nodes along the path, source first. Empty when `found` is false.
pub nodes: Vec<GraphNode>,
/// Edges connecting consecutive path nodes, in order.
pub edges: Vec<GraphEdge>,
/// Total confidence-weighted cost of the path (lower = shorter/more proven). 0 when not found.
pub cost: u32,
/// True when the code-graph scan hit its cap before the build completed: a `found:false` here
/// may be a partial-scan artifact, not a proof that no route exists. Distinguishes "unreachable"
/// from "not scanned far enough", matching `neighbors`/`subgraph`.
#[serde(default)]
pub truncated: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notice: Option<super::types::LifecycleNotice>,
/// Server-side handler latency in microseconds (excludes transport).
#[serde(default)]
pub elapsed_us: u64,
}
#[derive(Debug, Serialize, schemars::JsonSchema)]
pub struct SubgraphResponse {
/// Echo of the requested root name.
pub root: String,
/// Kept nodes, most central first; roots always present.
pub nodes: Vec<GraphNode>,
/// Edges induced among the kept nodes.
pub edges: Vec<GraphEdge>,
/// True when the neighborhood was cut to the centrality head (more nodes existed).
pub truncated: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notice: Option<super::types::LifecycleNotice>,
/// Server-side handler latency in microseconds (excludes transport).
#[serde(default)]
pub elapsed_us: u64,
}