build-knowledge-graph
Analyze code repositories to create dependency graphs and visualize relationships for better understanding of project structure and external dependencies.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repositoryUrl | Yes | ||
| depth | No | ||
| includeExternalDependencies | No |
Implementation Reference
- src/features/knowledge-graph/index.ts:15-46 (registration)Registration of the MCP tool 'build-knowledge-graph', including Zod input schema validation and thin handler wrapper that calls the core buildKnowledgeGraph function and formats response.server.tool( "build-knowledge-graph", { repositoryUrl: z.string(), depth: z.number().default(2), includeExternalDependencies: z.boolean().default(true) }, async ({ repositoryUrl, depth, includeExternalDependencies }) => { try { const result = await buildKnowledgeGraph( repositoryUrl, depth, includeExternalDependencies ); return { content: [{ type: "text", text: `Knowledge graph built successfully. Nodes: ${result.nodes}, Relationships: ${result.relationships}` }] }; } catch (error) { return { content: [{ type: "text", text: `Error building knowledge graph: ${(error as Error).message}` }], isError: true }; } } );
- Input schema for the tool using Zod: repository URL (required), analysis depth (default 2), include external deps (default true).{ repositoryUrl: z.string(), depth: z.number().default(2), includeExternalDependencies: z.boolean().default(true) },
- Core handler logic for building the knowledge graph: initializes SQLite DB, clones repository, lists files, analyzes each file's code to extract imports/classes/functions, creates corresponding graph nodes and relationships (contains, imports, defines), commits transaction.export async function buildKnowledgeGraph( repositoryUrl: string, depth: number = 2, includeExternalDependencies: boolean = true ): Promise<{ nodes: number, relationships: number }> { // Initialize db if needed if (!db) { db = await createDatabase("knowledge"); } if (!db) { throw new Error("Database not initialized"); } // Get repository const repoPath = await getRepository(repositoryUrl); const files = listFiles(repoPath); let nodesCount = 0; let relationshipsCount = 0; await db.exec('BEGIN TRANSACTION'); try { // Add repository node const repoId = `repo:${uuidv4()}`; addNode({ id: repoId, type: "repository", name: path.basename(repositoryUrl), attributes: { url: repositoryUrl, fileCount: files.length } }); nodesCount++; // Process each file for (const file of files) { const fullPath = path.join(repoPath, file); try { const code = fs.readFileSync(fullPath, 'utf8'); const fileLanguage = path.extname(file).slice(1); // Create file node const fileId = `file:${uuidv4()}`; addNode({ id: fileId, type: "file", name: file, attributes: { language: fileLanguage, size: code.length, path: file } }); nodesCount++; // Link file to repository addRelationship({ id: `rel:${uuidv4()}`, type: "contains", sourceId: repoId, targetId: fileId, attributes: {} }); relationshipsCount++; // Analyze code to get dependencies, classes, functions const analysis = analyzeCode(code, fileLanguage); // Process imports/dependencies for (const importItem of analysis.imports) { let targetId: string; // Check if the import already exists as a node const existingNode = await findNodeByName(importItem); if (existingNode) { targetId = existingNode.id; } else { // Create a new node for the import targetId = `dep:${uuidv4()}`; addNode({ id: targetId, type: "dependency", name: importItem, attributes: { isExternal: !files.some(f => f.endsWith(importItem) || f.includes(importItem)) } }); nodesCount++; } // Link file to dependency addRelationship({ id: `rel:${uuidv4()}`, type: "imports", sourceId: fileId, targetId: targetId, attributes: {} }); relationshipsCount++; } // Process classes for (const className of analysis.classes) { const classId = `class:${uuidv4()}`; addNode({ id: classId, type: "class", name: className, attributes: { file: file } }); nodesCount++; // Link class to file addRelationship({ id: `rel:${uuidv4()}`, type: "defines", sourceId: fileId, targetId: classId, attributes: {} }); relationshipsCount++; } // Process functions for (const funcName of analysis.functions) { const funcId = `func:${uuidv4()}`; addNode({ id: funcId, type: "function", name: funcName, attributes: { file: file } }); nodesCount++; // Link function to file addRelationship({ id: `rel:${uuidv4()}`, type: "defines", sourceId: fileId, targetId: funcId, attributes: {} }); relationshipsCount++; } } catch (error) { console.warn(`Error processing file ${file}: ${(error as Error).message}`); } } await db.exec('COMMIT'); return { nodes: nodesCount, relationships: relationshipsCount }; } catch (error) { await db.exec('ROLLBACK'); throw error; } }
- Helper function to add or update a node in the knowledge graph database.async function addNode(node: GraphNode): Promise<void> { if (!db) { db = await createDatabase("knowledge"); } if (!db) { throw new Error("Database not initialized"); } await db.run( `INSERT OR REPLACE INTO nodes (id, type, name, attributes) VALUES (?, ?, ?, ?)`, [node.id, node.type, node.name, JSON.stringify(node.attributes)] ); }
- Helper function to add or update a relationship in the knowledge graph database.async function addRelationship(relationship: GraphRelationship): Promise<void> { if (!db) { db = await createDatabase("knowledge"); } if (!db) { throw new Error("Database not initialized"); } await db.run( `INSERT OR REPLACE INTO relationships (id, type, sourceId, targetId, attributes) VALUES (?, ?, ?, ?, ?)`, [ relationship.id, relationship.type, relationship.sourceId, relationship.targetId, JSON.stringify(relationship.attributes) ] ); }