UNPKG

ts-project-indexer-mcp

Version:

A powerful MCP (Model Context Protocol) server that indexes and analyzes TypeScript/JavaScript projects, providing intelligent code exploration, dependency tracking, method discovery, and project structure analysis for AI assistants and development tools.

269 lines 12.4 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { ProjectIndexer } from './core/indexer.js'; import { CacheManager } from './cache/cache-manager.js'; import { SimpleFileWatcher } from './watchers/simple-file-watcher.js'; import { analyzeProjectTool } from './tools/analyze-project.js'; import { searchMethodsTool } from './tools/search-methods.js'; import { findDependenciesTool } from './tools/find-dependencies.js'; import { generateGraphTool } from './tools/generate-graph.js'; import { FilesResource } from './resources/files-resource.js'; import { MethodsResource } from './resources/methods-resource.js'; import { PathsResource } from './resources/paths-resource.js'; import { logger } from './mcp-server.js'; class ProjectIndexerServer { server; indexer; cache; watcher; resources; constructor() { this.server = new Server({ name: 'mcp-project-indexer', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, }); // Initialize core services this.cache = new CacheManager(); this.indexer = new ProjectIndexer(this.cache); this.watcher = new SimpleFileWatcher(this.indexer); // Initialize resources this.resources = { files: new FilesResource(this.indexer), methods: new MethodsResource(this.indexer), paths: new PathsResource(this.indexer), }; this.setupHandlers(); } setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'analyze_project', description: 'Analyze and index the entire project structure', inputSchema: { type: 'object', properties: { projectPath: { type: 'string', description: 'Path to the project root directory', default: '../', }, includePatterns: { type: 'array', items: { type: 'string' }, description: 'File patterns to include (glob patterns)', default: ['**/*.ts', '**/*.js', '**/*.json'], }, excludePatterns: { type: 'array', items: { type: 'string' }, description: 'File patterns to exclude (glob patterns)', default: ['node_modules/**', 'dist/**', '**/*.d.ts'], }, forceReindex: { type: 'boolean', description: 'Force full reindexing even if cache exists', default: false, }, }, required: [], }, }, { name: 'search_methods', description: 'Search for methods, functions, and classes in the project', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (method/function/class name or pattern)', }, type: { type: 'string', enum: ['method', 'function', 'class', 'interface', 'type', 'all'], description: 'Type of definition to search for', default: 'all', }, includeUsages: { type: 'boolean', description: 'Include usage locations in the results', default: false, }, }, required: ['query'], }, }, { name: 'find_dependencies', description: 'Find dependencies and relationships between code entities', inputSchema: { type: 'object', properties: { entityName: { type: 'string', description: 'Name of the method/class/function to analyze', }, direction: { type: 'string', enum: ['incoming', 'outgoing', 'both'], description: 'Direction of dependencies to find', default: 'both', }, depth: { type: 'number', description: 'Maximum depth of dependency traversal', default: 2, minimum: 1, maximum: 10, }, }, required: ['entityName'], }, }, { name: 'generate_graph', description: 'Generate a dependency graph for visualization', inputSchema: { type: 'object', properties: { format: { type: 'string', enum: ['mermaid', 'dot', 'json'], description: 'Output format for the graph', default: 'mermaid', }, scope: { type: 'string', enum: ['full', 'services', 'controllers', 'types'], description: 'Scope of the graph to generate', default: 'services', }, maxNodes: { type: 'number', description: 'Maximum number of nodes to include', default: 50, minimum: 10, maximum: 200, }, }, required: [], }, }, ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'analyze_project': return await analyzeProjectTool(this.indexer, args); case 'search_methods': return await searchMethodsTool(this.indexer, args); case 'find_dependencies': return await findDependenciesTool(this.indexer, args); case 'generate_graph': return await generateGraphTool(this.indexer, args || {}); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${message}`); } }); // List available resources this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'project://files', name: 'Project Files', description: 'List of all indexed project files with metadata', mimeType: 'application/json', }, { uri: 'project://methods', name: 'Project Methods', description: 'Index of all methods, functions, and classes', mimeType: 'application/json', }, { uri: 'project://paths', name: 'API Paths', description: 'List of all API endpoints and routes', mimeType: 'application/json', }, { uri: 'project://relationships', name: 'Code Relationships', description: 'Dependency relationships between code entities', mimeType: 'application/json', }, ], }; }); // Handle resource reads this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; try { switch (uri) { case 'project://files': return await this.resources.files.read(); case 'project://methods': return await this.resources.methods.read(); case 'project://paths': return await this.resources.paths.read(); case 'project://relationships': return await this.resources.methods.readRelationships(); default: throw new McpError(ErrorCode.InvalidRequest, `Unknown resource: ${uri}`); } } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new McpError(ErrorCode.InternalError, `Resource read failed: ${message}`); } }); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); // Start file watcher for real-time updates await this.watcher.start('../'); logger.info('🚀 MCP Project Indexer server started!'); } async stop() { await this.watcher.stop(); await this.server.close(); } } // Start the server const server = new ProjectIndexerServer(); process.on('SIGINT', async () => { logger.info('🛑 Shutting down MCP Project Indexer server...'); await server.stop(); process.exit(0); }); process.on('SIGTERM', async () => { logger.info('🛑 Shutting down MCP Project Indexer server...'); await server.stop(); process.exit(0); }); server.start().catch((error) => { logger.error(`❌ Failed to start MCP Project Indexer server: ${error}`); process.exit(1); }); //# sourceMappingURL=server.js.map