Skip to content

Conversation

@jonathanpopham
Copy link
Collaborator

@jonathanpopham jonathanpopham commented Jan 27, 2026

Summary

Adds four new MCP tools for targeted graph generation, allowing agents to request only the graph type relevant to their task:

Tool Endpoint Use Case
get_call_graph /v1/graphs/call Trace function calls, find callers/callees
get_dependency_graph /v1/graphs/dependency Understand imports, module relationships
get_domain_graph /v1/graphs/domain High-level architecture overview
get_parse_graph /v1/graphs/parse AST-level detail for refactoring

The existing explore_codebase tool (full supermodel graph) remains available for comprehensive analysis.

Motivation

  1. Faster responses - Individual graphs are smaller and faster to generate
  2. Targeted context - Agent can request only what's relevant (e.g., call graph for "what calls this function?")
  3. Lower token cost - Smaller responses = fewer tokens in context
  4. Better agent decision-making - Agent can choose the right tool for the job

Changes

  • src/tools/graph-tools.ts - New file with factory for creating graph-type-specific tools
  • src/server.ts - Register all tools (existing + new)
  • src/types.ts - Update ClientContext to use DefaultApi directly
  • src/tools/create-supermodel-graph.ts - Update to use DefaultApi

Test Plan

  • npm run build passes
  • npm test passes (95 tests)
  • Manual testing with Claude Code

Closes #81

Summary by CodeRabbit

  • New Features

    • Four graph analysis tools added (call, dependency, domain, parse); all tools now appear in listings.
  • Bug Fixes & Reliability

    • Safer tool registration with duplicate-name prevention and dynamic tool lookup.
    • Improved API client timeouts and transport handling for more robust requests.
    • Server startup logs default workdir and API configuration.
  • Documentation

    • Expanded README with tool guidance, performance/timeout notes, and local development instructions.
  • Tests

    • New unit tests for graph tools and end-to-end integration tests for the server.

✏️ Tip: You can customize this high-level summary in your review settings.

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
@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

This PR adds four new graph-analysis MCP tools, centralizes API helper utilities, updates server tool registration/dispatch to use a dynamic tool map with duplicate-name checks, adds integration and unit tests, and expands README documentation and CLI entrypoint handling.

Changes

Cohort / File(s) Summary
Graph tools & tests
src/tools/graph-tools.ts, src/tools/graph-tools.test.ts
New createGraphTool factory and four tools (get_call_graph, get_dependency_graph, get_domain_graph, get_parse_graph) with validation, ZIP packaging, idempotency, API calls, optional jq_filter, robust error classification, and tests validating metadata, input schema, error cases, and idempotency. Attention: ZIP lifecycle, error classification, and jq filtering behavior.
Server registration & integration tests
src/server.ts, src/server.integration.test.ts
Server now aggregates graphTools with explore_codebase, builds an O(1) toolMap (duplicate-name guard), ListTools returns all tools, and CallToolRequest dispatches by tool name; integration tests exercise JSON‑RPC init, tools/list, and tools/call validation. Attention: toolMap duplicate-name handling and JSON‑RPC timeouts.
Centralized helpers & create-supermodel changes
src/utils/api-helpers.ts, src/tools/create-supermodel-graph.ts, src/tools/create-supermodel-graph.test.ts
New shared utilities (generateIdempotencyKey, formatBytes, classifyApiError, reporting constants); create-supermodel-graph now imports these helpers and expands metadata; tests updated to import classifyApiError from utils. Attention: idempotency key format changes and relocated error-classification behavior.
Filtering minor update
src/filtering.ts
maybeFilter explicitly handles “no jq_filter” path, adds JSDoc and keeps same signatures. Attention: jq error detection and propagation.
Entrypoint
src/index.ts
Adds main() to parse optional default workdir, constructs Server with that workdir, and starts it; process-level error handling added.
Documentation
README.md
Adds "Individual Graph Tools" docs, usage guidance, performance/timeouts, and local dev/testing instructions (some duplication noted). Attention: duplicated sections in docs to tidy.

Sequence Diagram(s)

