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: queuedplus a realaudit_idin 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
McpAuditResponseshape asget_audit. Findings will be empty until the audit transitions tocomplete.
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_auditwhen a preview deploy ships, captures theaudit_id, pollsget_auditevery 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_auditfor each, then mails a digest fromlist_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
| Code | When | Action |
|---|---|---|
UNAUTHORIZED (401) | Missing or invalid Bearer token | Issue a key at https://app.metricspot.com/settings/api-keys |
QUOTA_EXCEEDED (402) | Monthly plan allowance reached | Upgrade at https://app.metricspot.com/billing |
RATE_LIMITED (429) | Per-domain cooldown hit | Wait the indicated window before re-queuing the same domain |
INVALID_URL (400) | Bad URL, non-public host, or > 2000 chars | Pass an absolute https:// URL |
UPSTREAM_FAILED (5xx) | PSI or crawler upstream blip | Retry 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