Add evaluation framework and fix call graph extraction

- Add evaluation test suite with TypeScript and Python fixtures
- Fix MCP server to defer CodeGraph init until rootUri received
- Fix call edge extraction by calling resolveReferences() after indexAll/sync
- Fix glob matching for root-level files (e.g., **/*.py now matches auth.py)
- Fix duplicate node extraction for methods inside classes
- Update context tests to use buildContext for semantic search + graph traversal
- Export unused formatter functions to fix build

Evaluation results:
- TypeScript: 96% precision, 79% recall, 85% F1
- Python: 99% precision, 80% recall, 85% F1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-01-18 18:48:22 -06:00
co-authored by Claude Opus 4.5
parent e306114607
commit 6b672f9152
30 changed files with 2600 additions and 129 deletions
+57 -13
View File
@@ -42,26 +42,23 @@ export class MCPServer {
private transport: StdioTransport;
private cg: CodeGraph | null = null;
private toolHandler: ToolHandler | null = null;
private projectPath: string;
private projectPath: string | null;
private initError: string | null = null;
constructor(projectPath: string) {
this.projectPath = projectPath;
constructor(projectPath?: string) {
this.projectPath = projectPath || null;
this.transport = new StdioTransport();
}
/**
* Start the MCP server
*
* Note: CodeGraph initialization is deferred until the initialize request
* is received, which includes the rootUri from the client.
*/
async start(): Promise<void> {
// Open CodeGraph for the project
if (!CodeGraph.isInitialized(this.projectPath)) {
throw new Error(`CodeGraph not initialized in ${this.projectPath}. Run 'codegraph init' first.`);
}
this.cg = await CodeGraph.open(this.projectPath);
this.toolHandler = new ToolHandler(this.cg);
// Start listening for messages
// Start listening for messages immediately - don't check initialization yet
// We'll get the project path from the initialize request's rootUri
this.transport.start(this.handleMessage.bind(this));
// Keep the process running
@@ -69,6 +66,26 @@ export class MCPServer {
process.on('SIGTERM', () => this.stop());
}
/**
* Initialize CodeGraph for the project
*/
private async initializeCodeGraph(projectPath: string): Promise<void> {
this.projectPath = projectPath;
if (!CodeGraph.isInitialized(projectPath)) {
this.initError = `CodeGraph not initialized in ${projectPath}. Run 'codegraph init' first.`;
return;
}
try {
this.cg = await CodeGraph.open(projectPath);
this.toolHandler = new ToolHandler(this.cg);
this.initError = null;
} catch (err) {
this.initError = `Failed to open CodeGraph: ${err instanceof Error ? err.message : String(err)}`;
}
}
/**
* Stop the server
*/
@@ -133,6 +150,29 @@ export class MCPServer {
* Handle initialize request
*/
private async handleInitialize(request: JsonRpcRequest): Promise<void> {
const params = request.params as {
rootUri?: string;
workspaceFolders?: Array<{ uri: string; name: string }>;
} | undefined;
// Extract project path from rootUri or workspaceFolders
let projectPath = this.projectPath;
if (params?.rootUri) {
// Convert file:// URI to path
projectPath = params.rootUri.replace(/^file:\/\//, '');
} else if (params?.workspaceFolders?.[0]?.uri) {
projectPath = params.workspaceFolders[0].uri.replace(/^file:\/\//, '');
}
// Fall back to current working directory if no path provided
if (!projectPath) {
projectPath = process.cwd();
}
// Initialize CodeGraph if we have a project path
await this.initializeCodeGraph(projectPath);
// We accept the client's protocol version but respond with our supported version
this.transport.sendResult(request.id, {
protocolVersion: PROTOCOL_VERSION,
@@ -186,10 +226,14 @@ export class MCPServer {
// Execute the tool
if (!this.toolHandler) {
const errorMsg = this.initError ||
(this.projectPath
? `CodeGraph not initialized in ${this.projectPath}. Run 'codegraph init' first.`
: 'No project path provided. Ensure Claude Code is running in a project directory.');
this.transport.sendError(
request.id,
ErrorCodes.InternalError,
'Server not initialized'
errorMsg
);
return;
}