MCP server

MCP server

MetaMonster runs a remote MCP server at:

https://new.metamonster.ai/mcp

It's a stateless Streamable HTTP endpoint. There's 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 43 curated tools over the v1 API (39 for organizations on the new analysis), not a 1:1 endpoint mirror. Each tool is a small workflow step (find pages, stage an edit, resolve a recommendation, run an analysis, generate a brief or a fix) so a chat agent can drive the whole verification loop, and everything the app can generate, 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.

There's 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

Every tool returns a short human-readable summary plus a structured JSON payload mirroring the REST data shape (same snake_case field names as the REST API). Organizations on the new analysis get 46 tools: the five checks_* tools are dropped and write_recommendations is added. Everyone else gets 50.

Nothing a tool generates is published to your CMS. Drafts, content versions and alt text are saved in MetaMonster for review.

Find and read

Tool What it does REST endpoint(s)
list_sites Lists the sites in your organization. Start here unless you already have a page URL. GET /sites
find_pages Searches and filters a site's pages by URL, priority, grade, score, or recommendation presence (organizations on the new analysis filter by min_factors_passing_pct instead of grade). GET /sites/{siteId}/pages
get_opportunities Returns the ranked "what should I work on" report for a site: the homepage, scored opportunity rows, and cross-page recommendations. Requires an active subscription. GET /sites/{siteId}/opportunities
get_rubric Returns what the site's grader measures: components, weights, and criteria (the factor list on the new analysis). Read it once per site before editing. GET /sites/{siteId}/rubric
run_checks Runs free, instant, deterministic hygiene checks (keyword placement, JSON-LD validity, link counts). These are not the grade. GET /pages/{pageId}/checks
get_page_report Returns the full report for a page without triggering an analysis: fields, latest analysis, recommendations, keywords, GSC queries, SERP, and a comparison against the previous run. Accepts page_id or url. GET /pages/lookup (if url) → GET /pages/{pageId}/report
get_analysis_history Lists a page's past analyses (score and grade, or factors passing on the new analysis). GET /pages/{pageId}/analyses
get_job Returns the status of an async job: job_an_... / job_ck_... (analysis), job_cr_... (crawl), job_ol_... (content brief), job_ac_... (AI fix), job_rw_... (body draft). Use it when a tool returned status: "processing". GET /jobs/{jobId}
keyword_research Returns search volume, difficulty, CPC and intent for a seed keyword, plus related keyword ideas with the same metrics (DataForSEO). Pass site_id to use the site's search market. Each live lookup counts against the organization's daily limit (500, shared with the app); repeating a keyword within 30 days is served from cache for free. GET /keywords/research

Edit and verify

Tool What it does REST endpoint(s)
update_page Sets a page's keywords, priority flag, or purpose. Stores exactly what you send, with no AI involved. PATCH /pages/{pageId}
set_field_draft Stages a new title, meta description, h1, primary keyword, body, or schema (JSON-LD) for a page. An empty value discards the draft. Alt text is per image: use set_image_alt. PUT/DELETE /pages/{pageId}/drafts/{field}
update_content Replaces the page body with markdown, creating a content version. POST /pages/{pageId}/content
list_content_versions Lists a page's body versions (source, word count, date). GET /pages/{pageId}/content-versions
restore_content_version Restores an older body version by copying it forward as a new version. No version is deleted. POST /pages/{pageId}/content-versions/{versionId}/restore
resolve_recommendation Tells MetaMonster "I made this change": validates the current content or draft against the recommendation and marks it applied on a pass. POST /recommendations/{id}/resolve
dismiss_recommendation Tells MetaMonster "not doing this": marks a recommendation dismissed, with an optional rationale. POST /recommendations/{id}/dismiss
recheck_recommendation Re-verifies an already-applied recommendation after further edits (clears placeholders, confirms a skipped verdict). POST /recommendations/{id}/recheck
recrawl_page Re-fetches the live page after edits made directly on the site rather than through these tools. POST /pages/{pageId}/recrawl
analyze_page Runs a full analysis and returns the report. Accepts page_id or url, and 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
write_recommendations Organizations on the new analysis only. Writes (or rewrites) the recommendations for the page's latest analysis from its stored factor results, without re-running the analysis. Free. Returns at once; the writing takes about 20 seconds, then read them with get_page_report. Use it when an analysis reports recommendations_status: "not_requested". POST /pages/{pageId}/checks/recommendations
create_page Adds a page for content that isn't published yet, like the app's "Add page". Nothing is crawled. Write it with update_content and set_field_draft, then analyze_page reviews the saved content. On a planned site, the page joins the plan with no URL until go_live_site. POST /sites/{siteId}/pages (crawl: false)
delete_page Deletes a page created by mistake, such as a mistyped URL. A soft delete: the page leaves every listing, and the live website is not touched. DELETE /pages/{pageId}

Plan a new site

For a site that isn't live yet, the same flow as the app's "Plan a site".

