Skip to main content
Glama
index.js5.47 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { pipeline } from "@xenova/transformers"; import fs from "fs/promises"; import { createRequire } from "module"; // Import package.json for version const require = createRequire(import.meta.url); const packageJson = require("./package.json"); import { loadConfig } from "./lib/config.js"; import { EmbeddingsCache } from "./lib/cache.js"; import { CodebaseIndexer } from "./features/index-codebase.js"; import { HybridSearch } from "./features/hybrid-search.js"; import * as IndexCodebaseFeature from "./features/index-codebase.js"; import * as HybridSearchFeature from "./features/hybrid-search.js"; import * as ClearCacheFeature from "./features/clear-cache.js"; // Parse workspace from command line arguments const args = process.argv.slice(2); const workspaceIndex = args.findIndex(arg => arg.startsWith('--workspace')); let workspaceDir = null; if (workspaceIndex !== -1) { const arg = args[workspaceIndex]; let rawWorkspace = null; if (arg.includes('=')) { rawWorkspace = arg.split('=')[1]; } else if (workspaceIndex + 1 < args.length) { rawWorkspace = args[workspaceIndex + 1]; } // Check if IDE variable wasn't expanded (contains ${}) if (rawWorkspace && rawWorkspace.includes('${')) { console.error(`[Server] IDE variable not expanded: ${rawWorkspace}, using current directory`); workspaceDir = process.cwd(); } else if (rawWorkspace) { workspaceDir = rawWorkspace; } if (workspaceDir) { console.error(`[Server] Workspace mode: ${workspaceDir}`); } } // Global state let embedder = null; let cache = null; let indexer = null; let hybridSearch = null; let config = null; // Feature registry - ordered by priority (semantic_search first as primary tool) const features = [ { module: HybridSearchFeature, instance: null, handler: HybridSearchFeature.handleToolCall }, { module: IndexCodebaseFeature, instance: null, handler: IndexCodebaseFeature.handleToolCall }, { module: ClearCacheFeature, instance: null, handler: ClearCacheFeature.handleToolCall } ]; // Initialize application async function initialize() { // Load configuration with workspace support config = await loadConfig(workspaceDir); // Ensure search directory exists try { await fs.access(config.searchDirectory); } catch { console.error(`[Server] Error: Search directory "${config.searchDirectory}" does not exist`); process.exit(1); } // Load AI model console.error("[Server] Loading AI embedding model (this may take time on first run)..."); embedder = await pipeline("feature-extraction", config.embeddingModel); // Initialize cache cache = new EmbeddingsCache(config); await cache.load(); // Initialize features indexer = new CodebaseIndexer(embedder, cache, config); hybridSearch = new HybridSearch(embedder, cache, config); const cacheClearer = new ClearCacheFeature.CacheClearer(embedder, cache, config); // Store feature instances (matches features array order) features[0].instance = hybridSearch; features[1].instance = indexer; features[2].instance = cacheClearer; // Start indexing in background (non-blocking) console.error("[Server] Starting background indexing..."); indexer.indexAll().then(() => { // Only start file watcher if explicitly enabled in config if (config.watchFiles) { indexer.setupFileWatcher(); } }).catch(err => { console.error("[Server] Background indexing error:", err.message); }); } // Setup MCP server const server = new Server( { name: "smart-coding-mcp", version: packageJson.version }, { capabilities: { tools: {} } } ); // Register tools from all features server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = []; for (const feature of features) { const toolDef = feature.module.getToolDefinition(config); tools.push(toolDef); } return { tools }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { for (const feature of features) { const toolDef = feature.module.getToolDefinition(config); if (request.params.name === toolDef.name) { return await feature.handler(request, feature.instance); } } return { content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }] }; }); // Main entry point async function main() { await initialize(); const transport = new StdioServerTransport(); await server.connect(transport); console.error("[Server] Smart Coding MCP server ready!"); } // Graceful shutdown process.on('SIGINT', async () => { console.error("\n[Server] Shutting down gracefully..."); // Stop file watcher if (indexer && indexer.watcher) { await indexer.watcher.close(); console.error("[Server] File watcher stopped"); } // Save cache if (cache) { await cache.save(); console.error("[Server] Cache saved"); } console.error("[Server] Goodbye!"); process.exit(0); }); process.on('SIGTERM', async () => { console.error("\n[Server] Received SIGTERM, shutting down..."); process.exit(0); }); main().catch(console.error);

Latest Blog Posts

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/omar-haris/smart-coding-mcp'

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