mcp

run_audit

MCP tool that queues a full SEO audit with Core Web Vitals. Returns an audit_id immediately, then poll get_audit until status is complete.

What this tool does

run_audit queues a full SEO and AI-readability audit and returns immediately with the audit envelope. It is the authenticated, full-fat version of run_audit_anonymous.

  • Queues the audit asynchronously and returns status: queued plus a real audit_id in 1-2 seconds.
  • Pulls Core Web Vitals (LCP, CLS, INP) from Google PageSpeed Insights as part of the run.
  • If the user has linked Google Analytics 4 and Search Console, also pulls organic traffic signals: surfaced separately via get_organic_traffic.
  • Counts against the user’s plan allowance (Free 10/mo, Starter 50/mo, Pro unlimited) and respects per-domain cooldowns.
  • Returns the same McpAuditResponse shape as get_audit. Findings will be empty until the audit transitions to complete.

Why it matters

run_audit is the right tool whenever you need a persistent audit you can fetch later, a PDF, organic traffic data, or Core Web Vitals. Anonymous audits cover ~90 checks but skip PSI; this tool covers everything.

Concrete workflows:

  • An audit-on-PR bot calls run_audit when a preview deploy ships, captures the audit_id, polls get_audit every 5 s for up to 60 s, and posts a delta comment versus the last audit on the same URL.
  • A weekly cron agent iterates the user’s most-visited landing pages and queues a fresh run_audit for each, then mails a digest from list_audits.

How to use it

The tool is async by design. Treat the immediate response as an acknowledgment, then poll get_audit with the returned audit_id every few seconds. Typical end-to-end completion is 10-30 s; allow up to 90 s for slow targets.

Input schema

{
  "type": "object",
  "properties": {
    "url": { "type": "string", "format": "uri", "maxLength": 2000 }
  },
  "required": ["url"]
}

Response schema sample

{
  "audit_id": "aud_01HZ8X9YP7K3T2N6Q5",
  "url": "https://example.com",
  "status": "queued",
  "total_score": null,
  "module_scores": {},
  "findings": [],
  "report_url": "https://app.metricspot.com/audits/aud_01HZ8X9YP7K3T2N6Q5",
  "created_at": "2026-05-13T10:18:04.000Z"
}

When the audit completes, the same shape is returned by get_audit with status: "complete", a populated total_score, module_scores, and the findings array.

Claude Code

claude mcp add --transport http metricspot https://mcp.metricspot.com/mcp \
  --header "Authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Prompt:

Run a full MetricSpot audit on https://example.com, poll until complete, and summarize the failing critical and major findings.

Cursor

.cursor/mcp.json:

{
  "mcpServers": {
    "metricspot": {
      "url": "https://mcp.metricspot.com/mcp",
      "headers": {
        "Authorization": "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Prompt:

Queue an audit for this page, wait for completion, and tell me my Core Web Vitals scores.

Python (run + poll)

import httpx, time, json

URL = "https://mcp.metricspot.com/mcp"
HEADERS = {
    "content-type": "application/json",
    "accept": "application/json, text/event-stream",
    "authorization": "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx",
}

def call(name, args):
    r = httpx.post(URL, headers=HEADERS, json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {"name": name, "arguments": args},
    }, timeout=60.0)
    return json.loads(r.json()["result"]["content"][0]["text"])

queued = call("run_audit", {"url": "https://example.com"})
audit_id = queued["audit_id"]

for _ in range(30):
    time.sleep(3)
    result = call("get_audit", {"audit_id": audit_id})
    if result["status"] in ("complete", "failed"):
        break

print(result["total_score"], len(result["findings"]))

Node / TypeScript (with @modelcontextprotocol/sdk)

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
  new URL("https://mcp.metricspot.com/mcp"),
  {
    requestInit: {
      headers: { authorization: "Bearer ms_live_xxxxxxxxxxxxxxxxxxxxxxxx" },
    },
  },
);
const client = new Client({ name: "audit-on-pr", version: "1.0.0" });
await client.connect(transport);

const queued = await client.callTool({
  name: "run_audit",
  arguments: { url: "https://example.com" },
});
const auditId = JSON.parse(queued.content[0].text as string).audit_id;
console.log("queued", auditId);

Common errors

CodeWhenAction
UNAUTHORIZED (401)Missing or invalid Bearer tokenIssue a key at https://app.metricspot.com/settings/api-keys
QUOTA_EXCEEDED (402)Monthly plan allowance reachedUpgrade at https://app.metricspot.com/billing
RATE_LIMITED (429)Per-domain cooldown hitWait the indicated window before re-queuing the same domain
INVALID_URL (400)Bad URL, non-public host, or > 2000 charsPass an absolute https:// URL
UPSTREAM_FAILED (5xx)PSI or crawler upstream blipRetry once with backoff

Frequently asked questions

How long until the audit is complete?

10-30 seconds for typical sites. Slow targets, JS-heavy pages, or sites with large amounts of structured data can take up to 90 seconds. Poll get_audit every 3-5 seconds; the audit is fully computed once status flips to complete.

Does run_audit cost an audit even if it fails?

A failed audit does not consume plan allowance. Quota only decrements once the run completes successfully and findings are stored. PSI rate-limit failures are retried internally before being surfaced.

Can I get the PDF and organic traffic in the same call?

No, those are separate tools by design. After run_audit completes, call get_audit_pdf for the signed PDF URL and get_organic_traffic for the GA4 + GSC snapshot. Both reference the same audit_id.

Sources

Last updated 2026-05-13