MCP server

MCP server

MetaMonster runs a remote MCP server at:

https://new.metamonster.ai/mcp

It's a stateless Streamable HTTP endpoint — no session state, so any request can land on any server instance. Authentication is the same mm_ API key as the REST API, sent as a bearer token:

Authorization: Bearer mm_YOUR_API_KEY

There's no separate MCP-specific key or scope model — an MCP call spends the same rate-limit budget and respects the same scopes as the REST call behind it (see Authentication).

The server wraps 15 curated tools over the v1 API — not a 1:1 endpoint mirror. Each tool is a small workflow step (find pages, stage an edit, resolve a recommendation, run an analysis) so a chat agent can drive the whole verification loop without knowing the REST shapes underneath.

claude.ai web and Claude Desktop connect via OAuth, through the custom-connector flow below — sign in, pick the organization to authorize, and approve. Claude Code, Cursor, and Windsurf connect directly with a bearer API key since they support header-based auth on a remote server.

Setup

Claude Code

claude mcp add --transport http metamonster https://new.metamonster.ai/mcp --header "Authorization: Bearer mm_YOUR_KEY"

Cursor / Windsurf

Add to mcp.json:

{
  "mcpServers": {
    "metamonster": {
      "url": "https://new.metamonster.ai/mcp",
      "headers": { "Authorization": "Bearer mm_YOUR_KEY" }
    }
  }
}

claude.ai web / Claude Desktop (OAuth connector)

Both connect the same way, through the custom-connector UI:

  1. Go to Settings → Connectors → Add custom connector.
  2. Enter the server URL: https://new.metamonster.ai/mcp.
  3. Sign in with your MetaMonster account.
  4. Pick the organization to authorize (if you belong to more than one).
  5. Approve.

No API key to copy or paste — the connector flow issues and refreshes its own token. Revoke access anytime from Settings → Connected apps in the app; revoking cuts off MetaMonster API access immediately.

To switch which organization the connector is authorized for, revoke it in Settings → Connected apps, then reconnect and pick the new organization — there's no in-place org switch.

Tools

All 15 tools return a short human-readable summary plus a structured JSON payload mirroring the REST data shape (same snake_case field names as the REST API).

Tool What it does REST endpoint(s)
list_sites List the sites in your organization. Start here unless you already have a page URL. GET /sites
find_pages Search/filter a site's pages by URL, priority, grade, score, or recommendation presence. GET /sites/{siteId}/pages
get_opportunities Ranked "what should I work on" for a site — homepage + scored opportunity rows + cross-page recommendations. Requires an active subscription. GET /sites/{siteId}/opportunities
get_rubric What the site's grader measures — components, weights, criteria. Read once per site before editing. GET /sites/{siteId}/rubric
run_checks Free, instant, deterministic hygiene checks (title/meta length, keyword placement, JSON-LD validity, link counts). Not the grade. GET /pages/{pageId}/checks
get_page_report Full report for a page without triggering an analysis: fields, latest analysis, recommendations, keywords, GSC queries, SERP, comparison vs. the previous run. Accepts page_id or url. GET /pages/lookup (if url) → GET /pages/{pageId}/report
update_page Set a page's keywords, priority flag, or purpose. Stores exactly what you send; no AI. PATCH /pages/{pageId}
set_field_draft Stage a new title, meta description, h1, primary keyword, body, image alt, or schema (JSON-LD) for a page. Empty value discards the draft. PUT/DELETE /pages/{pageId}/drafts/{field}
update_content Replace the page body with markdown (creates a content version). POST /pages/{pageId}/content
resolve_recommendation "I made this change" — validates current content/draft against the recommendation and marks it applied on a pass. POST /recommendations/{id}/resolve
dismiss_recommendation "Not doing this" — marks a recommendation dismissed with an optional rationale. POST /recommendations/{id}/dismiss
recheck_recommendation Re-verify an already-applied recommendation after further edits (clears placeholders, confirms a skipped verdict). POST /recommendations/{id}/recheck
get_job Status of an async job (job_an_… analysis, job_cr_… crawl). Use when a tool returned status: "processing". GET /jobs/{jobId}
recrawl_page Re-fetch the live page after edits made directly on the site (not through these tools). POST /pages/{pageId}/recrawl
analyze_page Run a full analysis and return the report. Accepts page_id or url; resolves or creates the page (and, with create_site: true, the site) as needed. Spends one unit of the org's plan allowance. GET /pages/lookup, POST /sites, POST /sites/{siteId}/pages, POST /pages/{pageId}/analyze, GET /pages/{pageId}/report

Analyze a URL

analyze_page and get_page_report both accept { url } instead of { page_id }, so a client can hand over a bare URL and get a report back without knowing whether MetaMonster has seen the page before. analyze_page resolves it through three cases:

  1. Known page — the URL already resolves to a page. If it has no crawled snapshot yet (e.g. a page created but never fetched), it's crawled first; otherwise it's analyzed (or its existing report is returned) directly. A page found planned (no live URL yet) returns page_not_live instead of guessing a URL to crawl.
  2. Known site, unknown page — the URL's host matches an existing site but the path doesn't. The page is created and crawled first, then analyzed.
  3. Unknown site — neither the host nor the path is known. The tool returns site_not_found with a next_step hint instead of guessing. Confirm with the user, then call analyze_page again with create_site: true to add the site (this queues a background discovery crawl of the whole site) — only the one page you asked about is analyzed; site creation never spends extra allowance on its own.

A mistyped URL on a known site creates a page that can't be deleted through MCP (there's no delete tool yet), so confirm uncertain paths with the user before calling analyze_page with a URL.

Only analyze_page will create a site or page on your behalf, and only with explicit opt-in (create_site: true). get_page_report only reads what already exists (page_not_found/site_not_found otherwise), and recrawl_page only accepts page_id — it doesn't resolve a URL.

Errors

Tool errors carry the same code/message/details as the REST error envelopepayment_required, analysis_in_progress, crawl_in_progress, site_not_found, page_not_found, already_resolved, and so on. A couple of MCP-layer additions:

  • invalid_arguments — the tool call itself was malformed (bad zod input); fix the arguments and retry.
  • page_not_liveanalyze_page was asked to analyze a planned page (no live URL yet) with no crawled or saved content; add content with update_content or take the page live and recrawl instead.
  • analyze_page's crawl and analysis waits share a single ~75-second server-side budget for the whole call (not ~90s per step) — the second wait gets whatever's left after the first. If the budget runs out while a step is still running, the tool returns status: "processing" with a job_id instead of an error — call get_job to poll it, honoring its retry_after_seconds.

Rate limits are the calling key's normal REST limits (see Conventions) — there's no separate MCP-layer limit; each tool call spends the budget of the v1 request(s) it makes underneath.

Local development

Run against a local dev server at http://localhost:3000/mcp with the same bearer auth. The MCP Inspector is the fastest way to poke at it directly:

npx @modelcontextprotocol/inspector

In the Inspector UI, choose transport Streamable HTTP, set the URL to http://localhost:3000/mcp, and add an Authorization: Bearer mm_YOUR_KEY header.