-
Notifications
You must be signed in to change notification settings - Fork 0
feat: expose individual graph types as separate MCP tools #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Adds four new MCP tools for targeted graph generation: - get_call_graph: Function-level call relationships - get_dependency_graph: Module import relationships - get_domain_graph: High-level domain classification - get_parse_graph: AST-level code structure Each tool calls its corresponding API endpoint, allowing agents to request only the graph type relevant to their task. This provides: - Faster responses (smaller graphs) - Lower token costs (focused data) - Better tool discoverability Also fixes build issue where SupermodelClient was referenced but doesn't exist in the SDK - now uses DefaultApi directly. Closes #81
WalkthroughAdds four new graph tools (call, dependency, domain, parse) in Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Server as MCP Server
participant ToolH as Graph Tool Handler
participant Repo as Repository
participant ZIP as ZIP Ops
participant API as Graphs API
Client->>Server: CallTool(toolName, params)
Server->>Server: Lookup tool in toolMap
Server->>ToolH: Invoke handler(params)
ToolH->>Repo: Read files from path / workdir
ToolH->>ZIP: Create ZIP archive
ToolH->>ZIP: Read ZIP as Blob
ToolH->>API: POST /v1/graphs/:type (Blob + idempotencyKey)
API-->>ToolH: Graph response
ToolH->>ToolH: Apply jq_filter (optional)
ToolH-->>Server: Return wrapped text result
Server-->>Client: Return tool response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/tools/graph-tools.ts`:
- Around line 255-279: The handler currently treats non-string directory values
(e.g., directory: 123) as missing; update the validation in the handler function
to explicitly check the type of providedDirectory before falling back to
defaultWorkdir and return an asErrorResult with type 'validation_error' (and a
clear message like "Invalid 'directory' parameter: expected a string") when
providedDirectory exists but is not a string; ensure you reference the existing
variables providedDirectory, directory, args and reuse the existing error shape
(code 'MISSING_DIRECTORY' or a new code like 'INVALID_DIRECTORY') so callers get
a direct, descriptive validation error instead of "missing directory."
🧹 Nitpick comments (1)
src/tools/graph-tools.ts (1)
106-216: Consider extracting shared graph helper utilities to avoid drift
generateIdempotencyKey,formatBytes, andclassifyApiErrorlargely mirror logic insrc/tools/create-supermodel-graph.ts. A small shared helper module would keep behavior consistent and reduce future bugs from mismatched changes.
Root Cause Analysis: CI Build FailureThe ErrorSDK ArchitectureThe SDK has two layers:
What Went WrongThis PR switched from // Main uses SupermodelClient (returns SupermodelIR directly)
const response = await client.graphs.generateSupermodelGraph(file);
response.summary // ✓ Works
// PR uses DefaultApi (returns SupermodelIRAsync wrapper)
const response = await client.api.generateSupermodelGraph({...});
response.summary // ✗ Fails - data is at response.result?.summaryWhy Local Typecheck PassedLocal FixKeep using |
DefaultApi returns async job wrappers (SupermodelIRAsync) that require manual polling. SupermodelClient handles polling automatically and returns unwrapped results with .summary property. Fixes CI build failure: Property 'summary' does not exist on type 'SupermodelIRAsync'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/tools/graph-tools.ts`:
- Around line 336-339: The finally block currently awaits cleanup() directly
which can throw and mask a successful response; wrap the cleanup() call in its
own try/catch inside the finally so any error is caught and logged (e.g.,
console.error or the module's logger) and not rethrown—replace "await
cleanup();" with a try { await cleanup(); } catch (err) { /* log err only */ }
to ensure cleanup failures don't propagate.
Add explicit type check before falling back to defaultWorkdir. Returns INVALID_DIRECTORY error for values like `directory: 123` instead of confusing "missing directory" message.
Wrap cleanup() in try/catch inside finally block. Logs warning instead of throwing, so a successful graph generation isn't masked by a temp file cleanup error.
- Document get_call_graph, get_dependency_graph, get_domain_graph, get_parse_graph - Add "Choosing the Right Tool" comparison table - Add Local Development & Testing section with build/test instructions - Include test-local.js script for verifying server functionality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/tools/graph-tools.ts`:
- Around line 264-277: The code currently validates providedDirectory but lacks
a type guard for jq_filter; add a check after the args destructuring to ensure
jq_filter is either undefined or a string and if not return asErrorResult with
type 'validation_error' (similar shape to the directory error) so invalid inputs
(e.g., jq_filter: 123) produce a clear validation error before calling
maybeFilter; reference the jq_filter variable, the maybeFilter call, and the
asErrorResult helper when implementing this guard.
- Around line 327-330: The code constructs a Blob (new Blob([...])) in the try
block around readFile(zipPath), which is only available globally in Node 18+;
either enforce Node 18+ by adding an "engines": { "node": ">=18" } entry to
package.json, or (preferred for older Node support) import Blob from the buffer
module and use that: add import { Blob } from 'buffer' at the top of the module
so the new Blob([...]) call in src/tools/graph-tools.ts works on Node versions
<18; update tests/CI if you change the engine requirement.
| const { jq_filter, directory: providedDirectory } = args as { | ||
| jq_filter?: string; | ||
| directory?: string; | ||
| }; | ||
|
|
||
| if (providedDirectory !== undefined && typeof providedDirectory !== 'string') { | ||
| return asErrorResult({ | ||
| type: 'validation_error', | ||
| message: 'Invalid "directory" parameter. Provide a valid directory path as a string.', | ||
| code: 'INVALID_DIRECTORY', | ||
| recoverable: false, | ||
| suggestion: 'Pass directory as a string path, e.g. directory="/workspace/my-repo".', | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a type check for jq_filter to avoid confusing internal errors.
Right now, jq_filter: 123 would likely blow up inside maybeFilter and return an internal error. A quick guard gives a clean validation error.
♻️ Suggested patch
const { jq_filter, directory: providedDirectory } = args as {
jq_filter?: string;
directory?: string;
};
+ if (jq_filter !== undefined && typeof jq_filter !== 'string') {
+ return asErrorResult({
+ type: 'validation_error',
+ message: 'Invalid "jq_filter" parameter. Provide a jq filter string.',
+ code: 'INVALID_JQ_FILTER',
+ recoverable: false,
+ suggestion: 'Pass jq_filter as a string, e.g. jq_filter=".nodes".',
+ });
+ }
+
if (providedDirectory !== undefined && typeof providedDirectory !== 'string') {
return asErrorResult({
type: 'validation_error',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { jq_filter, directory: providedDirectory } = args as { | |
| jq_filter?: string; | |
| directory?: string; | |
| }; | |
| if (providedDirectory !== undefined && typeof providedDirectory !== 'string') { | |
| return asErrorResult({ | |
| type: 'validation_error', | |
| message: 'Invalid "directory" parameter. Provide a valid directory path as a string.', | |
| code: 'INVALID_DIRECTORY', | |
| recoverable: false, | |
| suggestion: 'Pass directory as a string path, e.g. directory="/workspace/my-repo".', | |
| }); | |
| } | |
| const { jq_filter, directory: providedDirectory } = args as { | |
| jq_filter?: string; | |
| directory?: string; | |
| }; | |
| if (jq_filter !== undefined && typeof jq_filter !== 'string') { | |
| return asErrorResult({ | |
| type: 'validation_error', | |
| message: 'Invalid "jq_filter" parameter. Provide a jq filter string.', | |
| code: 'INVALID_JQ_FILTER', | |
| recoverable: false, | |
| suggestion: 'Pass jq_filter as a string, e.g. jq_filter=".nodes".', | |
| }); | |
| } | |
| if (providedDirectory !== undefined && typeof providedDirectory !== 'string') { | |
| return asErrorResult({ | |
| type: 'validation_error', | |
| message: 'Invalid "directory" parameter. Provide a valid directory path as a string.', | |
| code: 'INVALID_DIRECTORY', | |
| recoverable: false, | |
| suggestion: 'Pass directory as a string path, e.g. directory="/workspace/my-repo".', | |
| }); | |
| } |
🤖 Prompt for AI Agents
In `@src/tools/graph-tools.ts` around lines 264 - 277, The code currently
validates providedDirectory but lacks a type guard for jq_filter; add a check
after the args destructuring to ensure jq_filter is either undefined or a string
and if not return asErrorResult with type 'validation_error' (similar shape to
the directory error) so invalid inputs (e.g., jq_filter: 123) produce a clear
validation error before calling maybeFilter; reference the jq_filter variable,
the maybeFilter call, and the asErrorResult helper when implementing this guard.
| try { | ||
| const fileBuffer = await readFile(zipPath); | ||
| const fileBlob = new Blob([fileBuffer], { type: 'application/zip' }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find and examine package.json files for Node engine constraints
echo "=== Searching for package.json files ==="
find . -name "package.json" -type f | head -20
echo ""
echo "=== Checking Node engine constraints ==="
find . -name "package.json" -type f -exec grep -H -A 3 '"engines"' {} \;Repository: supermodeltools/mcp
Length of output: 160
🏁 Script executed:
#!/bin/bash
# Read the package.json to check for engines field
echo "=== Full package.json ==="
cat ./package.json | head -50Repository: supermodeltools/mcp
Length of output: 1294
🏁 Script executed:
#!/bin/bash
# Check the file at the specified lines and surrounding context
echo "=== Viewing src/tools/graph-tools.ts around lines 327-330 ==="
sed -n '320,340p' src/tools/graph-tools.ts
echo ""
echo "=== Check all imports at the top of the file ==="
head -30 src/tools/graph-tools.ts
echo ""
echo "=== Search for Blob usage in the entire file ==="
grep -n "Blob\|undici" src/tools/graph-tools.tsRepository: supermodeltools/mcp
Length of output: 2019
Add explicit Node version constraint or import Blob from buffer.
Your code uses new Blob(...) which only exists as a global in Node 18+. Since your package.json doesn't specify engines.node, this code could theoretically run on Node 16/14 and crash at runtime with "Blob is not defined."
Pick one fix:
- Enforce Node 18+ in package.json — add
"engines": { "node": ">=18" }if you're okay requiring Node 18+ - Import Blob explicitly — safer if you need to support older versions. Just add
import { Blob } from 'buffer';since you already haveundiciavailable
Option 2 — Import Blob
import { readFile } from 'fs/promises';
+import { Blob } from 'buffer';🤖 Prompt for AI Agents
In `@src/tools/graph-tools.ts` around lines 327 - 330, The code constructs a Blob
(new Blob([...])) in the try block around readFile(zipPath), which is only
available globally in Node 18+; either enforce Node 18+ by adding an "engines":
{ "node": ">=18" } entry to package.json, or (preferred for older Node support)
import Blob from the buffer module and use that: add import { Blob } from
'buffer' at the top of the module so the new Blob([...]) call in
src/tools/graph-tools.ts works on Node versions <18; update tests/CI if you
change the engine requirement.
Summary
Adds four new MCP tools for targeted graph generation, allowing agents to request only the graph type relevant to their task:
get_call_graph/v1/graphs/callget_dependency_graph/v1/graphs/dependencyget_domain_graph/v1/graphs/domainget_parse_graph/v1/graphs/parseThe existing
explore_codebasetool (full supermodel graph) remains available for comprehensive analysis.Motivation
Changes
src/tools/graph-tools.ts- New file with factory for creating graph-type-specific toolssrc/server.ts- Register all tools (existing + new)src/types.ts- Update ClientContext to use DefaultApi directlysrc/tools/create-supermodel-graph.ts- Update to use DefaultApiAlso Fixes
The build was broken because
SupermodelClientwas being imported from the SDK but doesn't exist. Updated to useDefaultApidirectly.Test Plan
npm run buildpassesnpm testpasses (95 tests)Closes #81
Summary by CodeRabbit
New Features
Improvements
Tests
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.