Skip to main content
Glama
RichardDillman

SEO Audit MCP Server

server.ts6.96 kB
// src/server.ts // MCP Server implementation for SEO Audit tools import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { analyzePage, crawlSite, runLighthouse, analyzeSiteAccess, checkLighthouseInstalled, planAudit, samplePages, runAudit, TOOL_DEFINITIONS, } from './tools/index.js'; import { checkUrls } from './utils/http.js'; import { closeBrowser } from './utils/browser.js'; /** * Create and configure the MCP server */ export function createServer(): Server { const server = new Server( { name: 'seo-audit-mcp', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); // Register tool list handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TOOL_DEFINITIONS, }; }); // Register tool call handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args = {} } = request.params; try { switch (name) { case 'analyze_page': { const result = await analyzePage({ url: args.url as string, waitForSelector: args.waitForSelector as string | undefined, timeout: args.timeout as number | undefined, device: args.device as 'desktop' | 'mobile' | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'crawl_site': { const result = await crawlSite({ startUrl: args.startUrl as string, maxPages: args.maxPages as number | undefined, maxDepth: args.maxDepth as number | undefined, includePatterns: args.includePatterns as string[] | undefined, excludePatterns: args.excludePatterns as string[] | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'run_lighthouse': { // Check if Lighthouse is installed const isInstalled = await checkLighthouseInstalled(); if (!isInstalled) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'Lighthouse CLI is not installed', solution: 'Run: npm install -g lighthouse', }), }, ], isError: true, }; } const result = await runLighthouse({ url: args.url as string, device: args.device as 'mobile' | 'desktop' | undefined, categories: args.categories as any[] | undefined, saveReport: args.saveReport as boolean | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'analyze_sitemap': { const result = await analyzeSiteAccess({ baseUrl: args.baseUrl as string, includeSitemapUrls: args.includeSitemapUrls as boolean | undefined, maxUrls: args.maxUrls as number | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'check_urls': { const result = await checkUrls( args.urls as string[], { timeout: args.timeout as number | undefined } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'plan_audit': { const result = await planAudit({ baseUrl: args.baseUrl as string, maxSitemapsToProcess: args.maxSitemapsToProcess as number | undefined, maxUrlsPerSitemap: args.maxUrlsPerSitemap as number | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'sample_pages': { const result = await samplePages({ plan: args.plan as any, routeTypes: args.routeTypes as string[] | undefined, samplesOverride: args.samplesOverride as Record<string, number> | undefined, concurrency: args.concurrency as number | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } case 'run_audit': { const result = await runAudit({ baseUrl: args.baseUrl as string, reportsDir: args.reportsDir as string | undefined, maxSitemaps: args.maxSitemaps as number | undefined, maxUrlsPerSitemap: args.maxUrlsPerSitemap as number | undefined, samplesPerRouteType: args.samplesPerRouteType as number | undefined, concurrency: args.concurrency as number | undefined, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } default: return { content: [ { type: 'text', text: `Unknown tool: ${name}`, }, ], isError: true, }; } } catch (error: any) { return { content: [ { type: 'text', text: JSON.stringify({ error: error.message, stack: error.stack, }), }, ], isError: true, }; } }); return server; } /** * Run the server */ export async function runServer(): Promise<void> { const server = createServer(); const transport = new StdioServerTransport(); // Handle cleanup on exit process.on('SIGINT', async () => { await closeBrowser(); process.exit(0); }); process.on('SIGTERM', async () => { await closeBrowser(); process.exit(0); }); await server.connect(transport); console.error('SEO Audit MCP Server running on stdio'); }

Latest Blog Posts

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

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