Skip to main content
Glama

Kleros Court MCP Server

by gmkung
reCONSTRUCT.md7.53 kB
# Kleros Court MCP Server - Reconstruction Guide ## Overview A Model Context Protocol (MCP) server that retrieves comprehensive Kleros court dispute data including meta-evidence and evidence submissions from multiple blockchain networks. ## Core Architecture ### Supported Networks - **Ethereum Mainnet** (chainId: 1) - **Gnosis Chain** (chainId: 100) ### Data Sources 1. **Meta-evidence API**: `https://kleros-api.netlify.app/.netlify/functions/get-dispute-metaevidence` 2. **Ethereum Subgraph**: `https://gateway.thegraph.com/api/d1d19cef4bc7647cc6cfad4ad2662628/subgraphs/id/BqbBhB4R5pNAtdYya2kcojMrQMp8nVHioUnP22qN8JoN` 3. **Gnosis Subgraph**: `https://gateway.thegraph.com/api/d1d19cef4bc7647cc6cfad4ad2662628/subgraphs/id/FxhLntVBELrZ4t1c2HNNvLWEYfBjpB8iKZiEymuFSPSr` 4. **IPFS Gateway**: `https://cdn.kleros.link` ## Core Logic ### 1. Dispute Data Retrieval ```typescript // Main function to get comprehensive dispute data async function getDisputeData(disputeId: string, chainId: number) { // 1. Get meta-evidence const metaEvidence = await getMetaEvidence(disputeId, chainId); // 2. Get evidence submissions from subgraph const evidenceSubmissions = await getEvidenceSubmissions(disputeId, chainId); // 3. Fetch evidence content from IPFS const evidenceContents = await Promise.allSettled( evidenceSubmissions.map(submission => getEvidenceContent(submission.uri) ) ); return { disputeId, chainId, metaEvidence, evidenceContents: evidenceContents .filter(result => result.status === 'fulfilled') .map(result => result.value), evidenceErrors: evidenceContents .filter(result => result.status === 'rejected') .map(result => ({ evidenceUri: result.reason.uri, error: result.reason.message })) }; } ``` ### 2. Meta-evidence Retrieval ```typescript async function getMetaEvidence(disputeId: string, chainId: number) { const response = await axios.get( 'https://kleros-api.netlify.app/.netlify/functions/get-dispute-metaevidence', { params: { disputeId, chainId } } ); return response.data; } ``` ### 3. Evidence Submissions from Subgraph ```typescript async function getEvidenceSubmissions(disputeId: string, chainId: number) { const subgraphUrl = chainId === 1 ? 'https://gateway.thegraph.com/api/d1d19cef4bc7647cc6cfad4ad2662628/subgraphs/id/BqbBhB4R5pNAtdYya2kcojMrQMp8nVHioUnP22qN8JoN' : 'https://gateway.thegraph.com/api/d1d19cef4bc7647cc6cfad4ad2662628/subgraphs/id/FxhLntVBELrZ4t1c2HNNvLWEYfBjpB8iKZiEymuFSPSr'; const query = ` query getDispute($id: String!) { dispute(id: $id) { evidenceGroup { evidence { URI sender creationTime } } } } `; const response = await axios.post(subgraphUrl, { query, variables: { id: disputeId } }); return response.data.data.dispute?.evidenceGroup?.evidence || []; } ``` ### 4. IPFS Content Retrieval ```typescript async function getEvidenceContent(ipfsUri: string) { // Convert IPFS URI to HTTP URL - handle different formats let httpUrl: string; if (ipfsUri.startsWith('ipfs://')) { httpUrl = ipfsUri.replace('ipfs://', 'https://cdn.kleros.link/ipfs/'); } else if (ipfsUri.startsWith('/ipfs/')) { // Already has /ipfs/ prefix, just prepend the gateway httpUrl = `https://cdn.kleros.link${ipfsUri}`; } else if (ipfsUri.startsWith('Qm') || ipfsUri.startsWith('bafy')) { // Direct IPFS hash httpUrl = `https://cdn.kleros.link/ipfs/${ipfsUri}`; } else if (ipfsUri.startsWith('http')) { // Already an HTTP URL httpUrl = ipfsUri; } else { // Assume it's an IPFS hash and prepend the gateway httpUrl = `https://cdn.kleros.link/ipfs/${ipfsUri}`; } const response = await axios.get(httpUrl, { timeout: 10000, headers: { 'Accept': 'application/json' } }); return { title: response.data.title, description: response.data.description, fileURI: response.data.fileURI, fileTypeExtension: response.data.fileTypeExtension, type: response.data.type }; } ``` ## MCP Server Implementation ### Tool Definition ```typescript { name: "get_dispute_data", description: "Retrieve comprehensive dispute data from Kleros including meta-evidence and evidence submissions", inputSchema: { type: "object", properties: { disputeId: { type: "string", description: "The dispute ID to retrieve data for" }, chainId: { type: "number", description: "The chain ID (1 for Ethereum, 100 for Gnosis)", enum: [1, 100] } }, required: ["disputeId", "chainId"] } } ``` ### Server Transport Options 1. **Stdio Transport** (Local) ```typescript import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; ``` 2. **Streamable HTTP Transport** (Modern Remote - RECOMMENDED) ```typescript import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; ``` 3. **SSE Transport** (Legacy Remote - DEPRECATED) ```typescript import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; ``` **Note**: SSE transport is deprecated. Use Streamable HTTP for new implementations. ## Error Handling ### Network Timeouts ```typescript const response = await axios.get(url, { timeout: 10000, headers: { 'Accept': 'application/json' } }); ``` ### IPFS Content Validation ```typescript try { const content = await getEvidenceContent(uri); return content; } catch (error) { return { error: `Failed to retrieve content from ${uri}: ${error.message}`, uri }; } ``` ### Subgraph Query Errors ```typescript try { const response = await axios.post(subgraphUrl, { query, variables }); return response.data.data.evidenceSubmissions; } catch (error) { throw new Error(`Subgraph query failed: ${error.message}`); } ``` ## Testing ### Local Testing ```bash # Build and run yarn build yarn start # Test with curl curl -X POST http://localhost:3000/api/test \ -H "Content-Type: application/json" \ -d '{"disputeId": "123", "chainId": 1}' ``` ## Dependencies ```json { "type": "module", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.1", "axios": "^1.6.0", "express": "^4.18.2", "cors": "^2.8.5", "zod": "^3.22.0" }, "devDependencies": { "@types/node": "^20.0.0", "@types/express": "^4.17.21", "@types/cors": "^2.8.17", "tsx": "^4.0.0", "typescript": "^5.0.0" } } ``` **Important**: Set `"type": "module"` in package.json for ES module support. ## Key Features - ✅ **Multi-chain support** (Ethereum + Gnosis) - ✅ **Comprehensive error handling** - ✅ **IPFS content retrieval** (with proper URI format handling) - ✅ **Subgraph integration** (updated to new schema) - ✅ **Modern transport options** (Streamable HTTP recommended) - ✅ **ES Module support** (Node.js 18+) - ✅ **Fly.io deployment ready** ## Deployment URL **Live Server**: `https://kleros-mcp-server-new.fly.dev/mcp` ## Claude Desktop Integration Simply add the URL `https://kleros-mcp-server-new.fly.dev/mcp` as a custom connector in Claude Desktop: 1. Settings → Connectors → Add custom connector 2. Name: `Kleros Court` 3. URL: `https://kleros-mcp-server-new.fly.dev/mcp` This reconstruction guide contains all the essential logic needed to rebuild the Kleros Court MCP server from scratch.

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/gmkung/kleros-court-mcp'

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