code-graph-context 2.5.4 → 2.6.1

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.
@@ -23,12 +23,12 @@ export class GraphGeneratorHandler {
23
23
  this.projectId = projectId;
24
24
  }
25
25
  async generateGraph(graphJsonPath, batchSize = DEFAULTS.batchSize, clearExisting = true) {
26
- console.log(`Generating graph from JSON file: ${graphJsonPath}`);
26
+ console.error(`Generating graph from JSON file: ${graphJsonPath}`);
27
27
  await debugLog('Starting graph generation', { graphJsonPath, batchSize, clearExisting, projectId: this.projectId });
28
28
  try {
29
29
  const graphData = await this.loadGraphData(graphJsonPath);
30
30
  const { nodes, edges, metadata } = graphData;
31
- console.log(`Generating graph with ${nodes.length} nodes and ${edges.length} edges`);
31
+ console.error(`Generating graph with ${nodes.length} nodes and ${edges.length} edges`);
32
32
  await debugLog('Graph data loaded', { nodeCount: nodes.length, edgeCount: edges.length });
33
33
  if (clearExisting) {
34
34
  await this.clearExistingData();
@@ -57,18 +57,18 @@ export class GraphGeneratorHandler {
57
57
  }
58
58
  async clearExistingData() {
59
59
  if (this.projectId) {
60
- console.log(`Clearing existing graph data for project: ${this.projectId}...`);
60
+ console.error(`Clearing existing graph data for project: ${this.projectId}...`);
61
61
  await this.neo4jService.run(QUERIES.CLEAR_PROJECT, { projectId: this.projectId });
62
62
  await debugLog('Existing project graph data cleared', { projectId: this.projectId });
63
63
  }
64
64
  else {
65
- console.log('Clearing ALL existing graph data (no projectId set)...');
65
+ console.error('Clearing ALL existing graph data (no projectId set)...');
66
66
  await this.neo4jService.run(QUERIES.CLEAR_DATABASE);
67
67
  await debugLog('Existing graph data cleared');
68
68
  }
69
69
  }
70
70
  async createProjectIndexes() {
71
- console.log('Creating project indexes...');
71
+ console.error('Creating project indexes...');
72
72
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_INDEX_EMBEDDED);
73
73
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_INDEX_SOURCEFILE);
74
74
  await this.neo4jService.run(QUERIES.CREATE_PROJECT_ID_INDEX_EMBEDDED);
@@ -77,12 +77,12 @@ export class GraphGeneratorHandler {
77
77
  await debugLog('Project indexes created');
78
78
  }
79
79
  async importNodes(nodes, batchSize) {
80
- console.log(`Importing ${nodes.length} nodes with embeddings...`);
80
+ console.error(`Importing ${nodes.length} nodes with embeddings...`);
81
81
  for (let i = 0; i < nodes.length; i += batchSize) {
82
82
  const batch = await this.processNodeBatch(nodes.slice(i, i + batchSize));
83
83
  const result = await this.neo4jService.run(QUERIES.CREATE_NODE, { nodes: batch });
84
84
  const batchEnd = Math.min(i + batchSize, nodes.length);
85
- console.log(`Created ${result[0].created} nodes in batch ${i + 1}-${batchEnd}`);
85
+ console.error(`Created ${result[0].created} nodes in batch ${i + 1}-${batchEnd}`);
86
86
  await debugLog('Node batch imported', {
87
87
  batchStart: i + 1,
88
88
  batchEnd,
@@ -158,7 +158,7 @@ export class GraphGeneratorHandler {
158
158
  return nodeResults;
159
159
  }
160
160
  async importEdges(edges, batchSize) {
161
- console.log(`Importing ${edges.length} edges using APOC...`);
161
+ console.error(`Importing ${edges.length} edges using APOC...`);
162
162
  for (let i = 0; i < edges.length; i += batchSize) {
163
163
  const batch = edges.slice(i, i + batchSize).map((edge) => ({
164
164
  ...edge,
@@ -169,7 +169,7 @@ export class GraphGeneratorHandler {
169
169
  projectId: this.projectId,
170
170
  });
171
171
  const batchEnd = Math.min(i + batchSize, edges.length);
172
- console.log(`Created ${result[0].created} edges in batch ${i + 1}-${batchEnd}`);
172
+ console.error(`Created ${result[0].created} edges in batch ${i + 1}-${batchEnd}`);
173
173
  await debugLog('Edge batch imported', {
174
174
  batchStart: i + 1,
175
175
  batchEnd,
@@ -178,7 +178,7 @@ export class GraphGeneratorHandler {
178
178
  }
179
179
  }
180
180
  async createVectorIndexes() {
181
- console.log('Creating vector indexes...');
181
+ console.error('Creating vector indexes...');
182
182
  await this.neo4jService.run(QUERIES.CREATE_EMBEDDED_VECTOR_INDEX);
183
183
  await debugLog('Vector indexes created');
184
184
  }
@@ -153,7 +153,7 @@ export class TraversalHandler {
153
153
  const nodeMap = new Map(); // Dedupe nodes
154
154
  for (let depth = 1; depth <= maxDepth; depth++) {
155
155
  if (currentSourceIds.length === 0) {
156
- console.log(`No source nodes to explore at depth ${depth}`);
156
+ console.error(`No source nodes to explore at depth ${depth}`);
157
157
  break;
158
158
  }
159
159
  const traversalResults = await this.neo4jService.run(QUERIES.EXPLORE_DEPTH_LEVEL(direction, maxNodesPerDepth), {
@@ -165,7 +165,7 @@ export class TraversalHandler {
165
165
  projectId,
166
166
  });
167
167
  if (traversalResults.length === 0) {
168
- console.log(`No connections found at depth ${depth}`);
168
+ console.error(`No connections found at depth ${depth}`);
169
169
  break;
170
170
  }
171
171
  // Collect node IDs for next depth exploration
@@ -13,7 +13,7 @@ const __filename = fileURLToPath(import.meta.url);
13
13
  const __dirname = dirname(__filename);
14
14
  // Go up two levels from dist/mcp/mcp.server.js to the root
15
15
  const rootDir = join(__dirname, '..', '..');
16
- dotenv.config({ path: join(rootDir, '.env') });
16
+ dotenv.config({ path: join(rootDir, '.env'), quiet: true });
17
17
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
18
18
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
19
  import { MCP_SERVER_CONFIG, MESSAGES } from './constants.js';
@@ -33,7 +33,7 @@ class JobManager {
33
33
  this.cleanupInterval = setInterval(() => {
34
34
  const cleaned = this.cleanupOldJobs();
35
35
  if (cleaned > 0) {
36
- console.log(`[JobManager] Cleaned up ${cleaned} old jobs`);
36
+ console.error(`[JobManager] Cleaned up ${cleaned} old jobs`);
37
37
  }
38
38
  }, JOBS.cleanupIntervalMs);
39
39
  // Don't prevent Node.js from exiting if this is the only timer
@@ -63,7 +63,7 @@ class JobManager {
63
63
  `Please wait for jobs to complete or cancel existing jobs.`);
64
64
  }
65
65
  if (cleaned > 0) {
66
- console.log(`[JobManager] Auto-cleaned ${cleaned} old jobs to make room for new job`);
66
+ console.error(`[JobManager] Auto-cleaned ${cleaned} old jobs to make room for new job`);
67
67
  }
68
68
  }
69
69
  const id = generateJobId();
@@ -249,7 +249,7 @@ class WatchManager {
249
249
  },
250
250
  timestamp: new Date().toISOString(),
251
251
  });
252
- console.log(`[WatchManager] Incremental parse completed for ${state.projectId}: ` +
252
+ console.error(`[WatchManager] Incremental parse completed for ${state.projectId}: ` +
253
253
  `${result.nodesUpdated} nodes, ${result.edgesUpdated} edges in ${elapsedMs}ms`);
254
254
  }
255
255
  catch (error) {
@@ -309,7 +309,7 @@ class WatchManager {
309
309
  edgesUpdated: result.edgesUpdated
310
310
  });
311
311
  if (result.nodesUpdated > 0 || result.edgesUpdated > 0) {
312
- console.log(`[WatchManager] Synced missed changes for ${state.projectId}: ` +
312
+ console.error(`[WatchManager] Synced missed changes for ${state.projectId}: ` +
313
313
  `${result.nodesUpdated} nodes, ${result.edgesUpdated} edges`);
314
314
  }
315
315
  })
@@ -370,7 +370,7 @@ class WatchManager {
370
370
  }
371
371
  }
372
372
  this.watchers.delete(projectId);
373
- console.log(`[WatchManager] Stopped watching project: ${projectId}`);
373
+ console.error(`[WatchManager] Stopped watching project: ${projectId}`);
374
374
  return true;
375
375
  }
376
376
  /**
@@ -394,7 +394,7 @@ class WatchManager {
394
394
  async stopAllWatchers() {
395
395
  const projectIds = Array.from(this.watchers.keys());
396
396
  await Promise.all(projectIds.map((id) => this.stopWatching(id)));
397
- console.log(`[WatchManager] Stopped all ${projectIds.length} watchers`);
397
+ console.error(`[WatchManager] Stopped all ${projectIds.length} watchers`);
398
398
  }
399
399
  /**
400
400
  * Convert internal state to public info
@@ -158,12 +158,6 @@ export const createDetectDeadCodeTool = (server) => {
158
158
  if (!projectResult.success)
159
159
  return projectResult.error;
160
160
  const resolvedProjectId = projectResult.projectId;
161
- await debugLog('Dead code detection started', {
162
- projectId: resolvedProjectId,
163
- excludePatterns,
164
- excludeSemanticTypes,
165
- minConfidence,
166
- });
167
161
  // Query project's actual semantic types (data-driven, per-project detection)
168
162
  const semanticTypesResult = (await neo4jService.run(QUERIES.GET_PROJECT_SEMANTIC_TYPES, {
169
163
  projectId: resolvedProjectId,
@@ -389,16 +383,6 @@ export const createDetectDeadCodeTool = (server) => {
389
383
  affectedFiles,
390
384
  };
391
385
  }
392
- await debugLog('Dead code detection complete', {
393
- projectId: resolvedProjectId,
394
- totalCount: deadCodeItems.length,
395
- filteredCount: filteredItems.length,
396
- filterCategory,
397
- riskLevel,
398
- summaryOnly,
399
- offset,
400
- limit,
401
- });
402
386
  return createSuccessResponse(JSON.stringify(result, null, 2));
403
387
  }
404
388
  catch (error) {
@@ -151,12 +151,6 @@ export const createDetectDuplicateCodeTool = (server) => {
151
151
  if (!projectResult.success)
152
152
  return projectResult.error;
153
153
  const resolvedProjectId = projectResult.projectId;
154
- await debugLog('Duplicate code detection started', {
155
- projectId: resolvedProjectId,
156
- type,
157
- minSimilarity,
158
- scope,
159
- });
160
154
  const coreTypes = getScopeFilter(scope);
161
155
  const duplicateGroups = [];
162
156
  let groupCounter = 1;
@@ -320,16 +314,6 @@ export const createDetectDuplicateCodeTool = (server) => {
320
314
  usedInSemanticGroup.add(nodeId1);
321
315
  usedInSemanticGroup.add(nodeId2);
322
316
  }
323
- // Log semantic query diagnostics
324
- await debugLog('Semantic query diagnostics', {
325
- rawResults: semanticQueryResults,
326
- filteredAsSameFile,
327
- filteredAsSeenPair,
328
- filteredAsStructural,
329
- filteredAsUsedInGroup,
330
- structuralPairsCount: structuralPairs.size,
331
- finalSemanticGroups: duplicateGroups.filter((g) => g.type === 'semantic').length,
332
- });
333
317
  }
334
318
  // Sort by similarity (descending)
335
319
  duplicateGroups.sort((a, b) => b.similarity - a.similarity);
@@ -367,8 +351,6 @@ export const createDetectDuplicateCodeTool = (server) => {
367
351
  message: `${embeddingCount} nodes have embeddings but no semantic duplicates found above ${minSimilarity} similarity threshold.`,
368
352
  };
369
353
  }
370
- // Log diagnostic so user sees it in debug output
371
- await debugLog('Semantic duplicate diagnostic', semanticDiagnostic);
372
354
  }
373
355
  // Build summary with warning if no embeddings
374
356
  let summary = totalGroups === 0
@@ -427,15 +409,6 @@ export const createDetectDuplicateCodeTool = (server) => {
427
409
  if (semanticDiagnostic) {
428
410
  result.semanticDiagnostic = semanticDiagnostic;
429
411
  }
430
- await debugLog('Duplicate code detection complete', {
431
- projectId: resolvedProjectId,
432
- totalGroups,
433
- structuralGroups: allStructuralGroups.length,
434
- semanticGroups: allSemanticGroups.length,
435
- summaryOnly,
436
- offset,
437
- maxResults,
438
- });
439
412
  return createSuccessResponse(JSON.stringify(result, null, 2));
440
413
  }
441
414
  catch (error) {
@@ -81,13 +81,6 @@ export const createImpactAnalysisTool = (server) => {
81
81
  if (!nodeId && !filePath) {
82
82
  return createErrorResponse('Either nodeId or filePath must be provided');
83
83
  }
84
- await debugLog('Impact analysis started', {
85
- projectId: resolvedProjectId,
86
- nodeId,
87
- filePath,
88
- maxDepth,
89
- frameworkConfig,
90
- });
91
84
  // Merge default weights with framework-specific weights
92
85
  const weights = { ...DEFAULT_RELATIONSHIP_WEIGHTS, ...frameworkConfig?.relationshipWeights };
93
86
  const highRiskTypes = new Set(frameworkConfig?.highRiskTypes ?? []);
@@ -213,12 +206,6 @@ export const createImpactAnalysisTool = (server) => {
213
206
  affectedFiles,
214
207
  criticalPaths,
215
208
  };
216
- await debugLog('Impact analysis complete', {
217
- nodeId: nodeId ?? filePath,
218
- riskLevel,
219
- directCount: directDependents.length,
220
- transitiveCount: transitiveDependents.length,
221
- });
222
209
  return createSuccessResponse(JSON.stringify(result, null, 2));
223
210
  }
224
211
  catch (error) {
@@ -2,7 +2,6 @@
2
2
  * MCP Tool Factory
3
3
  * Centralized tool creation and registration
4
4
  */
5
- import { debugLog } from '../utils.js';
6
5
  import { createCheckParseStatusTool } from './check-parse-status.tool.js';
7
6
  import { createDetectDeadCodeTool } from './detect-dead-code.tool.js';
8
7
  import { createDetectDuplicateCodeTool } from './detect-duplicate-code.tool.js';
@@ -30,25 +29,15 @@ let globalToolCallCount = 0;
30
29
  /**
31
30
  * Log tool call start (exported for use by individual tools)
32
31
  */
33
- export const logToolCallStart = async (toolName, params) => {
32
+ export const logToolCallStart = async (_toolName, _params) => {
34
33
  globalToolCallCount++;
35
- const callId = globalToolCallCount;
36
- await debugLog(`Tool call START: ${toolName}`, {
37
- callId,
38
- totalCalls: globalToolCallCount,
39
- params: params ? JSON.stringify(params).substring(0, 500) : 'none',
40
- });
41
- return callId;
34
+ return globalToolCallCount;
42
35
  };
43
36
  /**
44
37
  * Log tool call end (exported for use by individual tools)
45
38
  */
46
- export const logToolCallEnd = async (toolName, callId, success, duration) => {
47
- await debugLog(`Tool call END: ${toolName}`, {
48
- callId,
49
- success,
50
- duration: duration ? `${duration}ms` : 'unknown',
51
- });
39
+ export const logToolCallEnd = async (_toolName, _callId, _success, _duration) => {
40
+ // No-op - verbose logging disabled
52
41
  };
53
42
  /**
54
43
  * Register all MCP tools with the server
@@ -14,7 +14,6 @@ export const createListProjectsTool = (server) => {
14
14
  }, async () => {
15
15
  const neo4jService = new Neo4jService();
16
16
  try {
17
- await debugLog('Listing projects');
18
17
  const results = await neo4jService.run(LIST_PROJECTS_QUERY, {});
19
18
  if (results.length === 0) {
20
19
  return createSuccessResponse('No projects found. Use parse_typescript_project to add a project first.');
@@ -28,7 +27,6 @@ export const createListProjectsTool = (server) => {
28
27
  edgeCount: r.edgeCount,
29
28
  updatedAt: r.updatedAt?.toString() ?? 'Unknown',
30
29
  }));
31
- await debugLog('Projects listed', { count: projects.length });
32
30
  // Format output for readability
33
31
  const header = `Found ${projects.length} project(s):\n\n`;
34
32
  const formatStats = (p) => {
@@ -12,7 +12,6 @@ export const createListWatchersTool = (server) => {
12
12
  inputSchema: {},
13
13
  }, async () => {
14
14
  try {
15
- await debugLog('Listing watchers');
16
15
  const watchers = watchManager.listWatchers();
17
16
  if (watchers.length === 0) {
18
17
  return createSuccessResponse('No active file watchers.\n\n' +
@@ -20,7 +19,6 @@ export const createListWatchersTool = (server) => {
20
19
  '- Use start_watch_project with a projectId\n' +
21
20
  '- Or use parse_typescript_project with watch: true (requires async: false)');
22
21
  }
23
- await debugLog('Watchers listed', { count: watchers.length });
24
22
  const header = `Found ${watchers.length} active watcher(s):\n\n`;
25
23
  const watcherList = watchers
26
24
  .map((w) => {
@@ -47,7 +47,6 @@ export const createNaturalLanguageToCypherTool = (server) => {
47
47
  await debugLog('Natural language service not available', { projectId: resolvedProjectId, query });
48
48
  return createSuccessResponse(MESSAGES.errors.serviceNotInitialized);
49
49
  }
50
- await debugLog('Natural language to Cypher conversion started', { projectId: resolvedProjectId, query });
51
50
  const cypherResult = await naturalLanguageToCypherService.promptToQuery(query, resolvedProjectId);
52
51
  // Validate Cypher syntax using EXPLAIN (no execution, just parse)
53
52
  const parameters = { ...cypherResult.parameters, projectId: resolvedProjectId };
@@ -67,11 +66,6 @@ export const createNaturalLanguageToCypherTool = (server) => {
67
66
  }
68
67
  // Execute the validated query
69
68
  const results = await neo4jService.run(cypherResult.cypher, parameters);
70
- await debugLog('Cypher query executed', {
71
- projectId: resolvedProjectId,
72
- cypher: cypherResult.cypher,
73
- resultsCount: results.length,
74
- });
75
69
  const formattedResponse = formatQueryResults(results, query, cypherResult);
76
70
  return createSuccessResponse(JSON.stringify(formattedResponse, null, 2));
77
71
  }
@@ -225,10 +225,10 @@ export const createParseTypescriptProjectTool = (server) => {
225
225
  const discoveredFiles = await parser.discoverSourceFiles();
226
226
  const totalFiles = discoveredFiles.length;
227
227
  const shouldUseStreaming = useStreaming === 'always' || (useStreaming === 'auto' && totalFiles > PARSING.streamingThreshold && chunkSize > 0);
228
- console.log(`📊 Project has ${totalFiles} files. Streaming: ${shouldUseStreaming ? 'enabled' : 'disabled'}`);
228
+ console.error(`📊 Project has ${totalFiles} files. Streaming: ${shouldUseStreaming ? 'enabled' : 'disabled'}`);
229
229
  if (shouldUseStreaming && clearExisting !== false) {
230
230
  // Use streaming import for large projects
231
- console.log(`🚀 Using streaming import with chunk size ${chunkSize}`);
231
+ console.error(`🚀 Using streaming import with chunk size ${chunkSize}`);
232
232
  await debugLog('Using streaming import', { totalFiles, chunkSize });
233
233
  // Create Project node BEFORE starting import (status: parsing)
234
234
  const projectName = await getProjectName(projectPath);
@@ -298,7 +298,7 @@ export const createParseTypescriptProjectTool = (server) => {
298
298
  projectType,
299
299
  });
300
300
  const { nodes, edges, savedCrossFileEdges, resolvedProjectId: finalProjectId } = graphData;
301
- console.log(`Parsed ${nodes.length} nodes / ${edges.length} edges for project ${finalProjectId}`);
301
+ console.error(`Parsed ${nodes.length} nodes / ${edges.length} edges for project ${finalProjectId}`);
302
302
  await debugLog('Parsing completed', {
303
303
  nodeCount: nodes.length,
304
304
  edgeCount: edges.length,
@@ -306,7 +306,7 @@ export const createParseTypescriptProjectTool = (server) => {
306
306
  });
307
307
  const outputPath = join(projectPath, FILE_PATHS.graphOutput);
308
308
  writeFileSync(outputPath, JSON.stringify(graphData, null, LOG_CONFIG.jsonIndentation));
309
- console.log(`Graph data written to ${outputPath}`);
309
+ console.error(`Graph data written to ${outputPath}`);
310
310
  try {
311
311
  // Set projectId for project-scoped operations (clear, indexes)
312
312
  graphGeneratorHandler.setProjectId(finalProjectId);
@@ -321,7 +321,7 @@ export const createParseTypescriptProjectTool = (server) => {
321
321
  const recreatedCount = recreateResult[0]?.recreatedCount ?? 0;
322
322
  await debugLog('Cross-file edges recreated', { recreatedCount, expected: savedCrossFileEdges.length });
323
323
  }
324
- console.log('Graph generation completed:', result);
324
+ console.error('Graph generation completed:', result);
325
325
  await debugLog('Neo4j import completed', result);
326
326
  // Update Project node status to complete
327
327
  await neo4jService.run(UPDATE_PROJECT_STATUS_QUERY, {
@@ -63,7 +63,6 @@ export const createSearchCodebaseTool = (server) => {
63
63
  const sanitizedMaxNodesPerChain = sanitizeNumericInput(maxNodesPerChain, 5);
64
64
  const sanitizedSkip = sanitizeNumericInput(skip, 0);
65
65
  const sanitizedSnippetLength = sanitizeNumericInput(snippetLength, DEFAULTS.codeSnippetLength);
66
- await debugLog('Search codebase started', { projectId: resolvedProjectId, query });
67
66
  const embeddingsService = new EmbeddingsService();
68
67
  const traversalHandler = new TraversalHandler(neo4jService);
69
68
  const embedding = await embeddingsService.embedText(query);
@@ -75,7 +74,6 @@ export const createSearchCodebaseTool = (server) => {
75
74
  minSimilarity,
76
75
  });
77
76
  if (vectorResults.length === 0) {
78
- await debugLog('No relevant code found', { projectId: resolvedProjectId, query, minSimilarity });
79
77
  return createSuccessResponse(`No code found with similarity >= ${minSimilarity}. ` +
80
78
  `Try rephrasing your query or lowering the minSimilarity threshold. Query: "${query}"`);
81
79
  }
@@ -84,26 +82,9 @@ export const createSearchCodebaseTool = (server) => {
84
82
  const similarityScore = vectorResults[0].score;
85
83
  // Check if best match meets threshold - prevents traversing low-relevance results
86
84
  if (similarityScore < minSimilarity) {
87
- await debugLog('Best match below similarity threshold', {
88
- projectId: resolvedProjectId,
89
- query,
90
- score: similarityScore,
91
- threshold: minSimilarity,
92
- });
93
85
  return createSuccessResponse(`No sufficiently relevant code found. Best match score: ${similarityScore.toFixed(3)} ` +
94
86
  `(threshold: ${minSimilarity}). Try rephrasing your query.`);
95
87
  }
96
- await debugLog('Vector search completed, starting traversal', {
97
- projectId: resolvedProjectId,
98
- nodeId,
99
- similarityScore,
100
- resultsCount: vectorResults.length,
101
- maxDepth: sanitizedMaxDepth,
102
- maxNodesPerChain: sanitizedMaxNodesPerChain,
103
- skip: sanitizedSkip,
104
- includeCode,
105
- snippetLength: sanitizedSnippetLength,
106
- });
107
88
  // Include similarity score in the title so users can see relevance
108
89
  const scoreDisplay = typeof similarityScore === 'number' ? similarityScore.toFixed(3) : 'N/A';
109
90
  return await traversalHandler.traverseFromNode(nodeId, embedding, {