@supermodeltools/mcp-server 0.7.0 → 0.7.2
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 +71 -2
- package/dist/server.js +74 -11
- package/dist/tools/feature-request.js +84 -0
- package/dist/tools/find-call-sites.js +141 -0
- package/dist/tools/find-definition.js +161 -0
- package/dist/tools/report-bug.js +133 -0
- package/dist/tools/task-query-tools.js +81 -0
- package/dist/tools/trace-call-chain.js +179 -0
- package/dist/tools/trace-data-flow.js +233 -0
- package/dist/utils/github.js +253 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -366,6 +366,71 @@ Generate an AST-level parse graph with fine-grained code structure.
|
|
|
366
366
|
| `directory` | string | Yes | Path to repository directory |
|
|
367
367
|
| `jq_filter` | string | No | jq filter for custom data extraction |
|
|
368
368
|
|
|
369
|
+
### Task-Specific Query Tools
|
|
370
|
+
|
|
371
|
+
After generating a graph with any of the tools above, use these lightweight tools for focused queries. They run against the cached graph and return small, targeted results (<10KB, <5s).
|
|
372
|
+
|
|
373
|
+
#### `find_call_sites`
|
|
374
|
+
|
|
375
|
+
Find all locations where a specific function is called.
|
|
376
|
+
|
|
377
|
+
**Parameters:**
|
|
378
|
+
|
|
379
|
+
| Argument | Type | Required | Description |
|
|
380
|
+
|----------|------|----------|-------------|
|
|
381
|
+
| `path` | string | Yes | Path that was previously analyzed |
|
|
382
|
+
| `function_name` | string | Yes | Name of the function to find call sites for |
|
|
383
|
+
| `include_context` | boolean | No | Include surrounding code context |
|
|
384
|
+
| `max_results` | number | No | Maximum number of results to return |
|
|
385
|
+
|
|
386
|
+
#### `trace_call_chain`
|
|
387
|
+
|
|
388
|
+
Find the shortest call path from function A to function B using BFS.
|
|
389
|
+
|
|
390
|
+
**Parameters:**
|
|
391
|
+
|
|
392
|
+
| Argument | Type | Required | Description |
|
|
393
|
+
|----------|------|----------|-------------|
|
|
394
|
+
| `path` | string | Yes | Path that was previously analyzed |
|
|
395
|
+
| `from_function` | string | Yes | Starting function name |
|
|
396
|
+
| `to_function` | string | Yes | Target function name |
|
|
397
|
+
| `max_depth` | number | No | Maximum search depth |
|
|
398
|
+
|
|
399
|
+
#### `find_definition`
|
|
400
|
+
|
|
401
|
+
Locate where a symbol (function, class, variable, type) is defined.
|
|
402
|
+
|
|
403
|
+
**Parameters:**
|
|
404
|
+
|
|
405
|
+
| Argument | Type | Required | Description |
|
|
406
|
+
|----------|------|----------|-------------|
|
|
407
|
+
| `path` | string | Yes | Path that was previously analyzed |
|
|
408
|
+
| `name` | string | Yes | Symbol name to find |
|
|
409
|
+
| `type` | string | No | Symbol type: function, class, variable, or type |
|
|
410
|
+
|
|
411
|
+
#### `trace_data_flow`
|
|
412
|
+
|
|
413
|
+
Track how data flows through function parameters and variables.
|
|
414
|
+
|
|
415
|
+
**Parameters:**
|
|
416
|
+
|
|
417
|
+
| Argument | Type | Required | Description |
|
|
418
|
+
|----------|------|----------|-------------|
|
|
419
|
+
| `path` | string | Yes | Path that was previously analyzed |
|
|
420
|
+
| `variable` | string | Yes | Variable or parameter name to trace |
|
|
421
|
+
| `function_name` | string | No | Starting function for the trace |
|
|
422
|
+
| `max_depth` | number | No | Maximum trace depth |
|
|
423
|
+
|
|
424
|
+
### Feedback Tools
|
|
425
|
+
|
|
426
|
+
#### `request_feature`
|
|
427
|
+
|
|
428
|
+
Open a feature request issue on the supermodeltools/mcp GitHub repository. Requires a `GITHUB_TOKEN` environment variable with `public_repo` scope.
|
|
429
|
+
|
|
430
|
+
#### `report_bug`
|
|
431
|
+
|
|
432
|
+
Report a bug or unexpected behavior by opening an issue on the supermodeltools/mcp GitHub repository. Requires a `GITHUB_TOKEN` environment variable with `public_repo` scope.
|
|
433
|
+
|
|
369
434
|
### Choosing the Right Tool
|
|
370
435
|
|
|
371
436
|
| Tool | Best For | Output Size |
|
|
@@ -375,10 +440,14 @@ Generate an AST-level parse graph with fine-grained code structure.
|
|
|
375
440
|
| `get_dependency_graph` | Module refactoring, circular deps | Small - modules only |
|
|
376
441
|
| `get_domain_graph` | Architecture overview | Smallest - domains only |
|
|
377
442
|
| `get_parse_graph` | AST analysis, precise refactoring | Large - full AST |
|
|
443
|
+
| `find_call_sites` | Where is function X called? | Small (<10KB) |
|
|
444
|
+
| `trace_call_chain` | Path from function A to B | Small (<10KB) |
|
|
445
|
+
| `find_definition` | Where is symbol X defined? | Small (<10KB) |
|
|
446
|
+
| `trace_data_flow` | How does data flow through params? | Small (<10KB) |
|
|
378
447
|
|
|
379
|
-
**Tip:** Start with `get_domain_graph` for a quick architecture overview, then
|
|
448
|
+
**Tip:** Start with `get_domain_graph` for a quick architecture overview, then use task-specific query tools (`find_call_sites`, `trace_call_chain`, etc.) for focused questions. Drill down with `get_call_graph` or `get_dependency_graph` for broader analysis.
|
|
380
449
|
|
|
381
|
-
> **Note:** All graph tools accept a `directory` parameter and an optional `jq_filter`. If you start the server with a default working directory (`node dist/index.js /path/to/repo`),
|
|
450
|
+
> **Note:** All graph tools accept a `directory` parameter and an optional `jq_filter`. Task-specific query tools use `path` instead. If you start the server with a default working directory (`node dist/index.js /path/to/repo`), directory arguments can be omitted from tool calls.
|
|
382
451
|
|
|
383
452
|
## Tool Performance & Timeout Requirements
|
|
384
453
|
|
package/dist/server.js
CHANGED
|
@@ -47,6 +47,9 @@ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
47
47
|
const sdk_1 = require("@supermodeltools/sdk");
|
|
48
48
|
const create_supermodel_graph_1 = __importDefault(require("./tools/create-supermodel-graph"));
|
|
49
49
|
const graph_tools_1 = require("./tools/graph-tools");
|
|
50
|
+
const task_query_tools_1 = require("./tools/task-query-tools");
|
|
51
|
+
const feature_request_1 = __importDefault(require("./tools/feature-request"));
|
|
52
|
+
const report_bug_1 = __importDefault(require("./tools/report-bug"));
|
|
50
53
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
51
54
|
const zip_repository_1 = require("./utils/zip-repository");
|
|
52
55
|
const undici_1 = require("undici");
|
|
@@ -87,28 +90,85 @@ class Server {
|
|
|
87
90
|
|
|
88
91
|
Generate code graphs to understand a codebase before making changes.
|
|
89
92
|
|
|
90
|
-
##
|
|
93
|
+
## Choosing a Tool
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
- **Need architecture overview?** → \`get_domain_graph\` (smallest output, fastest to read)
|
|
96
|
+
- **Need to trace function calls?** → \`get_call_graph\` (function nodes + "calls" relationships)
|
|
97
|
+
- **Need to understand imports/dependencies?** → \`get_dependency_graph\` (file nodes + "IMPORTS" relationships)
|
|
98
|
+
- **Need full code structure (classes, types, functions)?** → \`get_parse_graph\` (all nodes + structural relationships)
|
|
99
|
+
- **Need everything in one call?** → \`explore_codebase\` (complete graph with built-in query engine)
|
|
100
|
+
- **Need a focused answer about a specific function/symbol?** → Task-specific query tools (see below)
|
|
95
101
|
|
|
96
|
-
Node IDs are consistent across all graph types. A function ID from \`get_domain_graph\`
|
|
102
|
+
Node IDs are consistent across all graph types. A function ID from \`get_domain_graph\` works in \`get_call_graph\` results.
|
|
103
|
+
|
|
104
|
+
## What Each Tool Returns
|
|
105
|
+
|
|
106
|
+
- \`get_domain_graph\`: Domains → { name, description, responsibilities, subdomains, files, functions, classes }
|
|
107
|
+
- \`get_call_graph\`: Functions → { name, filePath, startLine, endLine } with "calls" relationships
|
|
108
|
+
- \`get_dependency_graph\`: Files → { name, filePath, language } with "IMPORTS" relationships
|
|
109
|
+
- \`get_parse_graph\`: All node types (File, Directory, Class, Function, Type) with structural relationships (CONTAINS, DEFINES, DECLARES, IMPORTS)
|
|
110
|
+
- \`explore_codebase\`: Full graph (all of the above combined) with a query engine for filtering
|
|
111
|
+
|
|
112
|
+
## Parameters
|
|
113
|
+
|
|
114
|
+
All graph tools accept \`directory\` and \`jq_filter\`. Both are optional:
|
|
115
|
+
- \`directory\`: Path to analyze. **Omit this** if the MCP server was started with a default workdir — it will use that automatically. Pass a subdirectory (e.g. \`src/auth\`) for faster results.
|
|
116
|
+
- \`jq_filter\`: Optional jq expression to extract specific data from the response.
|
|
117
|
+
|
|
118
|
+
## Caching
|
|
119
|
+
|
|
120
|
+
\`explore_codebase\` caches graphs in memory (1-hour TTL, LRU eviction). The first call hits the API (30+ seconds); subsequent queries on the same directory are instant. Use \`query: "graph_status"\` to check if a graph is cached before making API calls. Regenerate after code changes by calling without a query. The individual graph tools (\`get_call_graph\`, etc.) do not use the cache — each call hits the API.
|
|
121
|
+
|
|
122
|
+
## Task-Specific Query Tools
|
|
123
|
+
|
|
124
|
+
After generating a graph, use these lightweight tools for focused queries:
|
|
125
|
+
|
|
126
|
+
- \`find_call_sites\`: Find where a function is called (<5s, <10KB)
|
|
127
|
+
- \`trace_call_chain\`: Find path from function A to B (<5s, <10KB)
|
|
128
|
+
- \`find_definition\`: Locate where a symbol is defined (<5s, <10KB)
|
|
129
|
+
- \`trace_data_flow\`: Follow how data flows through parameters (<5s, <10KB)
|
|
130
|
+
|
|
131
|
+
These tools require a graph to be cached first (via any graph generation tool).
|
|
97
132
|
|
|
98
133
|
## Performance
|
|
99
134
|
|
|
100
|
-
- First call takes 30+ seconds (
|
|
135
|
+
- First API call takes 30+ seconds (complex repos can take 10+ minutes)
|
|
101
136
|
- Analyze subdirectories for faster results: \`src/auth\` instead of full repo
|
|
102
137
|
- Use \`jq_filter\` to extract only the data you need
|
|
138
|
+
- With \`explore_codebase\`, use the query engine instead of re-fetching
|
|
139
|
+
- Task query tools are fast (<5s) after initial graph generation
|
|
103
140
|
|
|
104
|
-
##
|
|
141
|
+
## Common Mistakes
|
|
105
142
|
|
|
106
|
-
|
|
143
|
+
- **Don't analyze the full repo when you only need one module.** Pass a subdirectory to \`directory\`.
|
|
144
|
+
- **Don't use \`explore_codebase\` when you only need call or dependency data.** The individual tools return smaller, focused results.
|
|
145
|
+
- **Don't re-fetch when the graph is cached.** Use \`explore_codebase\` with \`query: "graph_status"\` to check first.
|
|
146
|
+
- **Don't forget \`jq_filter\`.** Large graphs can be megabytes — filter to what you need.
|
|
107
147
|
|
|
108
148
|
## Errors
|
|
109
149
|
|
|
110
|
-
- \`error.recoverable: true\` → retry after brief wait
|
|
111
|
-
- \`error.reportable: true\` → server bug,
|
|
150
|
+
- \`error.recoverable: true\` → retry after a brief wait
|
|
151
|
+
- \`error.reportable: true\` → likely a server bug, consider filing with \`report_bug\`
|
|
152
|
+
|
|
153
|
+
## Feedback: Feature Requests & Bug Reports
|
|
154
|
+
|
|
155
|
+
If you have an idea for a feature that would make the Supermodel MCP server more useful, or if you encounter a bug or unexpected behavior with any tool or with the underlying Supermodel API, you can open an issue directly on the supermodeltools/mcp GitHub repository using the \`request_feature\` and \`report_bug\` tools. The Supermodel team reviews and responds to all submitted issues.
|
|
156
|
+
|
|
157
|
+
**Setup:** These tools require a \`GITHUB_TOKEN\` environment variable with permission to create issues on public repositories. To set this up:
|
|
158
|
+
1. Create a GitHub personal access token at https://github.com/settings/tokens with the \`public_repo\` scope.
|
|
159
|
+
2. Set the token as an environment variable: \`export GITHUB_TOKEN=ghp_your_token_here\`
|
|
160
|
+
3. Restart the MCP server so it picks up the new environment variable.
|
|
161
|
+
|
|
162
|
+
**When to use \`request_feature\`:**
|
|
163
|
+
- You think a new tool or query type would be helpful
|
|
164
|
+
- An existing tool is missing a capability you need
|
|
165
|
+
- You have an idea to improve the developer experience
|
|
166
|
+
|
|
167
|
+
**When to use \`report_bug\`:**
|
|
168
|
+
- A tool returned an error that seems incorrect or unexpected
|
|
169
|
+
- Results don't match what the tool description promises
|
|
170
|
+
- You hit a crash, timeout, or other failure that seems like a server issue
|
|
171
|
+
- The Supermodel API returned malformed or unexpected data`,
|
|
112
172
|
});
|
|
113
173
|
const config = new sdk_1.Configuration({
|
|
114
174
|
basePath: process.env.SUPERMODEL_BASE_URL || 'https://api.supermodeltools.com',
|
|
@@ -128,10 +188,13 @@ Returns the complete graph with all data combined. Useful for comprehensive expo
|
|
|
128
188
|
this.setupHandlers();
|
|
129
189
|
}
|
|
130
190
|
setupHandlers() {
|
|
131
|
-
// Collect all tools: the main explore_codebase tool plus individual graph tools
|
|
191
|
+
// Collect all tools: the main explore_codebase tool plus individual graph tools plus task query tools
|
|
132
192
|
const allTools = [
|
|
133
193
|
create_supermodel_graph_1.default,
|
|
134
194
|
...graph_tools_1.graphTools,
|
|
195
|
+
...task_query_tools_1.taskQueryTools,
|
|
196
|
+
feature_request_1.default,
|
|
197
|
+
report_bug_1.default,
|
|
135
198
|
];
|
|
136
199
|
// Create a map for quick handler lookup, checking for duplicates
|
|
137
200
|
const toolMap = new Map();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool for agents to submit feature requests as GitHub issues.
|
|
4
|
+
* Creates issues on the supermodeltools/mcp repository.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.handler = exports.tool = exports.metadata = void 0;
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const github_1 = require("../utils/github");
|
|
10
|
+
exports.metadata = {
|
|
11
|
+
resource: 'issues',
|
|
12
|
+
operation: 'write',
|
|
13
|
+
tags: ['feature-request', 'github'],
|
|
14
|
+
httpMethod: 'post',
|
|
15
|
+
httpPath: '/repos/supermodeltools/mcp/issues',
|
|
16
|
+
operationId: 'createFeatureRequest',
|
|
17
|
+
};
|
|
18
|
+
exports.tool = {
|
|
19
|
+
name: 'request_feature',
|
|
20
|
+
description: `Submit a feature request to the Supermodel MCP server GitHub repository (${github_1.GITHUB_REPO}).
|
|
21
|
+
|
|
22
|
+
Creates a GitHub issue on behalf of the agent. The Supermodel team actively monitors and responds to all submitted issues.
|
|
23
|
+
|
|
24
|
+
Use this tool whenever you believe a new feature, improvement, or enhancement would be helpful for the Supermodel MCP server or its underlying API. Examples include:
|
|
25
|
+
- Suggesting a new tool, query type, or graph capability
|
|
26
|
+
- Requesting improvements to an existing tool (e.g. better filtering, new parameters, improved output format)
|
|
27
|
+
- Proposing changes to error messages, documentation, or developer experience
|
|
28
|
+
- Identifying a missing capability you needed while working on a task
|
|
29
|
+
|
|
30
|
+
This tool requires a GITHUB_TOKEN environment variable. To set it up:
|
|
31
|
+
1. Create a GitHub personal access token at https://github.com/settings/tokens with the "public_repo" scope
|
|
32
|
+
2. Set it in your environment: export GITHUB_TOKEN=ghp_your_token_here
|
|
33
|
+
3. Restart the MCP server`,
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
title: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Short, descriptive title for the feature request.',
|
|
40
|
+
},
|
|
41
|
+
description: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Detailed description of the feature. Include context, use cases, and expected behavior.',
|
|
44
|
+
},
|
|
45
|
+
labels: {
|
|
46
|
+
type: 'array',
|
|
47
|
+
items: { type: 'string' },
|
|
48
|
+
description: 'Optional labels to categorize the issue (e.g. ["enhancement"]).',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ['title', 'description'],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const handler = async (_client, args) => {
|
|
55
|
+
const tokenError = (0, github_1.validateGitHubToken)();
|
|
56
|
+
if (tokenError)
|
|
57
|
+
return (0, types_1.asErrorResult)(tokenError);
|
|
58
|
+
if (!args) {
|
|
59
|
+
return (0, types_1.asErrorResult)({
|
|
60
|
+
type: 'validation_error',
|
|
61
|
+
message: 'Missing required parameters: title and description.',
|
|
62
|
+
code: 'MISSING_PARAMETERS',
|
|
63
|
+
recoverable: false,
|
|
64
|
+
suggestion: 'Provide both "title" and "description" parameters.',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const { title, description, labels } = args;
|
|
68
|
+
const titleError = (0, github_1.validateRequiredString)(title, 'title', 'INVALID_TITLE', 'Provide a short, descriptive title as a string.');
|
|
69
|
+
if (titleError)
|
|
70
|
+
return (0, types_1.asErrorResult)(titleError);
|
|
71
|
+
const descError = (0, github_1.validateRequiredString)(description, 'description', 'INVALID_DESCRIPTION', 'Provide a detailed description as a string.');
|
|
72
|
+
if (descError)
|
|
73
|
+
return (0, types_1.asErrorResult)(descError);
|
|
74
|
+
const labelsError = (0, github_1.validateLabels)(labels);
|
|
75
|
+
if (labelsError)
|
|
76
|
+
return (0, types_1.asErrorResult)(labelsError);
|
|
77
|
+
return (0, github_1.createGitHubIssue)('request_feature', {
|
|
78
|
+
title: title,
|
|
79
|
+
body: description,
|
|
80
|
+
labels: labels,
|
|
81
|
+
}, 'Feature request created successfully.');
|
|
82
|
+
};
|
|
83
|
+
exports.handler = handler;
|
|
84
|
+
exports.default = { metadata: exports.metadata, tool: exports.tool, handler: exports.handler };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Task-specific tool: Find call sites for a function
|
|
4
|
+
* Lightweight, focused query without full graph overhead
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.findCallSitesTool = void 0;
|
|
8
|
+
exports.findCallSites = findCallSites;
|
|
9
|
+
const zod_1 = require("zod");
|
|
10
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
11
|
+
const cache_1 = require("../cache");
|
|
12
|
+
const FindCallSitesArgsSchema = zod_1.z.object({
|
|
13
|
+
path: zod_1.z.string().describe('Repository path'),
|
|
14
|
+
function_name: zod_1.z.string().describe('Name of function to find call sites for'),
|
|
15
|
+
include_context: zod_1.z.boolean().optional().describe('Include surrounding code context'),
|
|
16
|
+
max_results: zod_1.z.number().optional().describe('Maximum number of results to return'),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Find all places where a function is called
|
|
20
|
+
*/
|
|
21
|
+
async function findCallSites(args) {
|
|
22
|
+
const { path, function_name, include_context = true, max_results = 10 } = args;
|
|
23
|
+
// Get cached graph
|
|
24
|
+
const cacheKey = getCacheKey(path);
|
|
25
|
+
const graph = cache_1.graphCache.get(cacheKey);
|
|
26
|
+
if (!graph) {
|
|
27
|
+
throw new Error('Graph not cached. Run explore_codebase first to analyze the repository.');
|
|
28
|
+
}
|
|
29
|
+
// Find function node by name (case-insensitive)
|
|
30
|
+
const functionNodes = findFunctionsByName(graph, function_name);
|
|
31
|
+
if (functionNodes.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
function_name,
|
|
34
|
+
total_call_sites: 0,
|
|
35
|
+
call_sites: [],
|
|
36
|
+
summary: `Function "${function_name}" not found in codebase.`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Use first match if multiple functions with same name
|
|
40
|
+
const targetNode = functionNodes[0];
|
|
41
|
+
const targetId = targetNode.id;
|
|
42
|
+
// Get incoming call edges
|
|
43
|
+
const callAdj = graph.callAdj.get(targetId);
|
|
44
|
+
if (!callAdj || callAdj.in.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
function_name,
|
|
47
|
+
total_call_sites: 0,
|
|
48
|
+
call_sites: [],
|
|
49
|
+
summary: `Function "${function_name}" is not called by any other functions.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Build call site results
|
|
53
|
+
const callSites = [];
|
|
54
|
+
for (const callerId of callAdj.in) {
|
|
55
|
+
const callerNode = graph.nodeById.get(callerId);
|
|
56
|
+
if (!callerNode)
|
|
57
|
+
continue;
|
|
58
|
+
// Find edge details (if available)
|
|
59
|
+
const edge = findCallEdge(graph, callerId, targetId);
|
|
60
|
+
const result = {
|
|
61
|
+
caller: {
|
|
62
|
+
name: callerNode.properties?.name || 'unknown',
|
|
63
|
+
file: callerNode.properties?.filePath || 'unknown',
|
|
64
|
+
line: callerNode.properties?.startLine || 0,
|
|
65
|
+
},
|
|
66
|
+
call_site: {
|
|
67
|
+
line: edge?.properties?.lineNumber || 0,
|
|
68
|
+
column: edge?.properties?.columnNumber,
|
|
69
|
+
context: edge?.properties?.context,
|
|
70
|
+
code_snippet: include_context ? edge?.properties?.codeSnippet : undefined,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
callSites.push(result);
|
|
74
|
+
if (callSites.length >= max_results) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Generate summary
|
|
79
|
+
const summary = generateSummary(function_name, callSites, callAdj.in.length);
|
|
80
|
+
return {
|
|
81
|
+
function_name,
|
|
82
|
+
total_call_sites: callAdj.in.length,
|
|
83
|
+
call_sites: callSites,
|
|
84
|
+
summary,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Helper: Find function nodes by name
|
|
89
|
+
*/
|
|
90
|
+
function findFunctionsByName(graph, name) {
|
|
91
|
+
const lowerName = name.toLowerCase();
|
|
92
|
+
const nodeIds = graph.nameIndex.get(lowerName) || [];
|
|
93
|
+
return nodeIds
|
|
94
|
+
.map((id) => graph.nodeById.get(id))
|
|
95
|
+
.filter((node) => node && node.labels?.[0] === 'Function');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Helper: Find specific call edge between two functions
|
|
99
|
+
*/
|
|
100
|
+
function findCallEdge(graph, fromId, toId) {
|
|
101
|
+
// In original cache, edges aren't indexed by ID
|
|
102
|
+
// This is a limitation we'd fix in edge-aware cache
|
|
103
|
+
// For now, search through raw relationships
|
|
104
|
+
const relationships = graph.raw?.graph?.relationships || [];
|
|
105
|
+
return relationships.find((rel) => rel.type === 'calls' && rel.startNode === fromId && rel.endNode === toId);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Helper: Generate natural language summary
|
|
109
|
+
*/
|
|
110
|
+
function generateSummary(functionName, callSites, total) {
|
|
111
|
+
if (callSites.length === 0) {
|
|
112
|
+
return `Function "${functionName}" is not called by any functions.`;
|
|
113
|
+
}
|
|
114
|
+
const callerNames = callSites.map(cs => cs.caller.name);
|
|
115
|
+
const uniqueFiles = new Set(callSites.map(cs => cs.caller.file));
|
|
116
|
+
let summary = `Function "${functionName}" is called by ${total} function(s) in ${uniqueFiles.size} file(s).`;
|
|
117
|
+
if (callSites.length > 0) {
|
|
118
|
+
summary += ` Primary callers: ${callerNames.slice(0, 3).join(', ')}`;
|
|
119
|
+
if (total > 3) {
|
|
120
|
+
summary += ` and ${total - 3} more`;
|
|
121
|
+
}
|
|
122
|
+
summary += '.';
|
|
123
|
+
}
|
|
124
|
+
return summary;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Helper: Generate cache key (simplified - should match main implementation)
|
|
128
|
+
*/
|
|
129
|
+
function getCacheKey(path) {
|
|
130
|
+
// In real implementation, this would include git hash, etc.
|
|
131
|
+
return `cache_${path}`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Tool metadata for MCP registration
|
|
135
|
+
*/
|
|
136
|
+
exports.findCallSitesTool = {
|
|
137
|
+
name: 'find_call_sites',
|
|
138
|
+
description: 'Find all places where a specific function is called, with line numbers and context',
|
|
139
|
+
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(FindCallSitesArgsSchema),
|
|
140
|
+
handler: findCallSites,
|
|
141
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Task-specific tool: Find definition of a symbol
|
|
4
|
+
* Fast lookup in cache indexes to locate where something is defined
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.findDefinitionTool = void 0;
|
|
8
|
+
exports.findDefinition = findDefinition;
|
|
9
|
+
const zod_1 = require("zod");
|
|
10
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
11
|
+
const cache_1 = require("../cache");
|
|
12
|
+
const FindDefinitionArgsSchema = zod_1.z.object({
|
|
13
|
+
path: zod_1.z.string().describe('Repository path'),
|
|
14
|
+
name: zod_1.z.string().describe('Name of the symbol to find'),
|
|
15
|
+
type: zod_1.z.enum(['function', 'class', 'variable', 'type', 'any']).optional().describe('Type of symbol'),
|
|
16
|
+
max_results: zod_1.z.number().optional().describe('Maximum number of results if multiple matches'),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Find where a symbol is defined
|
|
20
|
+
*/
|
|
21
|
+
async function findDefinition(args) {
|
|
22
|
+
const { path, name, type = 'any', max_results = 5 } = args;
|
|
23
|
+
// Get cached graph
|
|
24
|
+
const cacheKey = getCacheKey(path);
|
|
25
|
+
const graph = cache_1.graphCache.get(cacheKey);
|
|
26
|
+
if (!graph) {
|
|
27
|
+
throw new Error('Graph not cached. Run explore_codebase first to analyze the repository.');
|
|
28
|
+
}
|
|
29
|
+
// Find nodes by name (case-insensitive)
|
|
30
|
+
const lowerName = name.toLowerCase();
|
|
31
|
+
const nodeIds = graph.nameIndex.get(lowerName) || [];
|
|
32
|
+
if (nodeIds.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
query_name: name,
|
|
35
|
+
query_type: type,
|
|
36
|
+
found: false,
|
|
37
|
+
results: [],
|
|
38
|
+
summary: `No definition found for "${name}".`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Filter by type if specified
|
|
42
|
+
const nodes = nodeIds
|
|
43
|
+
.map(id => graph.nodeById.get(id))
|
|
44
|
+
.filter((node) => node !== undefined && matchesType(node, type))
|
|
45
|
+
.slice(0, max_results);
|
|
46
|
+
if (nodes.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
query_name: name,
|
|
49
|
+
query_type: type,
|
|
50
|
+
found: false,
|
|
51
|
+
results: [],
|
|
52
|
+
summary: `No ${type} definition found for "${name}".`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Build results
|
|
56
|
+
const results = nodes.map(node => {
|
|
57
|
+
const props = node.properties || {};
|
|
58
|
+
return {
|
|
59
|
+
name: props.name || name,
|
|
60
|
+
type: node.labels?.[0] || 'unknown',
|
|
61
|
+
file: props.filePath || 'unknown',
|
|
62
|
+
line: props.startLine || 0,
|
|
63
|
+
end_line: props.endLine,
|
|
64
|
+
kind: props.kind,
|
|
65
|
+
context: buildContext(node),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
// Generate summary
|
|
69
|
+
const summary = generateSummary(name, type, results);
|
|
70
|
+
return {
|
|
71
|
+
query_name: name,
|
|
72
|
+
query_type: type,
|
|
73
|
+
found: true,
|
|
74
|
+
results,
|
|
75
|
+
summary,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if node matches the requested type
|
|
80
|
+
*/
|
|
81
|
+
function matchesType(node, requestedType) {
|
|
82
|
+
if (requestedType === 'any') {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
const primaryLabel = node.labels?.[0]?.toLowerCase() || '';
|
|
86
|
+
// Map requested type to node labels
|
|
87
|
+
const typeMap = {
|
|
88
|
+
function: ['function', 'method'],
|
|
89
|
+
class: ['class', 'interface', 'struct'],
|
|
90
|
+
variable: ['variable', 'constant', 'parameter', 'field'],
|
|
91
|
+
type: ['type', 'typedef', 'enum', 'interface'],
|
|
92
|
+
};
|
|
93
|
+
const acceptedLabels = typeMap[requestedType] || [requestedType];
|
|
94
|
+
return acceptedLabels.some(label => primaryLabel.includes(label));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Build context string for a definition
|
|
98
|
+
*/
|
|
99
|
+
function buildContext(node) {
|
|
100
|
+
const props = node.properties || {};
|
|
101
|
+
const label = node.labels?.[0] || 'symbol';
|
|
102
|
+
const parts = [];
|
|
103
|
+
// Add scope if available
|
|
104
|
+
if (props.scope) {
|
|
105
|
+
parts.push(`in ${props.scope}`);
|
|
106
|
+
}
|
|
107
|
+
// Add signature for functions
|
|
108
|
+
if (label === 'Function' && props.signature) {
|
|
109
|
+
parts.push(`signature: ${props.signature}`);
|
|
110
|
+
}
|
|
111
|
+
// Add modifiers
|
|
112
|
+
const modifiers = [];
|
|
113
|
+
if (props.isStatic)
|
|
114
|
+
modifiers.push('static');
|
|
115
|
+
if (props.isAsync)
|
|
116
|
+
modifiers.push('async');
|
|
117
|
+
if (props.isPublic)
|
|
118
|
+
modifiers.push('public');
|
|
119
|
+
if (props.isPrivate)
|
|
120
|
+
modifiers.push('private');
|
|
121
|
+
if (modifiers.length > 0) {
|
|
122
|
+
parts.push(modifiers.join(' '));
|
|
123
|
+
}
|
|
124
|
+
return parts.join(', ') || undefined;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Generate natural language summary
|
|
128
|
+
*/
|
|
129
|
+
function generateSummary(name, type, results) {
|
|
130
|
+
if (results.length === 0) {
|
|
131
|
+
return `No definition found for "${name}".`;
|
|
132
|
+
}
|
|
133
|
+
if (results.length === 1) {
|
|
134
|
+
const result = results[0];
|
|
135
|
+
return `${result.type} "${name}" defined in ${result.file}:${result.line}`;
|
|
136
|
+
}
|
|
137
|
+
// Multiple results
|
|
138
|
+
const typeCount = new Map();
|
|
139
|
+
for (const result of results) {
|
|
140
|
+
typeCount.set(result.type, (typeCount.get(result.type) || 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
const typeSummary = Array.from(typeCount.entries())
|
|
143
|
+
.map(([t, count]) => `${count} ${t}${count > 1 ? 's' : ''}`)
|
|
144
|
+
.join(', ');
|
|
145
|
+
return `Found ${results.length} definitions for "${name}": ${typeSummary}`;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Helper: Generate cache key
|
|
149
|
+
*/
|
|
150
|
+
function getCacheKey(path) {
|
|
151
|
+
return `cache_${path}`;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Tool metadata for MCP registration
|
|
155
|
+
*/
|
|
156
|
+
exports.findDefinitionTool = {
|
|
157
|
+
name: 'find_definition',
|
|
158
|
+
description: 'Find where a symbol (function, class, variable, type) is defined in the codebase',
|
|
159
|
+
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(FindDefinitionArgsSchema),
|
|
160
|
+
handler: findDefinition,
|
|
161
|
+
};
|