Tool What it does REST endpoint(s)
plan_site Creates a planned site from a brief about the business (no domain, no crawl) and proposes the pages it needs, each with a name, slug and purpose. Takes 10-40 seconds. POST /sites/plan, POST /sites/{siteId}/plan/generate, GET /sites/{siteId}/plan
get_site_plan Lists the planned pages with their paths, purposes and primary keywords, and whether keyword assignment is still running. GET /sites/{siteId}/plan
edit_site_plan Removes, edits and adds planned pages in one call, or regenerates the whole set from the brief (optionally a revised one). DELETE/PATCH /sites/{siteId}/plan/pages/{pageId}, POST /sites/{siteId}/plan/pages, POST /sites/{siteId}/plan/generate
assign_planned_keywords Assigns a primary keyword to every planned page that has none, then waits up to ~45 seconds for it to finish. Spends credits per page, as in the app. POST /sites/{siteId}/plan/keywords, GET /sites/{siteId}/plan
go_live_site Sets the site's real domain and starts a crawl. Crawled URLs are matched to the planned pages by path, so their keywords, briefs and content carry over. POST /sites/{siteId}/go-live

Generate

Tool What it does REST endpoint(s)
get_content_brief Reads the page's latest content brief: its angle and its proposed sections with their status. GET /pages/{pageId}/outline
generate_content_brief Generates a content brief from the page's primary keyword and SERP, then waits for it (about 45 seconds at most). Counts as a page against the plan allowance. POST /pages/{pageId}/outline → GET /jobs/{jobId}
update_content_brief Accepts, rejects, revises or reorders brief sections. Accepted headings go into the body as a new content version; write the sections next with update_content, passing the returned content_version_id as base_version_id. PATCH /pages/{pageId}/outline/sections
generate_fix Generates an AI fix for one recommendation (or for a title, meta description, h1 or schema with your instructions) and returns the before/after. Nothing is applied yet. Capped at 15 generations per page per analysis, revisions included (generation_cap_reached). POST /recommendations/{recId}/fix or POST /pages/{pageId}/actions → GET /jobs/{jobId} → GET /actions/{actionId}
review_fix Accepts, rejects or revises a generated fix. Accepting saves a metadata fix as the field's draft, or a body fix as a new content version. POST /actions/{actionId}/accept, /reject, /revise
generate_content_draft Rewrites the body with several body recommendations at once (the app's Review Mode). The result is saved as the latest content version straight away, with no review step. Costs 1 credit. POST /pages/{pageId}/content-rewrites → GET /jobs/{jobId} → GET /pages/{pageId}/content-versions/{versionId}
generate_field_draft Generates a title, meta description or h1 draft, with optional instructions. Costs credits, like the app. POST /pages/{pageId}/drafts/{field}/generate
get_page_images Lists the page's images with their alt text state (missing, decorative, draft or crawled). GET /pages/{pageId}/images
set_image_alt Sets one image's alt text, or generates it with generate: true. PUT /pages/{pageId}/images/alt, POST /pages/{pageId}/images/generate-alt
generate_missing_alt Generates alt text for every image still missing it, within a ~45-second budget per call. Call again to continue. Costs credits per image. GET /pages/{pageId}/images, POST /pages/{pageId}/images/generate-alt

Business facts and the Context Library

Tool What it does REST endpoint(s)
get_site_facts Reads the site's verified business facts (company details, contact info, Google listing, testimonials, stats, awards, clients, people) with their citations. Search the full library with search; status: "dismissed" lists removed facts and what removed each. GET /sites/{siteId}/facts
add_site_facts Records facts the user stated in conversation so later generations use them. Deduped by content; never overwrites a fact a person edited. POST /sites/{siteId}/facts
update_site_fact Edits a fact's value, confirms a stale fact is still true, deletes a fact (a fact read from the site is dismissed instead), or restores a removed one (undoing the dismissal that removed it, when there was one). PATCH/DELETE /sites/{siteId}/facts/{factId}, POST /sites/{siteId}/facts/{factId}/restore
get_site_guidance Reads what every generation is told: brand voice, about the business, audiences per offer, preferences and voice notes, and fact-scan status. GET /sites/{siteId}/guidance
update_site_guidance Edits brand voice and business context, and adds, edits or archives preferences and voice notes. PATCH /sites/{siteId}, POST /sites/{siteId}/guidance, PATCH /sites/{siteId}/guidance/{guidanceId}
manage_audiences Adds, edits or removes who an offer is for, or regenerates the generated rows. POST/PATCH/DELETE /sites/{siteId}/audiences..., POST /sites/{siteId}/audiences/regenerate
scan_site Rescans the site for facts (optionally with new skip patterns or instructions), or refreshes only the Google listing. Runs in the background. POST /sites/{siteId}/facts/extract, GET/PUT /sites/{siteId}/facts/settings, POST /sites/{siteId}/facts/google-listing
list_dismissal_fact_changes Lists the fact changes made from the reasons people gave when dismissing recommendations. GET /sites/{siteId}/dismissal-routing/facts
undo_dismissal_fact_change Reverses the Context Library change a dismissal reason made. POST /recommendations/{recId}/dismissal-routing/undo

Checks engine (preview)

Organizations that are not on the new analysis also get these five tools. They run the checks engine as a separate lineage: no score or grade, and not shown in the app.

Tool What it does REST endpoint(s)
checks_analyze_page Runs a checks analysis and returns its factor verdicts with per-check receipts. POST /pages/{pageId}/checks/analyze → GET /pages/{pageId}/checks/analysis
checks_get_report Reads the latest checks analysis without running one. GET /pages/{pageId}/checks/analysis
checks_run Runs the deterministic checks against the page's current state. Free; nothing is stored. GET /pages/{pageId}/checks/preview
checks_regenerate_recommendations Queues a rewrite of a checks analysis's recommendations from its stored results. POST /pages/{pageId}/checks/recommendations
checks_get_rubric Lists every factor and check the checks engine grades the site against. GET /sites/{siteId}/checks/rubric

New analysis (factors)

Organizations moved to the new analysis (rolled out gradually) see a slightly different tool set and different payloads, with the same REST mapping as the switched endpoints:

  • analyze_page and get_page_report return a factor table: every factor with its verdict, measurement and threshold, grouped by area, plus the recommendations once written. There is no score or grade. Passing factors are summarized; pass include_passing: true to list them. analyze_page re-judges content only when it changed (unchanged content spends no allowance), and force re-runs it anyway.
  • get_page_report returns the page's latest analysis (404-style no analysis error when the page has none, including a page that only has a legacy analysis).
  • find_pages takes min_factors_passing_pct (0-100) in place of grade. min_score/max_score still filter the opportunity (demand) score.
  • get_opportunities rows show factors passing (for example 36/41 factors passing, or not analyzed). A row's score is the demand rank, not an analysis score.
  • get_rubric returns the factor list instead of components and grade bands.
  • write_recommendations is added (see the table). An analysis whose recommendations_status is not_requested has findings but no recommendations yet.
  • The checks_* tools (checks_run, checks_analyze_page, checks_get_report, checks_regenerate_recommendations, checks_get_rubric) are not available to these organizations: they are neither listed nor callable. Use analyze_page, get_page_report, write_recommendations and get_rubric.

Everything else is unchanged.

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 a crawled snapshot, saved body content, or staged field drafts, it's analyzed directly (or its existing report is returned), so a page made with create_page and written with update_content is analyzed without a crawl. A discovered page with none of those is crawled first. A planned or hand-added page with no content returns page_not_live / no_content instead of crawling a URL that may not exist.
  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, so confirm uncertain paths with the user before calling analyze_page with a URL. If one slips through, remove it with delete_page.

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 (returning page_not_found/site_not_found otherwise), and recrawl_page only accepts page_id; it doesn't resolve a URL.

Create new content

To take a content idea from nothing to a reviewed draft without publishing anything:

  1. create_page with a working title (the path defaults to its slug, as in the app) and the idea as purpose.
  2. keyword_research on a seed keyword, then update_page to save the primary and secondary keywords.
  3. generate_content_brief, then update_content_brief to accept sections (their headings go into the body).
  4. Write the body with update_content (pass the brief's content_version_id as base_version_id) and the title, meta description and H1 with set_field_draft.
  5. run_checks (free), then analyze_page to review the saved content. No crawl is needed.

Plan a new site

To plan a whole site before it exists:

  1. plan_site with the user's description of the business. Show them the proposed pages.
  2. edit_site_plan to remove, rename, re-slug or add pages, or to regenerate the set from a revised brief.
  3. assign_planned_keywords once the user confirms the set. Check on it with get_site_plan if it is still running.
  4. Build each page as in Create new content (steps 3-5): briefs, body and fields work on planned pages.
  5. When the site is published, go_live_site with its domain. After the crawl, the pages are analyzed like any live page.

Errors

Tool errors carry the same code/message/details as the REST error envelope: payment_required, analysis_in_progress, crawl_in_progress, site_not_found, page_not_found, already_resolved, and so on. A few additions and behaviors are specific to the MCP layer:

  • invalid_arguments means the tool call itself was malformed (bad zod input). Fix the arguments and retry.
  • generation_cap_reached means the page has used its 15 AI generations for the current analysis (generate_fix and revisions). Re-run the analysis to get a fresh set.
  • generate_content_brief, generate_fix and generate_content_draft wait up to ~45 seconds. If the generation is still running, they return status: "processing" with a job_id. Poll it with get_job, then read the result: get_content_brief for a brief, or call generate_fix / generate_content_draft again with the returned action_id / rewrite_id (this resumes the wait and doesn't start a new generation).
  • page_not_live / no_content mean analyze_page was asked to analyze a planned (no live URL yet) or hand-added page that has no crawled or saved content. Write the body with update_content (or stage fields with set_field_draft) and analyze again, or, if the page is live, call recrawl_page first.
  • analyze_page's crawl and analysis waits share a single ~45-second server-side budget for the whole call (not ~90s per step), so 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.