sequenceDiagram
    participant Client as MCP Client
    participant Server as MCP Server
    participant FS as Filesystem
    participant API as Supermodel API

    Client->>Server: tools/call (toolName="get_call_graph", directory, jq_filter?)
    Server->>Server: lookup toolMap[toolName] -> handler
    Server->>Server: validate inputs (directory, jq_filter)
    Server->>FS: zip target directory -> /tmp/repo.zip
    FS-->>Server: zipPath
    Server->>API: POST /v1/graphs/call (zip blob + idempotencyKey)
    API-->>Server: graph JSON result or error
    Server->>Server: maybeFilter(jq_filter, result)
    Server-->>Client: result or structured error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • robotson

Poem

🌲 Four small graphs went out to roam,
Zip a repo, call an API, bring findings home.
Filter with jq or keep it whole,
Tools now focused, not one big bowl.
Cheers to slices that speed the roam.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR successfully implements all core requirements from #81: four new graph tools (get_call_graph, get_dependency_graph, get_domain_graph, get_parse_graph) with same parameters, caching, and format as the API. However, a critical TypeScript error exists—DefaultApi usage instead of SupermodelClient—that blocks property access on unwrapped responses. Fix the DefaultApi/SupermodelClient mismatch: revert to SupermodelClient (client.graphs) for automatic polling/unwrapping, or implement explicit polling logic to access response.result?.summary when using DefaultApi directly.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: exposing individual graph types (call, dependency, domain, parse) as separate MCP tools instead of one combined tool.
Out of Scope Changes check ✅ Passed Changes are primarily in-scope: graph-tools factory, server registration, and helper extraction. However, some changes exceed the stated scope—centralized api-helpers module, refactored filtering.ts, and new src/index.ts entry point—though these appear to be reasonable supporting infrastructure rather than unrelated work.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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, and classifyApiError largely mirror logic in src/tools/create-supermodel-graph.ts. A small shared helper module would keep behavior consistent and reduce future bugs from mismatched changes.

@jonathanpopham jonathanpopham marked this pull request as draft January 28, 2026 18:56
@jonathanpopham
Copy link
Collaborator Author

Root Cause Analysis: CI Build Failure

The Error

src/tools/create-supermodel-graph.ts(794,30): error TS2339: Property 'summary' does not exist on type 'SupermodelIRAsync'.

SDK Architecture

The SDK has two layers:

  1. DefaultApi (generated) - Raw API client

    • Returns async job wrappers: SupermodelIRAsync, CodeGraphEnvelopeAsync, etc.
    • Job wrapper: {status, jobId, retryAfter, error, result?: ActualData}
    • Caller must poll until status === 'completed'
  2. SupermodelClient (in async.js) - Polling wrapper

    • Wraps DefaultApi, handles polling automatically
    • Returns unwrapped results directly: SupermodelIR, CodeGraphEnvelope, etc.

What Went Wrong

This PR switched from SupermodelClientDefaultApi:

// 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?.summary

Why Local Typecheck Passed

Local node_modules/@supermodeltools/sdk was corrupted - missing async.js, async.d.ts, and SupermodelIRAsync.d.ts. TypeScript saw wrong types.

Fix

Keep using SupermodelClient which handles polling and returns unwrapped results. Will update the PR to use client.graphs (SupermodelClient) instead of client.api (DefaultApi).

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'
Copy link

@coderabbitai coderabbitai bot left a 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
Copy link

@coderabbitai coderabbitai bot left a 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.

Move duplicated code from graph-tools.ts and create-supermodel-graph.ts:
- formatBytes()
- generateIdempotencyKey()
- classifyApiError()
- REPORT_REPO / REPORT_SUGGESTION constants

Uses the more comprehensive classifyApiError from create-supermodel-graph.ts
which handles 404, timeout, and network errors with detailed context.
Copy link
Collaborator Author

@jonathanpopham jonathanpopham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required before merge:

  1. Add jq_filter type validation (similar to directory check at line 277):
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".',
  });
}
  1. Add Node version constraint or import Blob explicitly (line 330):
    • Option A: Add "engines": { "node": ">=18" } to package.json
    • Option B: Add import { Blob } from 'buffer'; at top of graph-tools.ts

