Claude Server MCP

by davidteren
Verified
  • build
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs/promises'; import path from 'path'; class ClaudeServer { constructor() { this.server = new Server({ name: 'claude-server', version: '0.1.0', }, { capabilities: { tools: {}, }, }); // Store contexts in user's Documents directory this.contextDir = path.join(process.env.HOME || '~', 'Documents', 'Claude', 'contexts'); this.setupToolHandlers(); // Error handling this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async ensureContextDir() { await fs.mkdir(this.contextDir, { recursive: true }); } async saveContext(context) { await this.ensureContextDir(); const filename = `${context.id}.json`; await fs.writeFile(path.join(this.contextDir, filename), JSON.stringify(context, null, 2), 'utf-8'); } async getContext(id) { try { const data = await fs.readFile(path.join(this.contextDir, `${id}.json`), 'utf-8'); return JSON.parse(data); } catch (error) { if (error.code === 'ENOENT') { return null; } throw error; } } async listContexts() { await this.ensureContextDir(); const files = await fs.readdir(this.contextDir); const contexts = []; for (const file of files) { if (file.endsWith('.json')) { const data = await fs.readFile(path.join(this.contextDir, file), 'utf-8'); contexts.push(JSON.parse(data)); } } return contexts.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'save_context', description: 'Save conversation context for future reference', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Unique identifier for the context', }, content: { type: 'string', description: 'Context content to save', }, tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags for categorizing the context', }, }, required: ['id', 'content'], }, }, { name: 'get_context', description: 'Retrieve previously saved context by ID', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ID of the context to retrieve', }, }, required: ['id'], }, }, { name: 'list_contexts', description: 'List all saved contexts', inputSchema: { type: 'object', properties: { tag: { type: 'string', description: 'Optional tag to filter contexts', }, }, }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'save_context': { const { id, content, tags } = request.params.arguments; const context = { id, content, timestamp: new Date().toISOString(), tags, }; await this.saveContext(context); return { content: [ { type: 'text', text: `Context saved with ID: ${id}`, }, ], }; } case 'get_context': { const { id } = request.params.arguments; const context = await this.getContext(id); if (!context) { throw new McpError(ErrorCode.InvalidRequest, `Context not found with ID: ${id}`); } return { content: [ { type: 'text', text: context.content, }, ], }; } case 'list_contexts': { const { tag } = request.params.arguments; let contexts = await this.listContexts(); if (tag) { contexts = contexts.filter((ctx) => ctx.tags?.includes(tag)); } return { content: [ { type: 'text', text: JSON.stringify(contexts, null, 2), }, ], }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Claude MCP server running on stdio'); } } const server = new ClaudeServer(); server.run().catch(console.error);