UNPKG

@covalenthq/goldrush-mcp-server

Version:

GoldRush MCP Server for interacting with Covalent GoldRush API

253 lines (250 loc) 9.57 kB
import packageJson from "../package.json" with { type: "json" }; import { addRealTimeChainStatusResources } from "./resources/dynamicResources.js"; import { addStaticResources } from "./resources/staticResources.js"; import { addAllChainsServiceTools } from "./services/AllChainsService.js"; import { addBalanceServiceTools } from "./services/BalanceService.js"; import { addBaseServiceTools } from "./services/BaseService.js"; import { addBitcoinServiceTools } from "./services/BitcoinService.js"; import { addNftServiceTools } from "./services/NftService.js"; import { addPricingServiceTools } from "./services/PricingService.js"; import { addSecurityServiceTools } from "./services/SecurityService.js"; import { addTransactionServiceTools } from "./services/TransactionService.js"; import { GoldRushClient } from "@covalenthq/client-sdk"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import dotenv from "dotenv"; import express from "express"; dotenv.config(); const { version } = packageJson; const DEFAULT_PORT = 3000; export function createGoldRushClient(apiKey) { const key = apiKey || process.env["GOLDRUSH_API_KEY"]; if (!key) { throw new Error("GOLDRUSH_API_KEY is required. Provide it as a parameter or set the environment variable."); } return new GoldRushClient(key); } export function createServer(apiKey) { const goldRushClient = createGoldRushClient(apiKey); const server = new McpServer({ name: "GoldRush MCP Server", version: version, }, { capabilities: { logging: {} } }); addStaticResources(server); addRealTimeChainStatusResources(server, goldRushClient); addAllChainsServiceTools(server, goldRushClient); addBaseServiceTools(server, goldRushClient); addBalanceServiceTools(server, goldRushClient); addTransactionServiceTools(server, goldRushClient); addBitcoinServiceTools(server, goldRushClient); addNftServiceTools(server, goldRushClient); addPricingServiceTools(server, goldRushClient); addSecurityServiceTools(server, goldRushClient); return server; } export async function startStdioServer(apiKey) { try { const server = createServer(apiKey); const transport = new StdioServerTransport(); await server.connect(transport); } catch (error) { console.error("Failed to start STDIO server:", error); process.exit(1); } } export function startHttpServer(port = DEFAULT_PORT) { const app = express(); app.use(express.json()); app.post("/mcp", async (req, res) => { try { const authHeader = req.headers.authorization; const requestId = (() => { try { return req.body?.id || null; } catch { return null; } })(); if (!authHeader || !authHeader.startsWith("Bearer ")) { console.log("Received invalid Authorization header"); res.writeHead(401).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32001, message: "Missing or invalid Authorization header. Expected format: Bearer <GOLDRUSH_API_KEY>", }, id: requestId, })); return; } const apiKey = authHeader?.split(" ")[1]; if (!apiKey || apiKey.trim() === "") { console.log("Received invalid API key"); res.writeHead(401).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32001, message: "API key is empty or invalid", }, id: requestId, })); return; } const server = createServer(apiKey); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => { console.log("Request closed"); transport.close(); server.close(); }); await server.connect(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("Error handling MCP request:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error", }, id: null, }); } } }); app.get("/mcp", async (req, res) => { console.log("Received GET MCP request"); res.writeHead(405).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed.", }, id: null, })); }); app.delete("/mcp", async (req, res) => { console.log("Received DELETE MCP request"); res.writeHead(405).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed.", }, id: null, })); }); app.listen(port, () => { console.log(`MCP Streamable HTTP Server listening on port ${port}`); }); process.on("SIGINT", async () => { console.log("Shutting down HTTP server..."); process.exit(0); }); } export async function startServer(transportType = "stdio", options = {}) { const { port = DEFAULT_PORT, apiKey } = options; switch (transportType) { case "stdio": await startStdioServer(apiKey); break; case "http": startHttpServer(port); break; default: throw new Error(`Unsupported transport type: ${transportType}`); } } export function parseArgsAndStart() { try { const args = process.argv.slice(2); let transportType = "stdio"; let port = DEFAULT_PORT; let apiKey; for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case "--transport": case "-t": { const transport = args[i + 1]; if (!transport) { throw new Error("Transport type is required"); } if (transport === "stdio" || transport === "http") { transportType = transport; i++; } else { throw new Error(`Invalid transport type: ${transport}. Supported: stdio, http`); } break; } case "--port": case "-p": { const portStr = args[i + 1]; if (!portStr) { throw new Error("Port value is required"); } const parsedPort = parseInt(portStr, 10); if (isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) { throw new Error(`Invalid port: ${portStr}`); } port = parsedPort; i++; break; } case "--api-key": case "-k": { const keyValue = args[i + 1]; if (!keyValue) { throw new Error("API key cannot be empty"); } apiKey = keyValue; i++; break; } case "--help": case "-h": console.log(` GoldRush MCP Server v${version} Usage: goldrush-mcp-server [options] Options: -t, --transport <type> Transport type: stdio (default) or http -p, --port <number> Port for HTTP transport (default: ${DEFAULT_PORT}) -k, --api-key <key> GoldRush API key (or set GOLDRUSH_API_KEY env var) -h, --help Show this help message Examples: goldrush-mcp-server # STDIO transport goldrush-mcp-server -t http # HTTP transport on default port goldrush-mcp-server -t http -p 8080 # HTTP transport on port 8080 goldrush-mcp-server -k your_api_key # With API key argument `); process.exit(0); break; default: if (arg && arg.startsWith("-")) { throw new Error(`Unknown argument: ${arg}`); } break; } } startServer(transportType, { port, apiKey }).catch((error) => { console.error("Failed to start server:", error); process.exit(1); }); } catch (error) { console.error("Error:", error instanceof Error ? error.message : error); process.exit(1); } } //# sourceMappingURL=server.js.map