Optional follow-up:

  • Consider extracting shared utilities (generateIdempotencyKey, formatBytes, classifyApiError) to reduce duplication with create-supermodel-graph.ts

Address PR review feedback:
- Add type validation for jq_filter parameter (returns INVALID_JQ_FILTER error)
- Import Blob from 'buffer' module for Node.js <18 compatibility
Copy link

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tools/create-supermodel-graph.ts (1)

698-701: Import Blob from buffer or enforce Node >=18.

This code uses new Blob(...) on line 700 without importing it. While Blob is a global in Node 18+, it doesn't exist in Node 16—users on that version will hit a runtime crash saying "Blob is not defined." Since package.json doesn't specify a minimum Node version, this will break things for anyone still on Node 16.

Pick one fix: Either import Blob from the buffer module, or add "engines": { "node": ">=18" } to package.json to make it clear this project needs Node 18+.

🔧 Suggested fix (import Blob)
 import { readFile } from 'fs/promises';
 import { basename } from 'path';
+import { Blob } from 'buffer';
🤖 Fix all issues with AI agents
In `@src/tools/graph-tools.ts`:
- Around line 236-241: The catch block after calling maybeFilter should detect
jq parse errors and return a validation error instead of treating them as API
errors: when catching an error, call isJqError(error) and if true return
asErrorResult({ code: "INVALID_JQ_FILTER", message: error.message }) (or
whatever shape asErrorResult expects for validation errors) rather than
classifyApiError; otherwise keep the existing logger.error(`[${config.toolName}]
API error:`, error.message) and return asErrorResult(classifyApiError(error)).
Ensure you reference maybeFilter, isJqError, asErrorResult and classifyApiError
so the jq syntax issue yields a clear INVALID_JQ_FILTER result.

- Import Blob from 'buffer' in create-supermodel-graph.ts for Node <18
- Handle jq filter errors separately from API errors in graph-tools.ts
  using isJqError() to return INVALID_JQ_FILTER validation error
Copy link

@coderabbitai coderabbitai bot left a 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 141-185: The code silently treats an empty providedDirectory as
missing by using "providedDirectory || defaultWorkdir"; change the assignment so
the handler uses nullish coalescing (directory = providedDirectory ??
defaultWorkdir) so an explicit empty string stays as-is and the existing
validation (the subsequent check against !directory / typeof directory) will
catch and return the validation error; update the line that declares "const
directory = providedDirectory || defaultWorkdir;" accordingly in the handler.

- Add 19 unit tests covering tool exports, metadata, input schema,
  and parameter validation for all graph tools
- Replace fragile array indexing (GRAPH_TYPES[0]) with safe lookup
  using getGraphType('call') helper function
- Tests verify consistent validation across all 4 graph tools

Test count: 95 -> 114
Change || to ?? so empty string directory explicitly passed by caller
triggers validation error instead of silently falling back to default.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tools/create-supermodel-graph.ts (1)

727-730: SDK layer mismatch — this is the CI error you mentioned.

Based on the PR comments, if ClientContext was changed in types.ts to expose DefaultApi directly, then client.graphs might not be the SupermodelClient anymore. The SupermodelClient handles polling and returns the unwrapped SupermodelIR type where .summary exists. But DefaultApi.generateSupermodelGraph() returns SupermodelIRAsync — an async wrapper where the actual payload lives under .result.

So when you do:

const summary = response.summary;  // line 744

TypeScript complains because SupermodelIRAsync doesn't have .summary at the top level — you'd need response.result?.summary.

Your proposed fix is correct: revert ClientContext to use SupermodelClient (client.graphs) so polling/unwrapping is handled automatically and .summary is directly accessible.

Also, the fileBlob as any cast on line 728 is a bit of a smell — it might be hiding type mismatches between what the SDK expects and what you're passing. Once you fix the client layer, double-check if this cast is still needed.

Also applies to: 744-749

@jonathanpopham
Copy link
Collaborator Author

Manual Testing with Claude Code

