{"id":633,"date":"2026-03-28T13:48:56","date_gmt":"2026-03-28T13:48:56","guid":{"rendered":"https:\/\/autorank.so\/blog\/how-to-build-mcp-server-seo-content\/"},"modified":"2026-03-28T13:48:56","modified_gmt":"2026-03-28T13:48:56","slug":"how-to-build-mcp-server-seo-content","status":"publish","type":"post","link":"https:\/\/autorank.so\/blog\/how-to-build-mcp-server-seo-content\/","title":{"rendered":"How to Build an MCP Server for AutoRank: Control Your Entire SEO Pipeline from Your AI Assistant"},"content":{"rendered":"<h1>How to Build an MCP Server for AutoRank: Run Your SEO Pipeline from Your AI Assistant<\/h1>\n<p>If you&#8217;ve ever managed a serious content operation, you know the drill. Google Search Console in one tab. Ahrefs in another. WordPress admin in a third. A spreadsheet tracking your content calendar. Another spreadsheet mapping keyword clusters.<\/p>\n<p>And somewhere in the chaos, you&#8217;re trying to figure out which of the 200 keyword opportunities you identified last week actually deserve a 2,000-word article.<\/p>\n<p><strong>It&#8217;s death by context switching.<\/strong> Every tab is a silo. Every tool speaks its own language. Your actual strategic thinking &mdash; deciding <em>what to write, when to publish, and how to outrank the competition<\/em> &mdash; gets buried under operational overhead.<\/p>\n<p>What if you could manage all of it from a single conversational interface?<\/p>\n<p>In this guide, we&#8217;ll build an <strong>MCP server<\/strong> that connects AutoRank&#8217;s SEO intelligence directly to Claude Code. By the end, you&#8217;ll have a working server that turns natural language commands like <em>&#8220;find content gaps against this competitor and queue articles for the top 5 opportunities&#8221;<\/em> into real SEO actions.<\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>What Is MCP (and Why Should SEOs Care)?<\/h2>\n<p>The <a href=\"https:\/\/modelcontextprotocol.io\/\" target=\"_blank\" rel=\"noopener\">Model Context Protocol<\/a> (MCP) is an open standard from Anthropic that defines how AI assistants connect to external tools. Think of it as <strong>USB-C for AI<\/strong> &mdash; a universal interface that lets any AI assistant plug into any service without custom glue code.<\/p>\n<p>The key insight: MCP separates <em>AI reasoning<\/em> from <em>tool execution<\/em>. Your AI assistant decides <strong>what<\/strong> needs to happen. Your MCP server handles the <strong>how<\/strong> &mdash; calling APIs, transforming data, returning results.<\/p>\n<p>For SEO teams, this is transformative. Instead of bouncing between dashboards, you describe your intent:<\/p>\n<ul>\n<li><em>&#8220;Find content gaps against this competitor&#8221;<\/em><\/li>\n<li><em>&#8220;Queue an article targeting this keyword cluster&#8221;<\/em><\/li>\n<li><em>&#8220;What are my highest-impact keyword opportunities right now?&#8221;<\/em><\/li>\n<\/ul>\n<p>The AI orchestrates the right API calls through your MCP server automatically.<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> MCP servers run locally on your machine. Your API tokens never leave your environment, and you control exactly which capabilities to expose. Security-conscious teams can audit every line of the bridge code.<\/p><\/blockquote>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>How the Architecture Works<\/h2>\n<p>The data flow is simple. Three components, two connections:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>Claude Code  &lt;--MCP\/STDIO--&gt;  Your MCP Server  &lt;--HTTP--&gt;  AutoRank API\n(AI Client)                   (Bridge Code)                (autorank.so)<\/code><\/pre>\n<p><strong>Claude Code<\/strong> sends structured requests over STDIO (standard input\/output). Your server receives them, calls the AutoRank API over HTTP, and returns structured responses. The AI never touches the API directly.<\/p>\n<p>This gives you three advantages worth noting:<\/p>\n<ol>\n<li><strong>Security<\/strong> &mdash; API tokens stay on your machine, never exposed to the AI model.<\/li>\n<li><strong>Control<\/strong> &mdash; You shape what data comes back. Filter, summarize, or combine responses before the AI sees them.<\/li>\n<li><strong>Composability<\/strong> &mdash; One MCP tool can call multiple APIs in sequence. Analyze a blog, pull keywords, <em>and<\/em> check GSC data in a single tool invocation.<\/li>\n<\/ol>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>The Three Tools We&#8217;re Building<\/h2>\n<p>MCP servers expose <strong>Tools<\/strong> (executable functions), <strong>Resources<\/strong> (read-only data), and <strong>Prompts<\/strong> (reusable templates). For this guide, we&#8217;re building three tools that cover the core SEO workflow:<\/p>\n<table style=\"width: 100%;border-collapse: collapse;margin: 24px 0;font-size: 0.95em\">\n<thead>\n<tr>\n<th style=\"background-color: #f8f9fa;padding: 12px 16px;text-align: left;border-bottom: 2px solid #e2e8f0;font-weight: 600\">Tool<\/th>\n<th style=\"background-color: #f8f9fa;padding: 12px 16px;text-align: left;border-bottom: 2px solid #e2e8f0;font-weight: 600\">What It Does<\/th>\n<th style=\"background-color: #f8f9fa;padding: 12px 16px;text-align: left;border-bottom: 2px solid #e2e8f0;font-weight: 600\">When to Use It<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">analyze_blog<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Content gap analysis on any URL<\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Competitor research, quarterly audits<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_keyword_opportunities<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">AI-ranked keyword clusters for a domain<\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Deciding what to write next<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">generate_article<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Queue an article for AI generation + publishing<\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Turning research into content<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These three tools create a complete loop: <strong>research -&gt; prioritize -&gt; execute<\/strong>. That&#8217;s the workflow that actually moves rankings.<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">analyze_blog<\/code> tool works on <em>any<\/em> public blog &mdash; not just your own. Use it to reverse-engineer competitor content strategies before building your own editorial calendar.<\/p><\/blockquote>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 1: Initialize the Project<\/h2>\n<p>You&#8217;ll need <strong>Node.js 18+<\/strong> and an <strong>AutoRank API token<\/strong> (grab yours from Dashboard &gt; Settings).<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>mkdir autorank-mcp-server &amp;&amp; cd autorank-mcp-server\nnpm init -y\nnpm install @modelcontextprotocol\/sdk\nnpm install -D typescript @types\/node\nnpx tsc --init<\/code><\/pre>\n<p>Update <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">tsconfig.json<\/code> for a modern Node runtime:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>{\n  &quot;compilerOptions&quot;: {\n    &quot;target&quot;: &quot;ES2022&quot;,\n    &quot;module&quot;: &quot;Node16&quot;,\n    &quot;moduleResolution&quot;: &quot;Node16&quot;,\n    &quot;outDir&quot;: &quot;.\/dist&quot;,\n    &quot;rootDir&quot;: &quot;.\/src&quot;,\n    &quot;strict&quot;: true,\n    &quot;esModuleInterop&quot;: true,\n    &quot;skipLibCheck&quot;: true\n  }\n}<\/code><\/pre>\n<p>Your project structure should now look like this:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>autorank-mcp-server\/\n  package.json\n  tsconfig.json\n  src\/          &lt;-- we&#x27;ll create this next<\/code><\/pre>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 2: Set Up the Server Skeleton<\/h2>\n<p>Create <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">src\/index.ts<\/code>. This is where everything lives &mdash; the server definition, the API helper, and all three tools.<\/p>\n<p>Start with the imports and configuration:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>import { McpServer } from &quot;@modelcontextprotocol\/sdk\/server\/mcp.js&quot;;\nimport { StdioServerTransport } from &quot;@modelcontextprotocol\/sdk\/server\/stdio.js&quot;;\nimport { z } from &quot;zod&quot;; \/\/ Built into the MCP SDK for input validation\n\n\/\/ AutoRank API config\nconst API_BASE = &quot;https:\/\/autorank.so\/api&quot;;\nconst API_TOKEN = process.env.AUTORANK_API_TOKEN;\n\nif (!API_TOKEN) {\n  console.error(&quot;AUTORANK_API_TOKEN environment variable is required&quot;);\n  process.exit(1);\n}\n\n\/\/ Initialize the MCP server\nconst server = new McpServer({\n  name: &quot;autorank-seo&quot;,\n  version: &quot;1.0.0&quot;,\n});<\/code><\/pre>\n<p>The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">z<\/code> import is Zod, which the MCP SDK uses for input validation. Every tool you register will define its parameters as a Zod schema &mdash; this gives you runtime type checking and auto-generated descriptions for the AI client.<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">name<\/code> field in your server config is what shows up in Claude Code&#8217;s tool list. Pick something descriptive &mdash; <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">autorank-seo<\/code> immediately tells you what this server does.<\/p><\/blockquote>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 3: Build the API Helper<\/h2>\n<p>Before wiring up tools, we need a reusable function for authenticated requests to AutoRank. Every tool will call this instead of using <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">fetch<\/code> directly.<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>async function autorankRequest(\n  endpoint: string,\n  options: {\n    method?: &quot;GET&quot; | &quot;POST&quot;;\n    body?: Record&lt;string, unknown&gt;;\n    params?: Record&lt;string, string&gt;;\n  } = {}\n): Promise&lt;unknown&gt; {\n  const { method = &quot;GET&quot;, body, params } = options;\n\n  \/\/ Build the URL with query params\n  const url = new URL(`${API_BASE}${endpoint}`);\n  if (params) {\n    Object.entries(params).forEach(([k, v]) =&gt; url.searchParams.set(k, v));\n  }\n\n  const response = await fetch(url.toString(), {\n    method,\n    headers: {\n      Authorization: `Bearer ${API_TOKEN}`,\n      &quot;Content-Type&quot;: &quot;application\/json&quot;,\n    },\n    body: body ? JSON.stringify(body) : undefined,\n  });\n\n  if (!response.ok) {\n    const errorText = await response.text();\n    throw new Error(`AutoRank API error (${response.status}): ${errorText}`);\n  }\n\n  return response.json();\n}<\/code><\/pre>\n<p>This handles authentication, query parameters, JSON serialization, and error reporting in one place. Every tool handler becomes a thin wrapper around this function.<\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 4: Register the Tools<\/h2>\n<p>Here&#8217;s where MCP gets interesting. The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">server.tool()<\/code> method takes four arguments:<\/p>\n<ol>\n<li><strong>Name<\/strong> &mdash; what the AI calls the tool<\/li>\n<li><strong>Description<\/strong> &mdash; the AI reads this to decide <em>when<\/em> to use the tool (write it like a product description, not a docstring)<\/li>\n<li><strong>Zod schema<\/strong> &mdash; defines and validates the input parameters<\/li>\n<li><strong>Handler<\/strong> &mdash; the async function that does the work<\/li>\n<\/ol>\n<h3>Tool 1: analyze_blog<\/h3>\n<p>This tool accepts any blog URL and returns a content gap analysis &mdash; keyword opportunities, competitive insights, and topics the target site is missing or underserving.<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>server.tool(\n  &quot;analyze_blog&quot;,\n  &quot;Analyze a blog URL for content gaps, keyword opportunities, and &quot; +\n    &quot;competitive insights. Works on any public blog -- use it for &quot; +\n    &quot;competitor research or auditing your own site.&quot;,\n  {\n    url: z.string().url()\n      .describe(&quot;Blog URL to analyze (e.g. https:\/\/competitor.com\/blog)&quot;),\n    include_serp_analysis: z.boolean().optional().default(true)\n      .describe(&quot;Include SERP competitor analysis for top opportunities&quot;),\n  },\n  async ({ url, include_serp_analysis }) =&gt; {\n    try {\n      const result = await autorankRequest(&quot;\/blog-analyzer\/analyze&quot;, {\n        method: &quot;POST&quot;,\n        body: { url, include_serp_analysis },\n      });\n      return {\n        content: [{ type: &quot;text&quot; as const, text: JSON.stringify(result, null, 2) }],\n      };\n    } catch (error) {\n      const msg = error instanceof Error ? error.message : String(error);\n      return {\n        content: [{ type: &quot;text&quot; as const, text: `Analysis failed: ${msg}` }],\n        isError: true,\n      };\n    }\n  }\n);<\/code><\/pre>\n<p>Notice the pattern: call <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">autorankRequest<\/code>, return the JSON result as text content, catch errors gracefully. <strong>Every tool you build follows this exact same structure.<\/strong> The only things that change are the endpoint, HTTP method, and parameters.<\/p>\n<h3>Tool 2: get_keyword_opportunities<\/h3>\n<p>This tool pulls AI-ranked keyword clusters for a domain &mdash; sorted by estimated traffic impact, with search volume, difficulty scores, and current positions.<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>server.tool(\n  &quot;get_keyword_opportunities&quot;,\n  &quot;Retrieve AI-ranked keyword opportunities for a domain. Returns &quot; +\n    &quot;clusters sorted by traffic impact with volume and difficulty scores. &quot; +\n    &quot;Filter by cluster to focus on a specific topic area.&quot;,\n  {\n    domain: z.string()\n      .describe(&quot;Domain to analyze (e.g. myblog.com)&quot;),\n    cluster: z.string().optional()\n      .describe(&quot;Filter by cluster name (e.g. &#x27;product-photography&#x27;)&quot;),\n    min_volume: z.number().optional()\n      .describe(&quot;Minimum monthly search volume threshold&quot;),\n    limit: z.number().optional().default(50)\n      .describe(&quot;Max opportunities to return (default: 50)&quot;),\n  },\n  async ({ domain, cluster, min_volume, limit }) =&gt; {\n    try {\n      const params: Record&lt;string, string&gt; = { domain, limit: String(limit) };\n      if (cluster) params.cluster = cluster;\n      if (min_volume) params.min_volume = String(min_volume);\n\n      const result = await autorankRequest(&quot;\/seo\/opportunities&quot;, { params });\n      return {\n        content: [{ type: &quot;text&quot; as const, text: JSON.stringify(result, null, 2) }],\n      };\n    } catch (error) {\n      const msg = error instanceof Error ? error.message : String(error);\n      return {\n        content: [{ type: &quot;text&quot; as const, text: `Failed to fetch: ${msg}` }],\n        isError: true,\n      };\n    }\n  }\n);<\/code><\/pre>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">cluster<\/code> filter is powerful for focused content sprints. Instead of looking at all 200 keyword opportunities, narrow it to one topic cluster and dominate that silo before moving on. Topical authority compounds.<\/p><\/blockquote>\n<h3>Tool 3: generate_article<\/h3>\n<p>This is where research becomes action. This tool queues an article for AI generation &mdash; AutoRank handles the outline, draft, SEO optimization, and scheduled WordPress publishing.<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>server.tool(\n  &quot;generate_article&quot;,\n  &quot;Queue an article for AI generation and WordPress publishing. &quot; +\n    &quot;AutoRank generates an SEO-optimized draft and publishes it on &quot; +\n    &quot;the scheduled date. Returns a job ID for tracking.&quot;,\n  {\n    topic: z.string()\n      .describe(&quot;Article topic or working title&quot;),\n    focus_keyword: z.string()\n      .describe(&quot;Primary keyword to target (e.g. &#x27;ai product photography&#x27;)&quot;),\n    cluster: z.string().optional()\n      .describe(&quot;Keyword cluster to assign this article to&quot;),\n    scheduled_date: z.string().optional()\n      .describe(&quot;Publish date in ISO 8601 format (e.g. &#x27;2026-04-07&#x27;)&quot;),\n    tone: z.enum([&quot;professional&quot;, &quot;conversational&quot;, &quot;technical&quot;, &quot;casual&quot;])\n      .optional().default(&quot;professional&quot;)\n      .describe(&quot;Writing tone for the article&quot;),\n    word_count_target: z.number().optional().default(2000)\n      .describe(&quot;Target word count (default: 2000)&quot;),\n  },\n  async ({ topic, focus_keyword, cluster, scheduled_date, tone, word_count_target }) =&gt; {\n    try {\n      const result = await autorankRequest(&quot;\/content\/queue&quot;, {\n        method: &quot;POST&quot;,\n        body: { topic, focus_keyword, cluster, scheduled_date, tone, word_count_target },\n      });\n      return {\n        content: [{ type: &quot;text&quot; as const, text: JSON.stringify(result, null, 2) }],\n      };\n    } catch (error) {\n      const msg = error instanceof Error ? error.message : String(error);\n      return {\n        content: [{ type: &quot;text&quot; as const, text: `Failed to queue: ${msg}` }],\n        isError: true,\n      };\n    }\n  }\n);<\/code><\/pre>\n<p>That&#8217;s all three tools registered. Notice how little code changes between them &mdash; the <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">autorankRequest<\/code> helper does the heavy lifting, and each handler is just configuration.<\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 5: Connect the Transport and Start the Server<\/h2>\n<p>The last piece: wire up STDIO transport so Claude Code can communicate with your server. Add this at the bottom of <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">src\/index.ts<\/code>:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>async function main() {\n  const transport = new StdioServerTransport();\n  await server.connect(transport);\n  console.error(&quot;AutoRank MCP server running on STDIO&quot;);\n}\n\nmain().catch((error) =&gt; {\n  console.error(&quot;Fatal error:&quot;, error);\n  process.exit(1);\n});<\/code><\/pre>\n<p>Now compile and verify:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>npx tsc<\/code><\/pre>\n<p><strong>What you should see:<\/strong> No errors. A <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">dist\/index.js<\/code> file appears in your project directory. If TypeScript reports errors, double-check that your imports match the SDK version &mdash; the <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">@modelcontextprotocol\/sdk<\/code> package includes Zod as a dependency.<\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Step 6: Configure Claude Code<\/h2>\n<p>Tell Claude Code where to find your server by adding it to <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">~\/.claude\/mcp.json<\/code>:<\/p>\n<pre style=\"background-color: #1a1b26;color: #c0caf5;padding: 20px 24px;border-radius: 8px;font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;font-size: 14px;line-height: 1.6;margin: 24px 0;border: 1px solid #2a2b3d\"><code>{\n  &quot;mcpServers&quot;: {\n    &quot;autorank&quot;: {\n      &quot;command&quot;: &quot;node&quot;,\n      &quot;args&quot;: [&quot;\/absolute\/path\/to\/autorank-mcp-server\/dist\/index.js&quot;],\n      &quot;env&quot;: {\n        &quot;AUTORANK_API_TOKEN&quot;: &quot;your-api-token-here&quot;\n      }\n    }\n  }\n}<\/code><\/pre>\n<p>Restart Claude Code. You should see the three AutoRank tools (<code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">analyze_blog<\/code>, <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_keyword_opportunities<\/code>, <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">generate_article<\/code>) listed when you check available tools.<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> Use an <strong>absolute path<\/strong> in the <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">args<\/code> field &mdash; relative paths break depending on where you launch Claude Code from. On macOS, that&#8217;s something like <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">\/Users\/yourname\/projects\/autorank-mcp-server\/dist\/index.js<\/code>.<\/p><\/blockquote>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Testing It: From Analysis to Published Content in One Conversation<\/h2>\n<p>Here&#8217;s where the payoff hits. Open Claude Code and start talking naturally. The AI maps your intent to tool calls automatically.<\/p>\n<h3>Competitive Research<\/h3>\n<p>Try this prompt:<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><em>&#8220;Analyze competitor.com for content gaps. Focus on topics where they rank on page 2 &mdash; those are the easiest to overtake.&#8221;<\/em><\/p><\/blockquote>\n<p>Claude calls <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">analyze_blog<\/code>, receives the gap analysis, and synthesizes it into prioritized recommendations. It might surface that the competitor has thin content on <em>&#8220;AI product photography pricing&#8221;<\/em> or is missing coverage on <em>&#8220;bulk background removal for ecommerce&#8221;<\/em> entirely.<\/p>\n<h3>Keyword Prioritization<\/h3>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><em>&#8220;Find keyword opportunities in the product-photography cluster with search volume above 200.&#8221;<\/em><\/p><\/blockquote>\n<p>This hits <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_keyword_opportunities<\/code> with the cluster filter. The response comes back as ranked keyword groups &mdash; each with volume, difficulty, and your current position. The AI recommends which ones to target based on the opportunity score.<\/p>\n<h3>Content Scheduling<\/h3>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><em>&#8220;Queue an article about AI headshots for LinkedIn targeting &#8216;ai headshot generator for linkedin&#8217;. Schedule it for next Monday, professional tone.&#8221;<\/em><\/p><\/blockquote>\n<p>The <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">generate_article<\/code> tool queues the job. AutoRank handles outline generation, drafting, SEO optimization, and WordPress publishing. You get a job ID back for tracking.<\/p>\n<h3>The Real Power: Chaining Operations<\/h3>\n<p>The magic is chaining all three in a single conversation:<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><em>&#8220;Analyze myblog.com, find the 5 highest-impact keyword gaps in the AI photography space, and queue articles for each one &mdash; spread across next week, Monday through Friday.&#8221;<\/em><\/p><\/blockquote>\n<p><strong>One prompt. Five articles queued.<\/strong> Each targeted at a validated keyword opportunity, scheduled across the week. That&#8217;s content velocity you cannot achieve clicking through dashboards.<\/p>\n<blockquote style=\"background-color: #f8f9fa;border-left: 4px solid #3b82f6;padding: 16px 20px;margin: 24px 0;border-radius: 0 8px 8px 0;font-size: 0.95em;line-height: 1.6\"><p><strong>Rank Tip:<\/strong> Start with the <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">analyze_blog<\/code> -&gt; <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_keyword_opportunities<\/code> -&gt; <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">generate_article<\/code> chain as your weekly content workflow. Run it every Monday morning. In 10 minutes, you&#8217;ll have a data-backed editorial calendar for the entire week.<\/p><\/blockquote>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Extending Your Server<\/h2>\n<p>The three tools we built cover the core research-to-publish loop. But the pattern is identical for any new capability &mdash; adding a tool takes about 15 minutes once you have the scaffolding.<\/p>\n<p>Ideas for additional tools:<\/p>\n<table style=\"width: 100%;border-collapse: collapse;margin: 24px 0;font-size: 0.95em\">\n<thead>\n<tr>\n<th style=\"background-color: #f8f9fa;padding: 12px 16px;text-align: left;border-bottom: 2px solid #e2e8f0;font-weight: 600\">Tool<\/th>\n<th style=\"background-color: #f8f9fa;padding: 12px 16px;text-align: left;border-bottom: 2px solid #e2e8f0;font-weight: 600\">Purpose<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_gsc_performance<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Pull Search Console data &mdash; impressions, clicks, CTR by query or page<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_rank_tracking<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Monitor keyword position changes over time, flag drops<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">manage_sites<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">List and configure connected WordPress properties<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">generate_pseo_pages<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">Trigger programmatic SEO page generation at scale<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\"><code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">get_content_calendar<\/code><\/td>\n<td style=\"padding: 12px 16px;border-bottom: 1px solid #e2e8f0\">View upcoming scheduled articles, check for publishing gaps<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Each one follows the same structure: define the Zod schema, write a thin handler that calls <code style=\"background-color: #f0f0f0;color: #e4376b;padding: 2px 6px;border-radius: 4px;font-family: 'Fira Code', monospace;font-size: 0.9em\">autorankRequest<\/code>, return the result. The scaffolding you&#8217;ve already built does all the hard work.<\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>Why This Matters<\/h2>\n<p>The SEO industry doesn&#8217;t have a tools <em>shortage<\/em> &mdash; it has a tools <em>fragmentation<\/em> problem. You gather data in one tool, analyze it in another, make decisions in a spreadsheet, and execute in a CMS. Every handoff is a place where context gets lost and decisions get delayed.<\/p>\n<p>MCP collapses that workflow. Your AI assistant becomes the orchestration layer &mdash; it holds the context of your entire conversation, understands your strategic goals, and executes across multiple tools in sequence. The gap between <em>&#8220;we should write about this topic&#8221;<\/em> and <em>&#8220;the article is queued for Thursday&#8221;<\/em> shrinks from hours to seconds.<\/p>\n<p>For SEO professionals who code, building an MCP server is one of the highest-leverage investments you can make. You&#8217;re not automating tasks &mdash; you&#8217;re building an interface that lets you operate at the speed of your strategic thinking, rather than the speed of your tab-switching.<\/p>\n<p>The code from this guide is a starting point. Extend it, connect additional data sources, and build the SEO command center that matches how you actually think about organic growth.<\/p>\n<p><strong>Start building:<\/strong> <a href=\"https:\/\/autorank.so\">autorank.so<\/a><\/p>\n<hr style=\"border: none;border-top: 1px solid #e2e8f0;margin: 40px 0\">\n<h2>More MCP Server Guides<\/h2>\n<p>Building MCP servers for other workflows? Check out our companion guides:<\/p>\n<ul>\n<li><strong><a href=\"https:\/\/pixelpanda.ai\/blog\/how-to-build-mcp-server-ai-image-processing\/\" target=\"_blank\" rel=\"noopener\">How to Build an MCP Server for AI Image Processing (PixelPanda)<\/a><\/strong> &mdash; Background removal, AI product photography, and virtual try-on, all driven by natural language from your AI assistant.<\/li>\n<li><strong><a href=\"https:\/\/shippost.ai\/blog\/how-to-build-mcp-server-twitter-growth\" target=\"_blank\" rel=\"noopener\">How to Build an MCP Server for Twitter\/X Growth (ShipPost)<\/a><\/strong> &mdash; Find the best tweets to reply to, draft voice-matched replies, and generate original content without leaving your editor.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Build a TypeScript MCP server that connects Claude Code to AutoRank&#8217;s SEO intelligence \u2014 analyze competitor blogs, find keyword opportunities, and queue AI-generated articles, all from natural language.<\/p>\n","protected":false},"author":1,"featured_media":635,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"Step-by-step guide to building an MCP server for SEO content generation with AutoRank. Analyze blogs, find keywords, and generate articles from Claude Code.","rank_math_focus_keyword":"mcp server seo content","footnotes":""},"categories":[1],"tags":[],"class_list":["post-633","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/posts\/633","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/comments?post=633"}],"version-history":[{"count":1,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/posts\/633\/revisions"}],"predecessor-version":[{"id":634,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/posts\/633\/revisions\/634"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/media\/635"}],"wp:attachment":[{"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/media?parent=633"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/categories?post=633"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/autorank.so\/blog\/wp-json\/wp\/v2\/tags?post=633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}