UNPKG

mcp-eth-test-subgraph-3

Version:

MCP server for ethereum_transactions_test subgraph - Track all Ethereum ERC20 transfers for testing

126 lines (121 loc) 5.15 kB
#!/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 { z } from 'zod'; import { GraphDatabaseService } from './services/GraphDatabaseService.js'; import { SchemaService } from './services/SchemaService.js'; import { CONFIG } from './config.js'; // Validation schema for Cypher query tool const CypherQuerySchema = z.object({ query: z.string().describe('Cypher query to execute'), params: z.record(z.any()).optional().describe('Query parameters'), }); class SubgraphMCPServer { server; graphDb; schemaService; constructor() { this.server = new Server({ name: 'ethereum_transactions_test-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.graphDb = new GraphDatabaseService(CONFIG.QUERY); this.schemaService = new SchemaService(CONFIG.SUBGRAPH_ID); this.setupToolHandlers(); } setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const schema = await this.schemaService.getSchema(); return { tools: [ { name: 'cypher_query', description: `Execute Cypher queries on the ${CONFIG.SUBGRAPH_NAME} subgraph. SUBGRAPH ID: ${CONFIG.SUBGRAPH_ID} ${this.schemaService.formatSchemaForToolDescription(schema)} IMPORTANT: All queries MUST include the subgraph_id filter: WHERE n.subgraph_id = $subgraph_id The subgraph_id parameter is automatically added to your query parameters. Generate only valid Cypher syntax, no explanations or markdown formatting.`, inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Cypher query to execute (subgraph_id filter will be automatically added)', }, params: { type: 'object', description: 'Query parameters (subgraph_id will be automatically included)', additionalProperties: true, }, }, required: ['query'], }, }, ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (name === 'cypher_query') { try { const { query, params = {} } = CypherQuerySchema.parse(args); // Validate that query includes subgraph_id filter for data isolation if (!query.includes('subgraph_id')) { throw new McpError(ErrorCode.InvalidParams, 'Query must include subgraph_id filter for data isolation. Example: WHERE n.subgraph_id = $subgraph_id'); } // Automatically add subgraph_id to parameters const queryParams = { ...params, subgraph_id: CONFIG.SUBGRAPH_ID, }; const result = await this.graphDb.executeCypherQuery(query, queryParams); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Query execution failed: ${error instanceof Error ? error.message : String(error)}`); } } throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); }); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error(`ethereum_transactions_test MCP server running on stdio`); } async close() { await this.graphDb.close(); } } // Start the server const server = new SubgraphMCPServer(); process.on('SIGINT', async () => { await server.close(); process.exit(0); }); process.on('SIGTERM', async () => { await server.close(); process.exit(0); }); server.start().catch((error) => { console.error('Failed to start server:', error); process.exit(1); });