Tested PR branch locally by cloning, building, and running the MCP server against its own codebase (/tmp/supermodel-mcp-test).

Setup

git clone https://github.com/supermodeltools/mcp.git /tmp/supermodel-mcp-test
cd /tmp/supermodel-mcp-test
git fetch origin pull/84/head:pr-84 && git checkout pr-84
npm install && npm run build

Tools Registered

Verified all 5 tools are exposed via tools/list:

  • explore_codebase (existing)
  • get_call_graph (new)
  • get_dependency_graph (new)
  • get_domain_graph (new)
  • get_parse_graph (new)

API Call Tests

Tool Status Result
get_call_graph Returned function nodes (runTests, sendRequest, buildIndexes, etc.)
get_dependency_graph 192 nodes showing module structure
get_domain_graph 4 domains, 8 relationships, 71 function assignments
get_parse_graph 192 nodes with AST structure (Directory, File labels)
jq_filter (valid: .domains | length) Returned 4

Validation Error Tests

Test Case Status Error Code
directory: 123 (wrong type) INVALID_DIRECTORY
jq_filter: 456 (wrong type) INVALID_JQ_FILTER
Missing directory param MISSING_DIRECTORY
Non-existent path DIRECTORY_NOT_FOUND
Invalid jq syntax (.nodes[) INVALID_JQ_FILTER with syntax details

Conclusion

All new tools work correctly with real API calls. Error handling covers all validation cases with actionable suggestions. Ready to merge.

@jonathanpopham jonathanpopham self-assigned this Jan 28, 2026
@jonathanpopham jonathanpopham marked this pull request as ready for review January 28, 2026 20:26
Copy link
Contributor

@greynewell greynewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I would prefer the test script be in JS, ideally a jest test. Also, we need to update the server instructions as well as the readme. I would also double check the tool descriptions for safety

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this as a jest test if possible

- Remove scripts/test-local.js, add Jest tests for tool safety instead
- Update server instructions to document all graph tools
- Add tool description safety tests (no unsafe instructions)
- Update README to remove test-local.js references
Address review feedback from Grey:
- Convert scripts/test-local.js to Jest integration test
- Add 11 integration tests for MCP protocol and tools
- Update server instructions to document available tools
- Update README to reference Jest tests instead of script

Test count: 114 -> 125
Copy link

@coderabbitai coderabbitai bot left a 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 `@README.md`:
- Around line 311-367: The README marks the `directory` parameter as required
for get_dependency_graph, get_domain_graph, and get_parse_graph even though the
server can use a configured default working directory; update each parameters
table for `get_dependency_graph`, `get_domain_graph`, and `get_parse_graph` to
mark `directory` as optional (or indicate "Optional if default workdir is set"),
keep `jq_filter` optional, and add a short clarifying sentence/note explaining
that if a default workdir is configured the `directory` argument may be omitted
(include a brief CLI example like `node dist/index.js /path/to/repo` or the
equivalent to show omission). Ensure the wording is consistent across all three
function docs.

In `@src/tools/graph-tools.test.ts`:
- Around line 1-149: Tests use hard-coded Unix paths ('/nonexistent/path/xyz123'
and '/default/workdir') which break on Windows or if the path exists; change
those tests in src/tools/graph-tools.test.ts to build OS-safe non-existent paths
using Node's os.tmpdir() and path.join (e.g., path.join(os.tmpdir(),
`nonexistent-${Date.now()}-${Math.random()}`)) so they are portable; update the
two cases that call callGraphTool.handler (the DIRECTORY_NOT_FOUND and
default-workdir test) to pass these generated paths instead of the hard-coded
strings.
🧹 Nitpick comments (2)
scripts/test-local.js (1)

21-25: Handle server start failures explicitly.

If dist/index.js is missing or crashes immediately, writes to stdin can throw EPIPE and the script keeps going with confusing output. A tiny error handler makes failures obvious.

Suggested tweak
 const server = spawn('node', ['dist/index.js'], {
   stdio: ['pipe', 'pipe', 'inherit'],
   env: { ...process.env }
 });
