@supermodeltools/mcp-server 0.4.5 → 0.4.8
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.
- package/README.md +0 -12
- package/dist/cache/graph-cache.js +32 -3
- package/dist/constants.js +20 -0
- package/dist/index.js +35 -1
- package/dist/queries/discovery.js +20 -5
- package/dist/queries/index.js +30 -19
- package/dist/queries/traversal.js +75 -22
- package/dist/server.js +62 -6
- package/dist/tools/create-supermodel-graph.js +263 -119
- package/dist/utils/logger.js +41 -0
- package/dist/utils/zip-repository.js +327 -41
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -150,23 +150,11 @@ Analyzes code structure, dependencies, and relationships across a repository. Us
|
|
|
150
150
|
|----------|------|----------|-------------|
|
|
151
151
|
| `directory` | string | Yes* | Path to repository directory (automatic zipping) |
|
|
152
152
|
| `file` | string | Yes* | Path to pre-zipped archive (deprecated) |
|
|
153
|
-
| `Idempotency-Key` | string | Yes | Cache key in format `{repo}:{type}:{hash}` |
|
|
154
153
|
| `query` | string | No | Query type (summary, search, list_nodes, etc.) |
|
|
155
154
|
| `jq_filter` | string | No | jq filter for custom data extraction |
|
|
156
155
|
|
|
157
156
|
\* Either `directory` (recommended) or `file` must be provided
|
|
158
157
|
|
|
159
|
-
**Quick start:**
|
|
160
|
-
|
|
161
|
-
```bash
|
|
162
|
-
# 1. Get commit hash for cache key
|
|
163
|
-
git rev-parse --short HEAD
|
|
164
|
-
# Output: abc123
|
|
165
|
-
|
|
166
|
-
# 2. Ask Claude to analyze (no manual zipping needed!)
|
|
167
|
-
# "Analyze the codebase at /path/to/repo using key myproject:supermodel:abc123"
|
|
168
|
-
```
|
|
169
|
-
|
|
170
158
|
**Example prompts:**
|
|
171
159
|
- "Analyze the codebase at . to understand its architecture"
|
|
172
160
|
- "Before I refactor the authentication module, analyze this repo to show me what depends on it"
|
|
@@ -8,6 +8,7 @@ exports.graphCache = exports.GraphCache = void 0;
|
|
|
8
8
|
exports.buildIndexes = buildIndexes;
|
|
9
9
|
exports.normalizePath = normalizePath;
|
|
10
10
|
exports.toNodeDescriptor = toNodeDescriptor;
|
|
11
|
+
const constants_1 = require("../constants");
|
|
11
12
|
/**
|
|
12
13
|
* Build indexes from raw SupermodelIR response
|
|
13
14
|
*/
|
|
@@ -200,10 +201,12 @@ class GraphCache {
|
|
|
200
201
|
cache = new Map();
|
|
201
202
|
maxGraphs;
|
|
202
203
|
maxNodes;
|
|
204
|
+
maxAgeMs;
|
|
203
205
|
currentNodes = 0;
|
|
204
206
|
constructor(options) {
|
|
205
|
-
this.maxGraphs = options?.maxGraphs ||
|
|
206
|
-
this.maxNodes = options?.maxNodes ||
|
|
207
|
+
this.maxGraphs = options?.maxGraphs || constants_1.DEFAULT_MAX_GRAPHS;
|
|
208
|
+
this.maxNodes = options?.maxNodes || constants_1.DEFAULT_MAX_NODES;
|
|
209
|
+
this.maxAgeMs = options?.maxAgeMs || constants_1.DEFAULT_CACHE_TTL_MS;
|
|
207
210
|
}
|
|
208
211
|
get(cacheKey) {
|
|
209
212
|
const entry = this.cache.get(cacheKey);
|
|
@@ -216,16 +219,20 @@ class GraphCache {
|
|
|
216
219
|
}
|
|
217
220
|
set(cacheKey, graph) {
|
|
218
221
|
const nodeCount = graph.summary.nodeCount;
|
|
222
|
+
// Evict stale entries first
|
|
223
|
+
this.evictStale();
|
|
219
224
|
// Evict if needed
|
|
220
225
|
while ((this.cache.size >= this.maxGraphs || this.currentNodes + nodeCount > this.maxNodes) &&
|
|
221
226
|
this.cache.size > 0) {
|
|
222
227
|
this.evictOldest();
|
|
223
228
|
}
|
|
224
229
|
// Store
|
|
230
|
+
const now = Date.now();
|
|
225
231
|
this.cache.set(cacheKey, {
|
|
226
232
|
graph,
|
|
227
233
|
nodeCount,
|
|
228
|
-
lastAccessed:
|
|
234
|
+
lastAccessed: now,
|
|
235
|
+
createdAt: now,
|
|
229
236
|
});
|
|
230
237
|
this.currentNodes += nodeCount;
|
|
231
238
|
}
|
|
@@ -247,6 +254,28 @@ class GraphCache {
|
|
|
247
254
|
this.cache.delete(oldestKey);
|
|
248
255
|
}
|
|
249
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Evict all cache entries that have exceeded their TTL (maxAgeMs)
|
|
259
|
+
* This method can be called manually or is automatically invoked before adding new entries
|
|
260
|
+
* @returns Number of entries evicted
|
|
261
|
+
*/
|
|
262
|
+
evictStale() {
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
const keysToEvict = [];
|
|
265
|
+
// Find all stale entries
|
|
266
|
+
for (const [key, entry] of this.cache) {
|
|
267
|
+
if (now - entry.createdAt > this.maxAgeMs) {
|
|
268
|
+
keysToEvict.push(key);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Evict them
|
|
272
|
+
for (const key of keysToEvict) {
|
|
273
|
+
const entry = this.cache.get(key);
|
|
274
|
+
this.currentNodes -= entry.nodeCount;
|
|
275
|
+
this.cache.delete(key);
|
|
276
|
+
}
|
|
277
|
+
return keysToEvict.length;
|
|
278
|
+
}
|
|
250
279
|
status() {
|
|
251
280
|
return {
|
|
252
281
|
graphs: this.cache.size,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Application-wide constants
|
|
4
|
+
* Single source of truth for configuration values
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MAX_NEIGHBORHOOD_DEPTH = exports.DEFAULT_QUERY_LIMIT = exports.DEFAULT_CACHE_TTL_MS = exports.DEFAULT_MAX_NODES = exports.DEFAULT_MAX_GRAPHS = exports.MAX_ZIP_SIZE_BYTES = exports.ZIP_CLEANUP_AGE_MS = exports.CONNECTION_TIMEOUT_MS = exports.DEFAULT_API_TIMEOUT_MS = void 0;
|
|
8
|
+
// HTTP timeout configuration
|
|
9
|
+
exports.DEFAULT_API_TIMEOUT_MS = 900_000; // 15 minutes (complex repos can take 10+ min)
|
|
10
|
+
exports.CONNECTION_TIMEOUT_MS = 30_000; // 30 seconds to establish connection
|
|
11
|
+
// ZIP configuration
|
|
12
|
+
exports.ZIP_CLEANUP_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
13
|
+
exports.MAX_ZIP_SIZE_BYTES = 500 * 1024 * 1024; // 500MB default
|
|
14
|
+
// Cache configuration
|
|
15
|
+
exports.DEFAULT_MAX_GRAPHS = 20; // Maximum number of graphs to cache
|
|
16
|
+
exports.DEFAULT_MAX_NODES = 1_000_000; // Maximum total nodes across all cached graphs
|
|
17
|
+
exports.DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour - time-to-live for cached graphs
|
|
18
|
+
// Query defaults
|
|
19
|
+
exports.DEFAULT_QUERY_LIMIT = 200; // Default result limit for queries
|
|
20
|
+
exports.MAX_NEIGHBORHOOD_DEPTH = 3; // Maximum traversal depth for neighborhood queries
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
37
|
const server_1 = require("./server");
|
|
38
|
+
const logger = __importStar(require("./utils/logger"));
|
|
5
39
|
async function main() {
|
|
6
40
|
const server = new server_1.Server();
|
|
7
41
|
await server.start();
|
|
8
42
|
}
|
|
9
43
|
main().catch((error) => {
|
|
10
|
-
|
|
44
|
+
logger.error('Fatal error:', error);
|
|
11
45
|
process.exit(1);
|
|
12
46
|
});
|
|
@@ -8,17 +8,22 @@ exports.search = search;
|
|
|
8
8
|
exports.listNodes = listNodes;
|
|
9
9
|
const graph_cache_1 = require("../cache/graph-cache");
|
|
10
10
|
const types_1 = require("./types");
|
|
11
|
-
const
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const DEFAULT_LIMIT = constants_1.DEFAULT_QUERY_LIMIT;
|
|
12
13
|
/**
|
|
13
14
|
* get_node - Return full details for a specific node ID
|
|
14
15
|
*/
|
|
15
16
|
function getNode(params, graph, source) {
|
|
16
17
|
if (!params.targetId) {
|
|
17
|
-
|
|
18
|
+
console.error('[ERROR] get_node called without targetId');
|
|
19
|
+
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
|
|
20
|
+
detail: 'Use search or list_nodes to find valid node IDs, then call get_node with the targetId',
|
|
21
|
+
});
|
|
18
22
|
}
|
|
19
23
|
const node = graph.nodeById.get(params.targetId);
|
|
20
24
|
if (!node) {
|
|
21
|
-
|
|
25
|
+
console.error('[ERROR] Node not found:', params.targetId);
|
|
26
|
+
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
22
27
|
detail: 'Use search or list_nodes to discover valid node IDs',
|
|
23
28
|
});
|
|
24
29
|
}
|
|
@@ -36,7 +41,10 @@ function getNode(params, graph, source) {
|
|
|
36
41
|
*/
|
|
37
42
|
function search(params, graph, source) {
|
|
38
43
|
if (!params.searchText) {
|
|
39
|
-
|
|
44
|
+
console.error('[ERROR] search called without searchText');
|
|
45
|
+
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: searchText', {
|
|
46
|
+
detail: 'Provide a search term to find nodes by name substring',
|
|
47
|
+
});
|
|
40
48
|
}
|
|
41
49
|
const searchLower = params.searchText.toLowerCase();
|
|
42
50
|
const limit = params.limit || DEFAULT_LIMIT;
|
|
@@ -94,7 +102,14 @@ function listNodes(params, graph, source) {
|
|
|
94
102
|
regex = new RegExp(namePattern, 'i');
|
|
95
103
|
}
|
|
96
104
|
catch (e) {
|
|
97
|
-
|
|
105
|
+
// Log detailed error for debugging
|
|
106
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
107
|
+
console.error('[ERROR] Invalid regex pattern');
|
|
108
|
+
console.error('[ERROR] Pattern:', namePattern);
|
|
109
|
+
console.error('[ERROR] Error:', errorMsg);
|
|
110
|
+
return (0, types_1.createError)('INVALID_PARAMS', `Invalid regex pattern: ${errorMsg}`, {
|
|
111
|
+
detail: `Pattern: ${namePattern}`,
|
|
112
|
+
});
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
115
|
const results = [];
|
package/dist/queries/index.js
CHANGED
|
@@ -54,9 +54,10 @@ async function executeQuery(params, apiResponse) {
|
|
|
54
54
|
}
|
|
55
55
|
// All other queries require a graph
|
|
56
56
|
if (!graph) {
|
|
57
|
-
|
|
57
|
+
console.error('[ERROR] Cache miss for key:', cacheKey);
|
|
58
|
+
return (0, types_1.createError)('CACHE_MISS', `Graph not cached. The query engine will fetch it from the API.`, {
|
|
58
59
|
retryable: true,
|
|
59
|
-
detail: '
|
|
60
|
+
detail: 'This is a normal first-time operation. The graph will be cached for subsequent queries.',
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
// Dispatch to appropriate handler
|
|
@@ -99,9 +100,15 @@ function dispatchQuery(params, graph, source) {
|
|
|
99
100
|
// Not implemented yet
|
|
100
101
|
case 'uses_in_file':
|
|
101
102
|
case 'list_files_in_dir':
|
|
102
|
-
|
|
103
|
+
console.error('[ERROR] Unimplemented query type requested:', params.query);
|
|
104
|
+
return (0, types_1.createError)('INVALID_QUERY', `Query type '${params.query}' is not yet implemented`, {
|
|
105
|
+
detail: 'This query is planned for a future release',
|
|
106
|
+
});
|
|
103
107
|
default:
|
|
104
|
-
|
|
108
|
+
console.error('[ERROR] Unknown query type:', params.query);
|
|
109
|
+
return (0, types_1.createError)('INVALID_QUERY', `Unknown query type: ${params.query}`, {
|
|
110
|
+
detail: 'Use graph_status to see available query types',
|
|
111
|
+
});
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
/**
|
|
@@ -129,8 +136,12 @@ async function executeJqQuery(params, graph, source) {
|
|
|
129
136
|
}
|
|
130
137
|
catch (error) {
|
|
131
138
|
const message = error instanceof Error ? error.message : String(error);
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
// Log detailed error for debugging
|
|
140
|
+
console.error('[ERROR] jq filter execution failed');
|
|
141
|
+
console.error('[ERROR] Filter:', params.jq_filter);
|
|
142
|
+
console.error('[ERROR] Error:', message);
|
|
143
|
+
return (0, types_1.createError)('BAD_JQ', `Invalid jq filter syntax. ${message}`, {
|
|
144
|
+
detail: `Filter: ${params.jq_filter}`,
|
|
134
145
|
});
|
|
135
146
|
}
|
|
136
147
|
}
|
|
@@ -144,63 +155,63 @@ function getAvailableQueries() {
|
|
|
144
155
|
{
|
|
145
156
|
query: 'graph_status',
|
|
146
157
|
description: 'Check if graph is cached and get summary stats',
|
|
147
|
-
requiredParams: ['
|
|
158
|
+
requiredParams: ['idempotencyKey'],
|
|
148
159
|
optionalParams: [],
|
|
149
160
|
phase: 'v1',
|
|
150
161
|
},
|
|
151
162
|
{
|
|
152
163
|
query: 'summary',
|
|
153
164
|
description: 'Get high-level stats about the codebase',
|
|
154
|
-
requiredParams: ['
|
|
165
|
+
requiredParams: ['idempotencyKey'],
|
|
155
166
|
optionalParams: [],
|
|
156
167
|
phase: 'v1',
|
|
157
168
|
},
|
|
158
169
|
{
|
|
159
170
|
query: 'get_node',
|
|
160
171
|
description: 'Get full details for a specific node by ID',
|
|
161
|
-
requiredParams: ['
|
|
172
|
+
requiredParams: ['idempotencyKey', 'targetId'],
|
|
162
173
|
optionalParams: [],
|
|
163
174
|
phase: 'v1',
|
|
164
175
|
},
|
|
165
176
|
{
|
|
166
177
|
query: 'search',
|
|
167
178
|
description: 'Search nodes by name substring',
|
|
168
|
-
requiredParams: ['
|
|
179
|
+
requiredParams: ['idempotencyKey', 'searchText'],
|
|
169
180
|
optionalParams: ['labels', 'filePathPrefix', 'limit'],
|
|
170
181
|
phase: 'v1',
|
|
171
182
|
},
|
|
172
183
|
{
|
|
173
184
|
query: 'list_nodes',
|
|
174
185
|
description: 'List nodes with filters (labels, namePattern, filePathPrefix)',
|
|
175
|
-
requiredParams: ['
|
|
186
|
+
requiredParams: ['idempotencyKey'],
|
|
176
187
|
optionalParams: ['labels', 'namePattern', 'filePathPrefix', 'searchText', 'limit'],
|
|
177
188
|
phase: 'v1',
|
|
178
189
|
},
|
|
179
190
|
{
|
|
180
191
|
query: 'function_calls_in',
|
|
181
192
|
description: 'Find all callers of a function',
|
|
182
|
-
requiredParams: ['
|
|
193
|
+
requiredParams: ['idempotencyKey', 'targetId'],
|
|
183
194
|
optionalParams: ['limit'],
|
|
184
195
|
phase: 'v1',
|
|
185
196
|
},
|
|
186
197
|
{
|
|
187
198
|
query: 'function_calls_out',
|
|
188
199
|
description: 'Find all functions called by a function',
|
|
189
|
-
requiredParams: ['
|
|
200
|
+
requiredParams: ['idempotencyKey', 'targetId'],
|
|
190
201
|
optionalParams: ['limit'],
|
|
191
202
|
phase: 'v1',
|
|
192
203
|
},
|
|
193
204
|
{
|
|
194
205
|
query: 'definitions_in_file',
|
|
195
206
|
description: 'Get all classes, functions, types defined in a file',
|
|
196
|
-
requiredParams: ['
|
|
207
|
+
requiredParams: ['idempotencyKey'],
|
|
197
208
|
optionalParams: ['targetId', 'filePathPrefix', 'limit'],
|
|
198
209
|
phase: 'v1',
|
|
199
210
|
},
|
|
200
211
|
{
|
|
201
212
|
query: 'jq',
|
|
202
213
|
description: 'Execute raw jq filter (escape hatch)',
|
|
203
|
-
requiredParams: ['
|
|
214
|
+
requiredParams: ['idempotencyKey', 'jq_filter'],
|
|
204
215
|
optionalParams: [],
|
|
205
216
|
phase: 'v1',
|
|
206
217
|
},
|
|
@@ -208,28 +219,28 @@ function getAvailableQueries() {
|
|
|
208
219
|
{
|
|
209
220
|
query: 'file_imports',
|
|
210
221
|
description: 'Get imports for a file (outgoing and incoming)',
|
|
211
|
-
requiredParams: ['
|
|
222
|
+
requiredParams: ['idempotencyKey', 'targetId'],
|
|
212
223
|
optionalParams: ['limit'],
|
|
213
224
|
phase: 'v1.1',
|
|
214
225
|
},
|
|
215
226
|
{
|
|
216
227
|
query: 'domain_map',
|
|
217
228
|
description: 'List all domains with relationships',
|
|
218
|
-
requiredParams: ['
|
|
229
|
+
requiredParams: ['idempotencyKey'],
|
|
219
230
|
optionalParams: [],
|
|
220
231
|
phase: 'v1.1',
|
|
221
232
|
},
|
|
222
233
|
{
|
|
223
234
|
query: 'domain_membership',
|
|
224
235
|
description: 'Get members of a domain',
|
|
225
|
-
requiredParams: ['
|
|
236
|
+
requiredParams: ['idempotencyKey'],
|
|
226
237
|
optionalParams: ['targetId', 'searchText', 'limit'],
|
|
227
238
|
phase: 'v1.1',
|
|
228
239
|
},
|
|
229
240
|
{
|
|
230
241
|
query: 'neighborhood',
|
|
231
242
|
description: 'Get ego graph around a node',
|
|
232
|
-
requiredParams: ['
|
|
243
|
+
requiredParams: ['idempotencyKey', 'targetId'],
|
|
233
244
|
optionalParams: ['depth', 'relationshipTypes', 'limit'],
|
|
234
245
|
phase: 'v1.1',
|
|
235
246
|
},
|
|
@@ -12,23 +12,32 @@ exports.domainMembership = domainMembership;
|
|
|
12
12
|
exports.neighborhood = neighborhood;
|
|
13
13
|
const graph_cache_1 = require("../cache/graph-cache");
|
|
14
14
|
const types_1 = require("./types");
|
|
15
|
-
const
|
|
15
|
+
const constants_1 = require("../constants");
|
|
16
|
+
const DEFAULT_LIMIT = constants_1.DEFAULT_QUERY_LIMIT;
|
|
16
17
|
/**
|
|
17
18
|
* function_calls_in - Find all callers of a function
|
|
18
19
|
*/
|
|
19
20
|
function functionCallsIn(params, graph, source) {
|
|
20
21
|
if (!params.targetId) {
|
|
21
|
-
|
|
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
|
+
});
|
|
22
26
|
}
|
|
23
27
|
// Verify target exists and is a function
|
|
24
28
|
const targetNode = graph.nodeById.get(params.targetId);
|
|
25
29
|
if (!targetNode) {
|
|
26
|
-
|
|
30
|
+
console.error('[ERROR] Node not found:', params.targetId);
|
|
31
|
+
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
27
32
|
detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
35
|
if (targetNode.labels?.[0] !== 'Function') {
|
|
31
|
-
|
|
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
|
+
});
|
|
32
41
|
}
|
|
33
42
|
const adj = graph.callAdj.get(params.targetId);
|
|
34
43
|
if (!adj) {
|
|
@@ -60,17 +69,25 @@ function functionCallsIn(params, graph, source) {
|
|
|
60
69
|
*/
|
|
61
70
|
function functionCallsOut(params, graph, source) {
|
|
62
71
|
if (!params.targetId) {
|
|
63
|
-
|
|
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
|
+
});
|
|
64
76
|
}
|
|
65
77
|
// Verify target exists and is a function
|
|
66
78
|
const targetNode = graph.nodeById.get(params.targetId);
|
|
67
79
|
if (!targetNode) {
|
|
68
|
-
|
|
80
|
+
console.error('[ERROR] Node not found:', params.targetId);
|
|
81
|
+
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
69
82
|
detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
|
|
70
83
|
});
|
|
71
84
|
}
|
|
72
85
|
if (targetNode.labels?.[0] !== 'Function') {
|
|
73
|
-
|
|
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
|
+
});
|
|
74
91
|
}
|
|
75
92
|
const adj = graph.callAdj.get(params.targetId);
|
|
76
93
|
if (!adj) {
|
|
@@ -106,10 +123,17 @@ function definitionsInFile(params, graph, source) {
|
|
|
106
123
|
if (params.targetId) {
|
|
107
124
|
const targetNode = graph.nodeById.get(params.targetId);
|
|
108
125
|
if (!targetNode) {
|
|
109
|
-
|
|
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
|
+
});
|
|
110
130
|
}
|
|
111
131
|
if (targetNode.labels?.[0] !== 'File') {
|
|
112
|
-
|
|
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
|
+
});
|
|
113
137
|
}
|
|
114
138
|
filePath = targetNode.properties?.filePath || targetNode.properties?.path;
|
|
115
139
|
}
|
|
@@ -117,10 +141,16 @@ function definitionsInFile(params, graph, source) {
|
|
|
117
141
|
filePath = params.filePathPrefix;
|
|
118
142
|
}
|
|
119
143
|
else {
|
|
120
|
-
|
|
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
|
+
});
|
|
121
148
|
}
|
|
122
149
|
if (!filePath) {
|
|
123
|
-
|
|
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
|
+
});
|
|
124
154
|
}
|
|
125
155
|
const normalizedPath = (0, graph_cache_1.normalizePath)(filePath);
|
|
126
156
|
const pathEntry = graph.pathIndex.get(normalizedPath);
|
|
@@ -134,8 +164,9 @@ function definitionsInFile(params, graph, source) {
|
|
|
134
164
|
}
|
|
135
165
|
}
|
|
136
166
|
if (resolvedPath === normalizedPath) {
|
|
137
|
-
|
|
138
|
-
|
|
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',
|
|
139
170
|
});
|
|
140
171
|
}
|
|
141
172
|
}
|
|
@@ -173,15 +204,24 @@ function definitionsInFile(params, graph, source) {
|
|
|
173
204
|
*/
|
|
174
205
|
function fileImports(params, graph, source) {
|
|
175
206
|
if (!params.targetId) {
|
|
176
|
-
|
|
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
|
+
});
|
|
177
211
|
}
|
|
178
212
|
const targetNode = graph.nodeById.get(params.targetId);
|
|
179
213
|
if (!targetNode) {
|
|
180
|
-
|
|
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
|
+
});
|
|
181
218
|
}
|
|
182
219
|
const label = targetNode.labels?.[0];
|
|
183
220
|
if (label !== 'File' && label !== 'LocalModule' && label !== 'ExternalModule') {
|
|
184
|
-
|
|
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
|
+
});
|
|
185
225
|
}
|
|
186
226
|
const limit = params.limit || DEFAULT_LIMIT;
|
|
187
227
|
const outIds = new Set();
|
|
@@ -275,13 +315,19 @@ function domainMap(params, graph, source) {
|
|
|
275
315
|
*/
|
|
276
316
|
function domainMembership(params, graph, source) {
|
|
277
317
|
if (!params.searchText && !params.targetId) {
|
|
278
|
-
|
|
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
|
+
});
|
|
279
322
|
}
|
|
280
323
|
let domainName;
|
|
281
324
|
if (params.targetId) {
|
|
282
325
|
const node = graph.nodeById.get(params.targetId);
|
|
283
326
|
if (!node) {
|
|
284
|
-
|
|
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
|
+
});
|
|
285
331
|
}
|
|
286
332
|
domainName = node.properties?.name;
|
|
287
333
|
}
|
|
@@ -290,7 +336,8 @@ function domainMembership(params, graph, source) {
|
|
|
290
336
|
}
|
|
291
337
|
const domainEntry = graph.domainIndex.get(domainName);
|
|
292
338
|
if (!domainEntry) {
|
|
293
|
-
|
|
339
|
+
console.error('[ERROR] Domain not found:', domainName);
|
|
340
|
+
return (0, types_1.createError)('NOT_FOUND', `Domain not found: ${domainName}`, {
|
|
294
341
|
detail: 'Use domain_map to list available domains',
|
|
295
342
|
});
|
|
296
343
|
}
|
|
@@ -312,13 +359,19 @@ function domainMembership(params, graph, source) {
|
|
|
312
359
|
*/
|
|
313
360
|
function neighborhood(params, graph, source) {
|
|
314
361
|
if (!params.targetId) {
|
|
315
|
-
|
|
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
|
+
});
|
|
316
366
|
}
|
|
317
367
|
const targetNode = graph.nodeById.get(params.targetId);
|
|
318
368
|
if (!targetNode) {
|
|
319
|
-
|
|
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
|
+
});
|
|
320
373
|
}
|
|
321
|
-
const depth = Math.min(params.depth || 1,
|
|
374
|
+
const depth = Math.min(params.depth || 1, constants_1.MAX_NEIGHBORHOOD_DEPTH);
|
|
322
375
|
const limit = params.limit || 100;
|
|
323
376
|
// Only include relationship types we actually implement
|
|
324
377
|
const relationshipTypes = params.relationshipTypes || ['calls', 'IMPORTS'];
|