@supermodeltools/mcp-server 0.8.0 → 0.9.0

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.
@@ -1,445 +0,0 @@
1
- "use strict";
2
- /**
3
- * Traversal queries: function_calls_in, function_calls_out, definitions_in_file
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.functionCallsIn = functionCallsIn;
7
- exports.functionCallsOut = functionCallsOut;
8
- exports.definitionsInFile = definitionsInFile;
9
- exports.fileImports = fileImports;
10
- exports.domainMap = domainMap;
11
- exports.domainMembership = domainMembership;
12
- exports.neighborhood = neighborhood;
13
- const graph_cache_1 = require("../cache/graph-cache");
14
- const types_1 = require("./types");
15
- const constants_1 = require("../constants");
16
- const DEFAULT_LIMIT = constants_1.DEFAULT_QUERY_LIMIT;
17
- /**
18
- * function_calls_in - Find all callers of a function
19
- */
20
- function functionCallsIn(params, graph, source) {
21
- if (!params.targetId) {
22
- console.error('[ERROR] function_calls_in called without targetId');
23
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
24
- detail: 'Use search or list_nodes with labels=["Function"] to find function IDs',
25
- });
26
- }
27
- // Verify target exists and is a function
28
- const targetNode = graph.nodeById.get(params.targetId);
29
- if (!targetNode) {
30
- console.error('[ERROR] Node not found:', params.targetId);
31
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
32
- detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
33
- });
34
- }
35
- if (targetNode.labels?.[0] !== 'Function') {
36
- const actualLabel = targetNode.labels?.[0] || 'unknown';
37
- console.error('[ERROR] Node is not a Function:', params.targetId, 'is', actualLabel);
38
- return (0, types_1.createError)('INVALID_PARAMS', `Node is not a Function (found ${actualLabel})`, {
39
- detail: 'This query only works on Function nodes. Use search with labels=["Function"] to find functions',
40
- });
41
- }
42
- const adj = graph.callAdj.get(params.targetId);
43
- if (!adj) {
44
- return (0, types_1.createResponse)('function_calls_in', graph.cacheKey, source, graph.cachedAt, { nodes: [], edges: [] });
45
- }
46
- const limit = params.limit || DEFAULT_LIMIT;
47
- const callerIds = adj.in.slice(0, limit);
48
- const nodes = [];
49
- const edges = [];
50
- for (const callerId of callerIds) {
51
- const callerNode = graph.nodeById.get(callerId);
52
- if (callerNode) {
53
- nodes.push((0, graph_cache_1.toNodeDescriptor)(callerNode));
54
- edges.push({
55
- type: 'calls',
56
- from: callerId,
57
- to: params.targetId,
58
- });
59
- }
60
- }
61
- const hasMore = adj.in.length > limit;
62
- return (0, types_1.createResponse)('function_calls_in', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
63
- page: { limit, hasMore },
64
- warnings: hasMore ? [`${adj.in.length - limit} more callers not shown. Increase limit to see more.`] : undefined,
65
- });
66
- }
67
- /**
68
- * function_calls_out - Find all functions called by a function
69
- */
70
- function functionCallsOut(params, graph, source) {
71
- if (!params.targetId) {
72
- console.error('[ERROR] function_calls_out called without targetId');
73
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
74
- detail: 'Use search or list_nodes with labels=["Function"] to find function IDs',
75
- });
76
- }
77
- // Verify target exists and is a function
78
- const targetNode = graph.nodeById.get(params.targetId);
79
- if (!targetNode) {
80
- console.error('[ERROR] Node not found:', params.targetId);
81
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
82
- detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
83
- });
84
- }
85
- if (targetNode.labels?.[0] !== 'Function') {
86
- const actualLabel = targetNode.labels?.[0] || 'unknown';
87
- console.error('[ERROR] Node is not a Function:', params.targetId, 'is', actualLabel);
88
- return (0, types_1.createError)('INVALID_PARAMS', `Node is not a Function (found ${actualLabel})`, {
89
- detail: 'This query only works on Function nodes. Use search with labels=["Function"] to find functions',
90
- });
91
- }
92
- const adj = graph.callAdj.get(params.targetId);
93
- if (!adj) {
94
- return (0, types_1.createResponse)('function_calls_out', graph.cacheKey, source, graph.cachedAt, { nodes: [], edges: [] });
95
- }
96
- const limit = params.limit || DEFAULT_LIMIT;
97
- const calleeIds = adj.out.slice(0, limit);
98
- const nodes = [];
99
- const edges = [];
100
- for (const calleeId of calleeIds) {
101
- const calleeNode = graph.nodeById.get(calleeId);
102
- if (calleeNode) {
103
- nodes.push((0, graph_cache_1.toNodeDescriptor)(calleeNode));
104
- edges.push({
105
- type: 'calls',
106
- from: params.targetId,
107
- to: calleeId,
108
- });
109
- }
110
- }
111
- const hasMore = adj.out.length > limit;
112
- return (0, types_1.createResponse)('function_calls_out', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
113
- page: { limit, hasMore },
114
- warnings: hasMore ? [`${adj.out.length - limit} more callees not shown. Increase limit to see more.`] : undefined,
115
- });
116
- }
117
- /**
118
- * definitions_in_file - Get all classes, functions, types defined in a file
119
- */
120
- function definitionsInFile(params, graph, source) {
121
- // Accept either targetId (file node ID) or filePathPrefix (file path)
122
- let filePath = null;
123
- if (params.targetId) {
124
- const targetNode = graph.nodeById.get(params.targetId);
125
- if (!targetNode) {
126
- console.error('[ERROR] Node not found:', params.targetId);
127
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
128
- detail: 'Use search or list_nodes with labels=["File"] to find file nodes',
129
- });
130
- }
131
- if (targetNode.labels?.[0] !== 'File') {
132
- const actualLabel = targetNode.labels?.[0] || 'unknown';
133
- console.error('[ERROR] Node is not a File:', params.targetId, 'is', actualLabel);
134
- return (0, types_1.createError)('INVALID_PARAMS', `Node is not a File (found ${actualLabel})`, {
135
- detail: 'This query requires a File node. Use search with labels=["File"] to find files',
136
- });
137
- }
138
- filePath = targetNode.properties?.filePath || targetNode.properties?.path;
139
- }
140
- else if (params.filePathPrefix) {
141
- filePath = params.filePathPrefix;
142
- }
143
- else {
144
- console.error('[ERROR] definitions_in_file called without targetId or filePathPrefix');
145
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId or filePathPrefix', {
146
- detail: 'Provide either a file node ID (targetId) or a file path (filePathPrefix)',
147
- });
148
- }
149
- if (!filePath) {
150
- console.error('[ERROR] Could not determine file path from node');
151
- return (0, types_1.createError)('INVALID_PARAMS', 'Could not determine file path from the provided node', {
152
- detail: 'The node is missing filePath or path properties',
153
- });
154
- }
155
- const normalizedPath = (0, graph_cache_1.normalizePath)(filePath);
156
- const pathEntry = graph.pathIndex.get(normalizedPath);
157
- let resolvedPath = normalizedPath;
158
- if (!pathEntry) {
159
- // Try to find by partial match
160
- for (const [path] of graph.pathIndex) {
161
- if (path.endsWith(normalizedPath) || normalizedPath.endsWith(path)) {
162
- resolvedPath = path;
163
- break;
164
- }
165
- }
166
- if (resolvedPath === normalizedPath) {
167
- console.error('[ERROR] File not found in codebase:', filePath);
168
- return (0, types_1.createError)('NOT_FOUND', `File not found in analyzed codebase: ${filePath}`, {
169
- detail: 'The file may not exist, may be excluded by .gitignore, or has no definitions',
170
- });
171
- }
172
- }
173
- const entry = pathEntry || graph.pathIndex.get(resolvedPath);
174
- if (!entry) {
175
- return (0, types_1.createResponse)('definitions_in_file', graph.cacheKey, source, graph.cachedAt, { file: null, definitions: { nodes: [] } });
176
- }
177
- const limit = params.limit || DEFAULT_LIMIT;
178
- const nodes = [];
179
- // Get file node
180
- let fileNode = null;
181
- if (entry.fileId) {
182
- const file = graph.nodeById.get(entry.fileId);
183
- if (file) {
184
- fileNode = (0, graph_cache_1.toNodeDescriptor)(file);
185
- }
186
- }
187
- // Collect all definitions
188
- const allIds = [...entry.classIds, ...entry.functionIds, ...entry.typeIds];
189
- for (const id of allIds.slice(0, limit)) {
190
- const node = graph.nodeById.get(id);
191
- if (node) {
192
- nodes.push((0, graph_cache_1.toNodeDescriptor)(node));
193
- }
194
- }
195
- const hasMore = allIds.length > limit;
196
- return (0, types_1.createResponse)('definitions_in_file', graph.cacheKey, source, graph.cachedAt, { file: fileNode, definitions: { nodes } }, {
197
- page: { limit, hasMore },
198
- warnings: hasMore ? [`${allIds.length - limit} more definitions not shown.`] : undefined,
199
- });
200
- }
201
- /**
202
- * file_imports - Get imports for a file (both outgoing and incoming)
203
- * v1.1 query
204
- */
205
- function fileImports(params, graph, source) {
206
- if (!params.targetId) {
207
- console.error('[ERROR] file_imports called without targetId');
208
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
209
- detail: 'Use search or list_nodes with labels=["File"] to find file nodes',
210
- });
211
- }
212
- const targetNode = graph.nodeById.get(params.targetId);
213
- if (!targetNode) {
214
- console.error('[ERROR] Node not found:', params.targetId);
215
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
216
- detail: 'Use search or list_nodes to discover valid node IDs',
217
- });
218
- }
219
- const label = targetNode.labels?.[0];
220
- if (label !== 'File' && label !== 'LocalModule' && label !== 'ExternalModule') {
221
- console.error('[ERROR] Node is not a File/Module:', params.targetId, 'is', label);
222
- return (0, types_1.createError)('INVALID_PARAMS', `Node is not a File/Module (found ${label})`, {
223
- detail: 'This query requires a File, LocalModule, or ExternalModule node',
224
- });
225
- }
226
- const limit = params.limit || DEFAULT_LIMIT;
227
- const outIds = new Set();
228
- const addOutgoingImports = (nodeId) => {
229
- const adj = graph.importAdj.get(nodeId);
230
- if (!adj)
231
- return;
232
- for (const outId of adj.out) {
233
- outIds.add(outId);
234
- }
235
- };
236
- // Always include imports directly attached to the target node.
237
- addOutgoingImports(params.targetId);
238
- // If the target is a File node, also aggregate IMPORTS edges attached to
239
- // definitions within the file. This handles graphs where IMPORTS edges
240
- // are modeled as Function -> Module rather than File -> Module.
241
- if (label === 'File') {
242
- const filePathRaw = targetNode.properties?.filePath ||
243
- targetNode.properties?.path ||
244
- '';
245
- const filePath = (0, graph_cache_1.normalizePath)(filePathRaw);
246
- const entry = graph.pathIndex.get(filePath);
247
- if (entry) {
248
- for (const id of entry.functionIds)
249
- addOutgoingImports(id);
250
- for (const id of entry.classIds)
251
- addOutgoingImports(id);
252
- for (const id of entry.typeIds)
253
- addOutgoingImports(id);
254
- }
255
- }
256
- const imports = [];
257
- for (const id of outIds) {
258
- if (imports.length >= limit)
259
- break;
260
- const node = graph.nodeById.get(id);
261
- if (node) {
262
- imports.push((0, graph_cache_1.toNodeDescriptor)(node));
263
- }
264
- }
265
- const hasMore = outIds.size > limit;
266
- return (0, types_1.createResponse)('file_imports', graph.cacheKey, source, graph.cachedAt, { imports: { nodes: imports } }, {
267
- page: { limit, hasMore },
268
- warnings: label === 'File' && outIds.size === 0
269
- ? [
270
- 'No IMPORTS edges found directly on the File node or on its contained definitions. This may reflect graph modeling choices for this repository.',
271
- ]
272
- : undefined,
273
- });
274
- }
275
- /**
276
- * domain_map - List all domains with their relationships
277
- * v1.1 query
278
- */
279
- function domainMap(params, graph, source) {
280
- const domains = [];
281
- const relationships = [];
282
- // Get domain nodes
283
- const domainIds = graph.labelIndex.get('Domain') || [];
284
- for (const id of domainIds) {
285
- const node = graph.nodeById.get(id);
286
- if (node) {
287
- const name = node.properties?.name;
288
- const description = node.properties?.description;
289
- const domainEntry = graph.domainIndex.get(name);
290
- domains.push({
291
- name,
292
- description,
293
- memberCount: domainEntry?.memberIds.length || 0,
294
- });
295
- // Collect relationships
296
- if (domainEntry) {
297
- for (const rel of domainEntry.relationships) {
298
- const targetNode = graph.nodeById.get(rel.endNode);
299
- if (targetNode) {
300
- relationships.push({
301
- from: name,
302
- to: targetNode.properties?.name,
303
- type: rel.type,
304
- });
305
- }
306
- }
307
- }
308
- }
309
- }
310
- return (0, types_1.createResponse)('domain_map', graph.cacheKey, source, graph.cachedAt, { domains, relationships });
311
- }
312
- /**
313
- * domain_membership - Get members of a domain
314
- * v1.1 query
315
- */
316
- function domainMembership(params, graph, source) {
317
- if (!params.searchText && !params.targetId) {
318
- console.error('[ERROR] domain_membership called without searchText or targetId');
319
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: searchText or targetId', {
320
- detail: 'Provide either a domain name (searchText) or domain node ID (targetId)',
321
- });
322
- }
323
- let domainName;
324
- if (params.targetId) {
325
- const node = graph.nodeById.get(params.targetId);
326
- if (!node) {
327
- console.error('[ERROR] Node not found:', params.targetId);
328
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
329
- detail: 'Use domain_map to discover domain nodes',
330
- });
331
- }
332
- domainName = node.properties?.name;
333
- }
334
- else {
335
- domainName = params.searchText;
336
- }
337
- const domainEntry = graph.domainIndex.get(domainName);
338
- if (!domainEntry) {
339
- console.error('[ERROR] Domain not found:', domainName);
340
- return (0, types_1.createError)('NOT_FOUND', `Domain not found: ${domainName}`, {
341
- detail: 'Use domain_map to list available domains',
342
- });
343
- }
344
- const limit = params.limit || DEFAULT_LIMIT;
345
- const members = [];
346
- for (const id of domainEntry.memberIds.slice(0, limit)) {
347
- const node = graph.nodeById.get(id);
348
- if (node)
349
- members.push((0, graph_cache_1.toNodeDescriptor)(node));
350
- }
351
- const hasMore = domainEntry.memberIds.length > limit;
352
- return (0, types_1.createResponse)('domain_membership', graph.cacheKey, source, graph.cachedAt, { domain: domainName, members: { nodes: members } }, {
353
- page: { limit, hasMore },
354
- });
355
- }
356
- /**
357
- * neighborhood - Get ego graph around a node
358
- * v1.1 query
359
- */
360
- function neighborhood(params, graph, source) {
361
- if (!params.targetId) {
362
- console.error('[ERROR] neighborhood called without targetId');
363
- return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
364
- detail: 'Use search or list_nodes to find a node, then explore its neighborhood',
365
- });
366
- }
367
- const targetNode = graph.nodeById.get(params.targetId);
368
- if (!targetNode) {
369
- console.error('[ERROR] Node not found:', params.targetId);
370
- return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
371
- detail: 'Use search or list_nodes to discover valid node IDs',
372
- });
373
- }
374
- const depth = Math.min(params.depth || 1, constants_1.MAX_NEIGHBORHOOD_DEPTH);
375
- const limit = params.limit || 100;
376
- // Only include relationship types we actually implement
377
- const relationshipTypes = params.relationshipTypes || ['calls', 'IMPORTS'];
378
- const visited = new Set();
379
- const nodes = [];
380
- const edges = [];
381
- // BFS
382
- let frontier = [params.targetId];
383
- visited.add(params.targetId);
384
- nodes.push((0, graph_cache_1.toNodeDescriptor)(targetNode));
385
- for (let d = 0; d < depth && nodes.length < limit; d++) {
386
- const nextFrontier = [];
387
- for (const nodeId of frontier) {
388
- if (nodes.length >= limit)
389
- break;
390
- // Get adjacent nodes via call graph
391
- if (relationshipTypes.includes('calls')) {
392
- const callAdj = graph.callAdj.get(nodeId);
393
- if (callAdj) {
394
- for (const adjId of [...callAdj.out, ...callAdj.in]) {
395
- if (!visited.has(adjId) && nodes.length < limit) {
396
- visited.add(adjId);
397
- const adjNode = graph.nodeById.get(adjId);
398
- if (adjNode) {
399
- nodes.push((0, graph_cache_1.toNodeDescriptor)(adjNode));
400
- nextFrontier.push(adjId);
401
- // Add edge
402
- if (callAdj.out.includes(adjId)) {
403
- edges.push({ type: 'calls', from: nodeId, to: adjId });
404
- }
405
- else {
406
- edges.push({ type: 'calls', from: adjId, to: nodeId });
407
- }
408
- }
409
- }
410
- }
411
- }
412
- }
413
- // Get adjacent nodes via import graph
414
- if (relationshipTypes.includes('IMPORTS')) {
415
- const importAdj = graph.importAdj.get(nodeId);
416
- if (importAdj) {
417
- for (const adjId of [...importAdj.out, ...importAdj.in]) {
418
- if (!visited.has(adjId) && nodes.length < limit) {
419
- visited.add(adjId);
420
- const adjNode = graph.nodeById.get(adjId);
421
- if (adjNode) {
422
- nodes.push((0, graph_cache_1.toNodeDescriptor)(adjNode));
423
- nextFrontier.push(adjId);
424
- if (importAdj.out.includes(adjId)) {
425
- edges.push({ type: 'IMPORTS', from: nodeId, to: adjId });
426
- }
427
- else {
428
- edges.push({ type: 'IMPORTS', from: adjId, to: nodeId });
429
- }
430
- }
431
- }
432
- }
433
- }
434
- }
435
- }
436
- frontier = nextFrontier;
437
- if (frontier.length === 0)
438
- break;
439
- }
440
- const hasMore = nodes.length >= limit;
441
- return (0, types_1.createResponse)('neighborhood', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
442
- page: { limit, hasMore },
443
- warnings: hasMore ? [`Neighborhood truncated at ${limit} nodes. Decrease depth or increase limit.`] : undefined,
444
- });
445
- }
@@ -1,38 +0,0 @@
1
- "use strict";
2
- /**
3
- * Query types and result shapes for the query engine
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isQueryError = isQueryError;
7
- exports.createError = createError;
8
- exports.createResponse = createResponse;
9
- // Type guard for error responses
10
- function isQueryError(result) {
11
- return (typeof result === 'object' &&
12
- result !== null &&
13
- 'error' in result &&
14
- typeof result.error === 'object');
15
- }
16
- // Create error response
17
- function createError(code, message, options) {
18
- return {
19
- error: {
20
- code,
21
- message,
22
- retryable: options?.retryable,
23
- detail: options?.detail,
24
- },
25
- };
26
- }
27
- // Create success response
28
- function createResponse(query, cacheKey, source, cachedAt, result, options) {
29
- return {
30
- query,
31
- cacheKey,
32
- source,
33
- cachedAt,
34
- result,
35
- page: options?.page,
36
- warnings: options?.warnings,
37
- };
38
- }