Skip to main content
Glama
index.ts6.87 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { config } from './config/env.js'; import { ProjectBrain } from './core/ProjectBrain.js'; import { CheckpointInputSchema, RollbackInputSchema, } from './types/schema.js'; import { logger } from './utils/logger.js'; /** * MCP Server for Claude Infinite Context */ class InfiniteContextServer { private server: Server; private brain: ProjectBrain; private initialized = false; constructor() { this.server = new Server( { name: 'infinite-context', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.brain = new ProjectBrain( config.REDIS_URL, config.GEMINI_API_KEY, config.ANTHROPIC_API_KEY || '', config.PROJECT_ROOT ); this.setupHandlers(); this.setupErrorHandling(); } /** * Sets up MCP request handlers */ private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools: Tool[] = [ { name: 'checkpoint', description: 'Save current context to Redis before running /clear. Merges new context with existing project state using LLM-based summarization.', inputSchema: { type: 'object', properties: { context: { type: 'string', description: 'The current work context to checkpoint (summary of recent work, decisions, files)', }, token_count: { type: 'number', description: 'Current token budget usage', }, }, required: ['context', 'token_count'], }, }, { name: 'resume', description: 'Load the last checkpoint at session start. Returns formatted context to inject into the conversation.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'rollback', description: 'Revert to a previous checkpoint version. Useful if a merge produced incorrect results.', inputSchema: { type: 'object', properties: { steps: { type: 'number', description: 'Number of versions to roll back (default: 1)', default: 1, }, }, }, }, { name: 'status', description: 'Show current state metadata including version, active files, tasks, token usage, and checkpoint history.', inputSchema: { type: 'object', properties: {}, }, }, ]; return { tools }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { // Lazy initialization on first tool call if (!this.initialized) { await this.brain.initialize(); this.initialized = true; } const { name, arguments: args } = request.params; switch (name) { case 'checkpoint': { const input = CheckpointInputSchema.parse(args); const result = await this.brain.checkpoint( input.context, input.token_count ); return { content: [{ type: 'text', text: result }], }; } case 'resume': { const result = await this.brain.resume(); return { content: [{ type: 'text', text: result }], }; } case 'rollback': { const input = RollbackInputSchema.parse(args || {}); const result = await this.brain.rollback(input.steps); return { content: [{ type: 'text', text: result }], }; } case 'status': { const result = await this.brain.status(); return { content: [{ type: 'text', text: result }], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { logger.error('Tool execution failed', { error, tool: request.params.name }); return { content: [ { type: 'text', text: this.formatUserError(error, request.params.name), }, ], isError: true, }; } }); } private formatUserError(error: unknown, tool: string): string { const baseMessage = error instanceof Error ? error.message : String(error); const guidance = this.getErrorGuidance(baseMessage, tool); return guidance ? `${baseMessage}\n\nSuggestion: ${guidance}` : baseMessage; } private getErrorGuidance(message: string, tool: string): string | null { if (message.includes('Redis')) { return 'Ensure Redis Stack is running with: redis-stack-server'; } if (message.includes('API key')) { return 'Check your .env file and ensure API keys are set correctly'; } if (message.includes('Session ID')) { return 'Try deleting .claude_session_id and restarting'; } if (tool === 'rollback' && message.includes('available')) { return 'No checkpoint history found. Create a checkpoint first using the checkpoint tool'; } return null; } /** * Sets up error handling and graceful shutdown */ private setupErrorHandling(): void { const shutdown = async () => { logger.info('Shutting down server...'); if (this.initialized) { await this.brain.shutdown(); } process.exit(0); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); process.on('uncaughtException', (error) => { logger.error('Uncaught exception', { error }); shutdown(); }); process.on('unhandledRejection', (reason) => { logger.error('Unhandled rejection', { reason }); shutdown(); }); } /** * Starts the MCP server */ async start(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); logger.info('Infinite Context MCP server started', { projectRoot: config.PROJECT_ROOT, redisUrl: config.REDIS_URL, }); } } // Start the server const server = new InfiniteContextServer(); server.start().catch((error) => { console.error('Failed to start server:', error); process.exit(1); });

Implementation Reference

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/coderdeep11/claude-memory-mcp'

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