Most SEO advice on the web is in English. If you search in Arabic for why Google Search Console says "Crawled – currently not indexed", you mostly get thin, copied posts.

So I built SEO Bank (بنك السيو): a free Arabic reference of real SEO problems (on-page, off-page, technical), each with symptoms, the cause, the tools to use, numbered fix steps, WordPress-specific steps and sources.

Then I asked: what if an AI agent could read it directly, instead of guessing? That became a small remote MCP server that anyone can plug into Claude, Cursor or VS Code in one line:

claude mcp add --transport http seo-bank https://seoquestionsbank.com/mcp 

This post is about the design decisions, because most of them were about not adding things.

What it does

The server exposes five read-only tools:

Tool Use it when
search_seo_questions You have a problem in words ("pages not indexed", "الصفحات مش بتتفهرس"). Ranked by relevance, paged with offset, optional category.
get_seo_question You have an id or a page URL and want the full fix as Markdown.
list_seo_categories You want to know what the bank covers.
get_seo_category You want one section's intro, topics and latest questions.
list_seo_questions You want to browse without a keyword, newest first.

A typical agent flow is search → read → answer with a citation:

You: Why are my pages "Crawled – currently not indexed"? Use seo-bank and cite the source.

Agent: calls search_seo_questions → gets crawled-not-indexed → calls get_seo_question → answers with the fix steps and links to https://seoquestionsbank.com/technical/crawled-not-indexed/.

The citation part matters to me. The answer comes from a page a human wrote and reviewed, and the user can click through and check it.

Decision 1: remote, stateless, JSON only

The MCP spec's Streamable HTTP transport allows sessions and Server-Sent Events. A read-only knowledge base needs neither:

  • No session id. Every POST is self-contained.
  • Responses are plain application/json, never an SSE stream.
  • GET returns 405, which tells clients there is no server-to-client stream.
  • Notifications (notifications/initialized) get a 202 with no body.

That makes the endpoint trivially cacheable and lets it run as one route in the site's existing Cloudflare Worker (the site is built with Astro). Nothing to install, nothing to keep alive.

Decision 2: no SDK

The official SDKs are great, but for a stateless read-only server the whole protocol surface is a switch:

switch (msg.method) { case 'initialize': return ok(id, { protocolVersion, capabilities: { tools: {} }, serverInfo, instructions }); case 'ping': return ok(id, {}); case 'tools/list': return ok(id, { tools: TOOLS }); case 'tools/call': return ok(id, await callTool(msg.params.name, msg.params.arguments)); case 'resources/list': return ok(id, { resources: [] }); case 'prompts/list': return ok(id, { prompts: [] }); default: return fail(id, -32601, 'Method not found'); } 

Add JSON-RPC batch support, a size cap on the body, a per-IP rate limit and CORS, and you have a complete server. Fewer dependencies also means a smaller Worker and a faster cold start.

Decision 3: search that understands Arabic

Arabic is hard for naive keyword matching. The same word can be written with different forms of alef (أ إ آ ا), with or without diacritics, and with attached prefixes like "ال" (the) or "بال" (with the).

Before scoring, both the query and the documents are normalised:

  • strip diacritics and tatweel,
  • unify alef forms, ى → ي, ة → ه,
  • strip common prefixes (ال، بال، وال، لل).

Each field has a weight (title > tags > summary > body), and the score is averaged per query word. English terms like hreflang or noindex stay as they are, since Arabic SEO people use them in English anyway. No vector database was needed for 86 well-structured documents.

Decision 4: write tool descriptions for the model, not for humans

I listed the server on Glama, which grades every tool's definition. My first version scored 16/20, and the feedback was fair:

  • Two tools overlapped. When should a model search and when should it browse? I now say it in both descriptions: search is "keyword, ranked by relevance"; list is "no keyword, newest first (not by relevance)".
  • Search was capped at 8 results with no paging. Now it returns total and nextOffset.
  • There was no way to ask about one category. Hence get_seo_category.
  • Every tool now has MCP annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false. A client can trust it won't change anything.

One trick I recommend: when you rename tools to make them clearer, keep the old names as aliases in tools/call. I renamed search_seo_bank to search_seo_questions, and existing configs kept working.

Decision 5: make the site itself agent-friendly

The MCP server is one piece. The same content is also available as:

  • /llms.txt and /llms-full.txt,
  • a Markdown version of every page (Accept: text/markdown),
  • an MCP server card under /.well-known/.

If an agent can't use MCP, it can still read clean Markdown instead of scraping HTML.

Try it

  • Claude Code: claude mcp add --transport http seo-bank https://seoquestionsbank.com/mcp
  • Claude Desktop / claude.ai: Settings → Connectors → Add custom connector → https://seoquestionsbank.com/mcp
  • Cursor / VS Code (mcp.json):
{ "mcpServers": { "seo-bank": { "type": "http", "url": "https://seoquestionsbank.com/mcp" } } } 

The docs live at github.com/designermohamedeid/seo-bank-mcp. The site also has 29 free SEO tools, including an AI readiness checker that tells you how ready your own site is for agents.

If you build MCP servers for content sites, I'd love to hear how you handle search and tool descriptions. And if you speak Arabic and work in SEO, tell me which problem the bank is missing.