Skip to main content
Glama

CTS MCP Server

by EricA1019
server.jsโ€ข14.4 kB
/** * CTS MCP Server * Handles MCP protocol over stdio transport */ import { z } from 'zod'; import { getConfig, formatConfig } from './config.js'; import { reasoningTool, createReasoningHandler } from './tools/reasoning/index.js'; import { bughunterTool, createBughunterHandler } from './tools/bughunter/index.js'; // TEMPORARILY DISABLED: CTS_Cleanup has sync/async file operation issues // import { cleanupTool, createCleanupHandler } from './tools/cleanup/index.js'; import { auditTool, createAuditHandler } from './tools/audit/index.js'; import { MCPError as MCPErrorType, Errors } from './errors.js'; import { logger } from './logger.js'; import { withCache, withObservability } from './tools/tool-wrapper.js'; import { ctsExportToShrimpTool, ctsExportToShrimpHandler, } from './tools/cts_export_to_shrimp.js'; import { ArtifactEngine } from './artifacts/artifact_engine.js'; import { D3SignalMapRenderer } from './artifacts/renderers/d3_signal_map.js'; import { ClusteredSignalMapRenderer } from './artifacts/renderers/d3_signal_map_v2.js'; import { ReactHopDashboardRenderer } from './artifacts/renderers/react_hop_dashboard.js'; import { DependencyGraphRenderer } from './artifacts/renderers/dependency_graph.js'; import { PerformanceTrendRenderer } from './artifacts/renderers/performance_trends.js'; import { renderArtifactTool, createRenderArtifactHandler } from './tools/render_artifact.js'; import { scanProjectSignalsTool, createScanSignalsHandler } from './tools/scan_project_signals.js'; import { analyzeProjectTool, createAnalyzeProjectHandler } from './tools/analyze_project.js'; import { suggestRefactoringTool, createSuggestRefactoringHandler } from './tools/suggest_refactoring.js'; // Zod schemas for validation const MCPRequestSchema = z.object({ jsonrpc: z.literal('2.0'), id: z.union([z.string(), z.number()]), method: z.string(), params: z.record(z.unknown()).optional(), }); const ToolCallParamsSchema = z.object({ name: z.string(), arguments: z.record(z.unknown()).optional(), }); const ResourceReadParamsSchema = z.object({ uri: z.string(), }); const PromptGetParamsSchema = z.object({ name: z.string(), arguments: z.record(z.unknown()).optional(), }); export class CTSMCPServer { tools = new Map(); resources = new Map(); prompts = new Map(); resourceContentProvider; serverName = 'cts-mcp-server'; serverVersion = '0.1.0'; protocolVersion = '2024-11-05'; artifactEngine; constructor() { // Load and validate configuration const config = getConfig(); logger.info('[CTS MCP] Loading configuration...', { config: formatConfig(config) }); // Initialize ArtifactEngine with production renderers this.artifactEngine = new ArtifactEngine(); this.artifactEngine.registerRenderer(new D3SignalMapRenderer()); this.artifactEngine.registerRenderer(new ClusteredSignalMapRenderer()); this.artifactEngine.registerRenderer(new ReactHopDashboardRenderer()); this.artifactEngine.registerRenderer(new DependencyGraphRenderer()); this.artifactEngine.registerRenderer(new PerformanceTrendRenderer()); // Register built-in tools with caching and observability this.registerTool(ctsExportToShrimpTool, withObservability('CTS_Export_to_Shrimp', ctsExportToShrimpHandler)); this.registerTool(renderArtifactTool, withCache('CTS_Render_Artifact', createRenderArtifactHandler(this.artifactEngine))); this.registerTool(scanProjectSignalsTool, withCache('CTS_Scan_Project_Signals', createScanSignalsHandler(this.artifactEngine))); // Phase 3 tools with caching this.registerTool(analyzeProjectTool, withCache('CTS_Analyze_Project', createAnalyzeProjectHandler())); this.registerTool(suggestRefactoringTool, withCache('CTS_Suggest_Refactoring', createSuggestRefactoringHandler())); // Tier 2B tools with observability this.registerTool(reasoningTool, withObservability('CTS_Reasoning', createReasoningHandler())); this.registerTool(bughunterTool, withCache('CTS_Bughunter', createBughunterHandler())); // TEMPORARILY DISABLED: CTS_Cleanup has sync/async file operation issues // this.registerTool(cleanupTool, withCache('CTS_Cleanup', createCleanupHandler())); this.registerTool(auditTool, withCache('CTS_Audit', createAuditHandler())); } /** * Handle incoming MCP request */ async handleMessage(rawMessage) { const startTime = Date.now(); let requestId = null; let method; try { // Validate request structure const request = MCPRequestSchema.parse(rawMessage); requestId = request.id; method = request.method; // Trace incoming request logger.traceRequest(requestId, method, request.params); let result; // Route to appropriate handler switch (request.method) { case 'initialize': result = await this.handleInitialize(); break; case 'tools/list': result = await this.handleToolsList(); break; case 'tools/call': result = await this.handleToolCall(request.params); break; case 'resources/list': result = await this.handleResourcesList(); break; case 'resources/read': result = await this.handleResourceRead(request.params); break; case 'prompts/list': result = await this.handlePromptsList(); break; case 'prompts/get': result = await this.handlePromptsGet(request.params); break; case 'cts/metrics': result = await this.handleMetrics(); break; default: throw Errors.methodNotFound(request.method); } const duration = Date.now() - startTime; logger.traceResponse(requestId, duration, true); return this.createResponse(request.id, result); } catch (error) { const duration = Date.now() - startTime; // Handle MCP structured errors if (error instanceof MCPErrorType) { logger.warn('MCP error occurred', { requestId: requestId ?? undefined, method, code: error.code, message: error.message, duration, }); return this.createErrorResponse(requestId, error.code, error.message, error.data); } // Handle Zod validation errors if (error instanceof z.ZodError) { logger.error('Request validation failed', error, { requestId: requestId ?? undefined, method, duration, }); return this.createErrorResponse(requestId, Errors.invalidRequest().code, 'Invalid request structure', error.errors); } // Handle unexpected errors const errorMessage = error instanceof Error ? error.message : 'Unknown error'; const errorStack = error instanceof Error ? error.stack : undefined; logger.error('Unexpected error handling message', error, { requestId: requestId ?? undefined, method, duration, }); return this.createErrorResponse(requestId, Errors.internalError(errorMessage, errorStack).code, `Internal error: ${errorMessage}`, { stack: errorStack }); } } /** * Handle initialize request */ async handleInitialize() { return { protocolVersion: this.protocolVersion, serverInfo: { name: this.serverName, version: this.serverVersion, }, capabilities: { tools: {}, resources: {}, prompts: {}, // Note: sampling is a CLIENT capability (servers request, clients execute) // We document this capability for reference but don't implement it server-side }, }; } /** * Handle tools/list request */ async handleToolsList() { const tools = Array.from(this.tools.values()).map(t => t.definition); return { tools }; } /** * Handle tools/call request */ async handleToolCall(params) { const startTime = Date.now(); let toolName; try { const validated = ToolCallParamsSchema.parse(params); toolName = validated.name; const tool = this.tools.get(validated.name); if (!tool) { throw Errors.toolNotFound(validated.name); } logger.toolStart(toolName, validated.arguments); const result = await tool.handler(validated.arguments || {}); const duration = Date.now() - startTime; logger.toolComplete(toolName, duration, true); // Check if tool already returns MCP format { content: [...] } if (result && typeof result === 'object' && 'content' in result) { // Old Tier 2B format (already wrapped in MCP content) return result; } // New Tier 2C format - BaseToolResponse needs to be wrapped // All tools now return: { success, timestamp, toolName, duration_ms?, result } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const duration = Date.now() - startTime; // Handle MCP errors if (error instanceof MCPErrorType) { if (toolName) { logger.toolError(toolName, error, duration); } throw error; // Re-throw to be caught by handleMessage } // Handle tool execution errors const errorMessage = error instanceof Error ? error.message : 'Unknown error'; const toolError = toolName ? Errors.toolExecutionFailed(toolName, error) : Errors.internalError(errorMessage); if (toolName) { logger.toolError(toolName, error, duration); } throw toolError; } } /** * Register a tool with the server */ registerTool(definition, handler) { this.tools.set(definition.name, { definition, handler }); console.error(`[CTS MCP] Registered tool: ${definition.name}`); } /** * Register a resource with the server */ registerResource(uri, resource) { this.resources.set(uri, resource); console.error(`[CTS MCP] Registered resource: ${uri}`); } /** * Set the resource content provider (e.g., PromptLoader) */ setResourceContentProvider(provider) { this.resourceContentProvider = provider; } /** * Register a prompt template with the server */ registerPrompt(name, template) { this.prompts.set(name, template); console.error(`[CTS MCP] Registered prompt: ${name}`); } /** * Handle prompts/list request */ async handlePromptsList() { const prompts = Array.from(this.prompts.values()).map(p => ({ name: p.name, description: p.description, arguments: p.arguments, })); return { prompts }; } /** * Handle prompts/get request */ async handlePromptsGet(params) { const validated = PromptGetParamsSchema.parse(params); const prompt = this.prompts.get(validated.name); if (!prompt) { throw new Error(`Prompt not found: ${validated.name}`); } // Render the prompt with provided arguments const result = prompt.render(validated.arguments || {}); return result; } /** * Handle resources/list request */ async handleResourcesList() { const resources = Array.from(this.resources.values()); return { resources }; } /** * Handle resources/read request */ async handleResourceRead(params) { const validated = ResourceReadParamsSchema.parse(params); const resource = this.resources.get(validated.uri); if (!resource) { throw new Error(`Resource not found: ${validated.uri}`); } if (!this.resourceContentProvider) { throw new Error('Resource content provider not configured'); } const contents = await this.resourceContentProvider(validated.uri); return { contents: [contents] }; } /** * Handle cts/metrics request (custom endpoint for performance monitoring) */ async handleMetrics() { const metrics = { artifact_engine: this.artifactEngine.getMetrics(), cache_stats: this.artifactEngine.getCacheStats(), }; // Try to include tree-sitter metrics if available try { const { getMetrics: getParserMetrics } = await import('./utils/tree_sitter.js'); return { ...metrics, tree_sitter: getParserMetrics(), }; } catch { // Tree-sitter not initialized or not available return metrics; } } /** * Create successful response */ createResponse(id, result) { return { jsonrpc: '2.0', id, result, }; } /** * Create error response */ createErrorResponse(id, code, message, data) { const error = { code, message }; if (data !== undefined) { error.data = data; } return { jsonrpc: '2.0', id: id ?? 0, error, }; } } //# sourceMappingURL=server.js.map

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/EricA1019/CTS_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server