Skip to main content
Glama

DataForSEO MCP Server

index-worker.ts3.93 kB
import { McpAgent } from "agents/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from 'zod'; import { DataForSEOClient, DataForSEOConfig } from '../core/client/dataforseo.client.js'; import { EnabledModulesSchema } from '../core/config/modules.config.js'; import { BaseModule, ToolDefinition } from '../core/modules/base.module.js'; import { ModuleLoaderService } from '../core/utils/module-loader.js'; import { version, name } from './version.worker.js'; /** * DataForSEO MCP Server for Cloudflare Workers * * This server provides MCP (Model Context Protocol) access to DataForSEO APIs * through a Cloudflare Worker runtime using the agents/mcp pattern. */ // Server metadata const SERVER_NAME = `${name} (Worker)`; const SERVER_VERSION = version; globalThis.__PACKAGE_VERSION__ = version; globalThis.__PACKAGE_NAME__ = name; /** * DataForSEO MCP Agent for Cloudflare Workers */ export class DataForSEOMcpAgent extends McpAgent { server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION, }); constructor(ctx: DurableObjectState, protected env: Env){ super(ctx, env); } async init() { const workerEnv = this.env || (globalThis as any).workerEnv; if (!workerEnv) { throw new Error(`Worker environment not available`); } // Initialize DataForSEO client const dataForSEOConfig: DataForSEOConfig = { username: workerEnv.DATAFORSEO_USERNAME || "", password: workerEnv.DATAFORSEO_PASSWORD || "", }; const dataForSEOClient = new DataForSEOClient(dataForSEOConfig); // Parse enabled modules from environment const enabledModules = EnabledModulesSchema.parse(workerEnv.ENABLED_MODULES); // Initialize and load modules const modules: BaseModule[] = ModuleLoaderService.loadModules(dataForSEOClient, enabledModules); // Register tools from all modules modules.forEach(module => { const tools = module.getTools(); Object.entries(tools).forEach(([name, tool]) => { const typedTool = tool as ToolDefinition; const schema = z.object(typedTool.params); this.server.tool( name, schema.shape, typedTool.handler ); }); }); } } /** * Creates a JSON-RPC error response */ function createErrorResponse(code: number, message: string): Response { return new Response(JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id: null }), { status: code === -32001 ? 401 : 400, headers: { 'Content-Type': 'application/json' } }); } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const url = new URL(request.url); // Store environment in global context for McpAgent access (globalThis as any).workerEnv = env; // Health check endpoint if (url.pathname === '/health' && request.method === 'GET') { return new Response(JSON.stringify({ status: 'healthy', server: SERVER_NAME, version: SERVER_VERSION, timestamp: new Date().toISOString() }), { headers: { 'Content-Type': 'application/json' } }); } // Check if credentials are configured if (!env.DATAFORSEO_USERNAME || !env.DATAFORSEO_PASSWORD) { if (['/mcp','/http', '/sse', '/messages','/sse/message'].includes(url.pathname)) { return createErrorResponse(-32001, "DataForSEO credentials not configured in worker environment variables"); } } // MCP endpoints using McpAgent pattern if (url.pathname === "/sse" || url.pathname === "/sse/message") { return DataForSEOMcpAgent.serveSSE("/sse").fetch(request, env, ctx); } if (url.pathname === "/mcp" || url.pathname == '/http') { return DataForSEOMcpAgent.serve("/mcp").fetch(request, env, ctx); } return new Response("Not found", { status: 404 }); }, };

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/ravinwebsurgeon/seo-mcp'

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