+
+server.on('error', (err) => {
+  console.error('Failed to start MCP server:', err);
+  process.exit(1);
+});
src/server.ts (1)

121-131: Guard against duplicate tool names.

If two tools accidentally share a name, the later one silently overwrites the earlier in the Map. A quick check prevents hard‑to‑trace routing bugs (e.g., get_call_graph hiding another tool).

Example guard
-    const toolMap = new Map(
-      allTools.map(t => [t.tool.name, t])
-    );
+    const toolMap = new Map();
+    for (const t of allTools) {
+      if (toolMap.has(t.tool.name)) {
+        throw new Error(`Duplicate tool name: ${t.tool.name}`);
+      }
+      toolMap.set(t.tool.name, t);
+    }

Comment on lines +311 to +367
**Parameters:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `directory` | string | Yes | Path to repository directory |
| `jq_filter` | string | No | jq filter for custom data extraction |

#### `get_dependency_graph`

Generate a module-level dependency graph showing import relationships.

**Use this to:**
- Understand module dependencies
- Find circular dependencies
- Identify tightly coupled modules
- Plan module extraction or refactoring

**Parameters:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `directory` | string | Yes | Path to repository directory |
| `jq_filter` | string | No | jq filter for custom data extraction |

#### `get_domain_graph`

Generate a high-level domain classification graph.

**Use this to:**
- Understand the architectural domains in a codebase
- See how code is organized into logical areas
- Get a bird's-eye view of system structure
- Identify domain boundaries

**Parameters:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `directory` | string | Yes | Path to repository directory |
| `jq_filter` | string | No | jq filter for custom data extraction |

#### `get_parse_graph`

Generate an AST-level parse graph with fine-grained code structure.

**Use this to:**
- Analyze detailed code structure
- Find specific syntax patterns
- Understand class/function definitions at AST level
- Support precise refactoring operations

**Parameters:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `directory` | string | Yes | Path to repository directory |
| `jq_filter` | string | No | jq filter for custom data extraction |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify that directory can be omitted when a default workdir is set.

The server supports a default working directory and passes it to tools, but these tables mark directory as strictly required. This can confuse users who rely on the CLI default (e.g., node dist/index.js /path/to/repo).

Doc tweak example
-| `directory` | string | Yes | Path to repository directory |
+| `directory` | string | Yes* | Path to repository directory |
 ...
