mcp-eth-test-subgraph-9
Version:
MCP server for ethereum_transactions_test subgraph - Track all Ethereum ERC20 transfers for testing
90 lines (85 loc) • 3.56 kB
JavaScript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.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 (subgraph_id filter will be automatically added)'),
params: z.record(z.any()).optional().describe('Query parameters (subgraph_id will be automatically included)'),
});
class SubgraphMCPServer {
server;
graphDb;
schemaService;
constructor() {
this.server = new McpServer({
name: 'ethereum_transactions_test-mcp-server',
version: '1.0.0',
});
this.graphDb = new GraphDatabaseService(CONFIG.QUERY);
this.schemaService = new SchemaService(CONFIG.SUBGRAPH_ID);
this.setupToolHandlers();
}
setupToolHandlers() {
// Register the Cypher query tool
this.server.registerTool('cypher_query', {
title: 'Execute Cypher Query',
description: `Execute Cypher queries on the ${CONFIG.SUBGRAPH_NAME} subgraph.
SUBGRAPH ID: ${CONFIG.SUBGRAPH_ID}
CRITICAL FILTERING RULES:
- EVERY node in your query MUST be filtered by subgraph_id property: WHERE n.subgraph_id = $subgraph_id
- EVERY relationship in your query MUST be filtered by subgraph_id property: WHERE r.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: {
query: z.string().describe('Cypher query to execute (subgraph_id filter will be automatically added)'),
params: z.record(z.any()).optional().describe('Query parameters (subgraph_id will be automatically included)'),
},
}, async (request) => {
const { query, params = {} } = request;
// Validate that query includes subgraph_id filter for data isolation
if (!query.includes('subgraph_id')) {
throw new Error('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),
},
],
};
});
}
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);
});