+* If the server was started with a default working directory, `directory` can be omitted.
🤖 Prompt for AI Agents
In `@README.md` around lines 311 - 367, The README marks the `directory` parameter
as required for get_dependency_graph, get_domain_graph, and get_parse_graph even
though the server can use a configured default working directory; update each
parameters table for `get_dependency_graph`, `get_domain_graph`, and
`get_parse_graph` to mark `directory` as optional (or indicate "Optional if
default workdir is set"), keep `jq_filter` optional, and add a short clarifying
sentence/note explaining that if a default workdir is configured the `directory`
argument may be omitted (include a brief CLI example like `node dist/index.js
/path/to/repo` or the equivalent to show omission). Ensure the wording is
consistent across all three function docs.

Copy link

@coderabbitai coderabbitai bot left a 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/server.integration.test.ts`:
- Around line 6-72: The test currently assumes dist/index.js exists and the
server is ready after a fixed 500ms wait which causes flaky timeouts; update the
beforeAll setup to first check existence of distPath (use fs.existsSync on the
distPath variable and fail the test early with a clear error if missing) and
replace the fixed await new Promise(r => setTimeout(r, 500)) with a configurable
startup wait or retry loop that polls server readiness (e.g., watch
server.stdout for a ready/initialized line or attempt a lightweight JSON-RPC
ping via sendRequest with retries using requestId/responseQueue) before
proceeding; ensure server is killed on failure and include clear error messages
referencing distPath, beforeAll, sendRequest, server, and responseQueue so CI
failures are fast-failing and deterministic.

- Add JSDoc comments to filtering.ts, index.ts, and server.ts for docstring coverage
- Add guard against duplicate tool names in server.ts toolMap
- Use OS-safe paths in graph-tools.test.ts (os.tmpdir() instead of hardcoded Unix paths)
- Improve integration test startup detection with dist/index.js existence check and retry loop
Copy link

@coderabbitai coderabbitai bot left a 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/server.integration.test.ts`:
- Around line 6-11: Replace the hardcoded Unix path '/nonexistent/path/xyz123'
with an OS-safe temp-based path: add "import * as os from 'os'" (since path is
already imported) and create a variable like nonExistentDir =
path.join(os.tmpdir(),
`nonexistent-${Date.now()}-${Math.random().toString(36).slice(2)}`) and use
nonExistentDir wherever '/nonexistent/path/xyz123' appears (including the
occurrences around lines 231-235) so the tests are portable across
Windows/macOS/Linux.
- Around line 94-107: The startup wait loop can silently exit without the server
being ready — add a boolean ready flag (e.g., let ready = false) before the
loop, set ready = true when server.stdin?.writable is detected inside the loop,
and after the loop check if (!ready) throw a descriptive error (including
SERVER_STARTUP_TIMEOUT_MS or server.exitCode) to fail fast; reference the
existing symbols SERVER_STARTUP_TIMEOUT_MS, STARTUP_POLL_INTERVAL_MS,
server.stdin?.writable and server.exitCode when implementing the check.
🧹 Nitpick comments (2)
src/filtering.ts (2)

6-6: The @ts-nocheck disables all TypeScript checking for this file.

Hey! So this directive basically tells TypeScript: "Don't look at this file, trust me bro." 😅 The problem is it can hide real bugs — like if someone passes the wrong type to maybeFilter, TypeScript won't catch it.

Usually @ts-nocheck gets added when there's a tricky library (like jq-web here) that doesn't have great type definitions. If that's the case, a more surgical approach is to use @ts-expect-error or @ts-ignore only on the specific line that needs it (line 30), rather than the whole file.

Example — instead of blanket-disabling checks:

// `@ts-expect-error` jq-web types are incomplete
return (await initJq).json(json, jqFilter);

This way you still get type safety for the rest of the file. Worth considering if you have time!


38-39: Type guard could be more precise to capture the stderr property.

So right now the function checks for 'stderr' in error, but the return type only narrows to Error. This means if someone uses this guard, TypeScript won't know that stderr exists on the error object — they'd still get a type error trying to access error.stderr.

A quick fix is to make the return type more specific:

♻️ Suggested improvement
-export function isJqError(error: any): error is Error {
+export function isJqError(error: any): error is Error & { stderr: string } {
   return error instanceof Error && 'stderr' in error;
 }

Then consumers can safely do:

if (isJqError(err)) {
  console.log(err.stderr); // TypeScript knows stderr exists!
}

Comment on lines +6 to +11
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import { spawn, ChildProcess } from 'child_process';
import { existsSync } from 'fs';
import * as readline from 'readline';
import * as path from 'path';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Make the “non‑existent directory” OS‑safe.
/nonexistent/path/xyz123 is Unix‑ish; on Windows it resolves to a drive root and could theoretically exist. Using os.tmpdir() keeps it portable (same idea you used in the unit tests).

🛠️ Suggested fix
-import { existsSync } from 'fs';
+import { existsSync } from 'fs';
+import * as os from 'os';
 import * as readline from 'readline';
 import * as path from 'path';
@@
     it('should return not_found error for non-existent directory', async () => {
+      const missingDir = path.join(
+        os.tmpdir(),
+        `supermodel-missing-${Date.now()}-${Math.random().toString(36).slice(2)}`
+      );
       const result = await sendRequest('tools/call', {
         name: 'get_call_graph',
-        arguments: { directory: '/nonexistent/path/xyz123' }
+        arguments: { directory: missingDir }
       });

Also applies to: 231-235

🤖 Prompt for AI Agents
In `@src/server.integration.test.ts` around lines 6 - 11, Replace the hardcoded
Unix path '/nonexistent/path/xyz123' with an OS-safe temp-based path: add
"import * as os from 'os'" (since path is already imported) and create a
variable like nonExistentDir = path.join(os.tmpdir(),
`nonexistent-${Date.now()}-${Math.random().toString(36).slice(2)}`) and use
nonExistentDir wherever '/nonexistent/path/xyz123' appears (including the
occurrences around lines 231-235) so the tests are portable across
Windows/macOS/Linux.

Comment on lines +94 to +107
// Wait for server to be ready with retry loop
const startTime = Date.now();
while (Date.now() - startTime < SERVER_STARTUP_TIMEOUT_MS) {
// Check if server has exited unexpectedly
if (server.exitCode !== null) {
throw new Error(`Server exited unexpectedly with code ${server.exitCode}`);
}
// Small delay between checks
await new Promise(r => setTimeout(r, STARTUP_POLL_INTERVAL_MS));
// Server is ready when stdin is writable
if (server.stdin?.writable) {
break;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fail fast if the server never becomes ready.
Right now the loop just stops after 5s even if the server isn’t ready, so the next test fails later with a less obvious timeout (e.g., “Request initialize timed out”). Add a ready flag and throw right after the loop when startup doesn’t complete.

🛠️ Suggested fix
-    const startTime = Date.now();
-    while (Date.now() - startTime < SERVER_STARTUP_TIMEOUT_MS) {
+    const startTime = Date.now();
+    let ready = false;
+    while (Date.now() - startTime < SERVER_STARTUP_TIMEOUT_MS) {
       // Check if server has exited unexpectedly
       if (server.exitCode !== null) {
         throw new Error(`Server exited unexpectedly with code ${server.exitCode}`);
       }
       // Small delay between checks
       await new Promise(r => setTimeout(r, STARTUP_POLL_INTERVAL_MS));
       // Server is ready when stdin is writable
       if (server.stdin?.writable) {
-        break;
+        ready = true;
+        break;
       }
     }
+    if (!ready) {
+      throw new Error(`Server did not become ready within ${SERVER_STARTUP_TIMEOUT_MS}ms`);
+    }
📝 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.

Suggested change
// Wait for server to be ready with retry loop
const startTime = Date.now();
while (Date.now() - startTime < SERVER_STARTUP_TIMEOUT_MS) {
// Check if server has exited unexpectedly
if (server.exitCode !== null) {
throw new Error(`Server exited unexpectedly with code ${server.exitCode}`);
}
// Small delay between checks
await new Promise(r => setTimeout(r, STARTUP_POLL_INTERVAL_MS));
// Server is ready when stdin is writable
if (server.stdin?.writable) {
break;
}
}
// Wait for server to be ready with retry loop
const startTime = Date.now();
let ready = false;
while (Date.now() - startTime < SERVER_STARTUP_TIMEOUT_MS) {
// Check if server has exited unexpectedly
if (server.exitCode !== null) {
throw new Error(`Server exited unexpectedly with code ${server.exitCode}`);
}
// Small delay between checks
await new Promise(r => setTimeout(r, STARTUP_POLL_INTERVAL_MS));
// Server is ready when stdin is writable
if (server.stdin?.writable) {
ready = true;
break;
}
}
if (!ready) {
throw new Error(`Server did not become ready within ${SERVER_STARTUP_TIMEOUT_MS}ms`);
}
🤖 Prompt for AI Agents
In `@src/server.integration.test.ts` around lines 94 - 107, The startup wait loop
can silently exit without the server being ready — add a boolean ready flag
(e.g., let ready = false) before the loop, set ready = true when
server.stdin?.writable is detected inside the loop, and after the loop check if
(!ready) throw a descriptive error (including SERVER_STARTUP_TIMEOUT_MS or
server.exitCode) to fail fast; reference the existing symbols
SERVER_STARTUP_TIMEOUT_MS, STARTUP_POLL_INTERVAL_MS, server.stdin?.writable and
server.exitCode when implementing the check.

…API info

- Describe actual return types for each graph tool based on API verification
- Remove incorrect AST references from parse graph description
- Add "Returns:" section to each tool showing actual output structure
- Simplify server instructions with scannable table format
- Filed bug for dependency graph returning full graph instead of imports only
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose individual graph types as separate MCP tools

3 participants