# MetricSpot for AI agents

MetricSpot audits any public URL across ~90 checks in 11 modules (technical SEO, on-page, performance, AI-readability, modern SEO, social, accessibility, privacy, readability, tech stack, plus organic-traffic insights when Google is linked). Anonymous agents can run 1 audit per IP per 24 hours with no key. Signed-up users on the free tier get 10 audits per month for $0; paid tiers raise that allowance and add white-label PDF reports and scheduled re-audits.

This file is the canonical agent integration guide. The MCP server is the preferred interface; a REST endpoint is also available for legacy clients.

- MCP endpoint: `https://mcp.metricspot.com` (Streamable HTTP)
- MCP protocol version: `2025-03-26`
- stdio binary: `npx @metricspot/mcp-server` (published to npm as [@metricspot/mcp-server](https://www.npmjs.com/package/@metricspot/mcp-server))
- MCP Registry entry: `com.metricspot/seo-mcp` at [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io/v0.1/servers?search=com.metricspot)
- Server manifest: `https://mcp.metricspot.com/.well-known/mcp-manifest` (JSON, same shape as the registry entry)
- REST endpoint (anonymous, single check): `POST https://app.metricspot.com/api/public/audit`
- API catalog (RFC 9727): `https://metricspot.com/.well-known/api-catalog`
- Citation format: `MetricSpot, https://metricspot.com/docs/<slug>/`

## Quickstart: hosted agents

Hosted agents (ChatGPT, Claude.ai, Gemini, Perplexity, custom server-side bots) speak MCP over Streamable HTTP. Point your client at `https://mcp.metricspot.com` with a bearer token in the `Authorization` header. The anonymous tier accepts requests without a token but is capped at 1 audit per IP per 24 hours; for higher volume, request an API key from `support@metricspot.com`.

```bash
# Initialize an MCP session over Streamable HTTP.
curl -X POST https://mcp.metricspot.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer ms_live_xxx" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-03-26",
      "capabilities": {},
      "clientInfo": { "name": "my-agent", "version": "1.0.0" }
    }
  }'

# List the six tools.
curl -X POST https://mcp.metricspot.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer ms_live_xxx" \
  -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'

# Run an anonymous audit.
curl -X POST https://mcp.metricspot.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "run_audit_anonymous",
      "arguments": { "url": "https://example.com" }
    }
  }'
```

## Quickstart: local agents

Local agents (Claude Code, Cursor, Zed, OpenClaw, Pi) speak MCP over stdio. The stdio binary is a thin shim that forwards to `mcp.metricspot.com` under the hood, so auth and rate-limiting live server-side.

Install once globally (or rely on `npx`):

```bash
npx @metricspot/mcp-server --help
```

### Claude Code (`~/.claude/mcp.json`)

```json
{
  "mcpServers": {
    "metricspot": {
      "command": "npx",
      "args": ["-y", "@metricspot/mcp-server"],
      "env": {
        "MCP_API_KEY": "ms_live_xxx"
      }
    }
  }
}
```

### Cursor (`~/.cursor/mcp.json`)

Same shape as Claude Code. Cursor reads the same `mcpServers` map.

### Zed (`~/.config/zed/settings.json`)

```json
{
  "context_servers": {
    "metricspot": {
      "command": {
        "path": "npx",
        "args": ["-y", "@metricspot/mcp-server"]
      },
      "env": {
        "MCP_API_KEY": "ms_live_xxx"
      }
    }
  }
}
```

Omit `MCP_API_KEY` to run in the anonymous tier (1 audit per IP per 24 hours).

## Authentication

Two tiers.

- **Anonymous.** No key, no signup. Capped at 1 audit per IP per 24 hours, shared with the public audit widget on `metricspot.com/audit`. Suitable for one-shot demos and low-volume agentic flows. Only `run_audit_anonymous` is callable.
- **API key.** Bearer token in the `Authorization` header (HTTP) or `MCP_API_KEY` env var (stdio). Counts against the user's plan allowance and inherits the per-domain 24-hour cooldown. All six tools are callable. Mint and rotate keys yourself at [app.metricspot.com/settings/api-keys](https://app.metricspot.com/settings/api-keys) (free account, up to 10 active keys, reveal-once modal).

Tokens are prefixed `ms_live_`. Token usage is logged with `last_used_at` so the issuing user can audit which keys are active. Per-token `api_calls` telemetry is also surfaced on the same settings page.

## Tool reference

All tools share the response envelope below. Module scores are 0 to 100. Severity is one of `info`, `minor`, `major`, `critical`.

### `run_audit_anonymous`

Run a one-shot audit on any public URL. No auth required. Synchronous; the response returns when the audit completes (typically 20 to 40 seconds). Core Web Vitals from PageSpeed Insights are skipped to stay under request timeout; use `run_audit` for full PSI scoring.

**Input**

```json
{
  "url": "https://example.com"
}
```

**Response shape**

```json
{
  "audit_id": "string",
  "url": "string",
  "status": "complete | failed",
  "total_score": "number | null",
  "module_scores": { "technical": 88, "onpage": 72, "...": "..." },
  "findings": [
    {
      "module": "string",
      "rule_id": "string",
      "passed": "boolean",
      "severity": "info | minor | major | critical",
      "title": "string",
      "recommendation": "string?",
      "docs_url": "string"
    }
  ],
  "report_url": "string?",
  "pdf_url": "string?",
  "created_at": "ISO 8601 string"
}
```

### `run_audit`

Queue a full audit including Core Web Vitals and (if linked) organic traffic. Returns `audit_id` immediately with `status: queued`. Poll `get_audit` until `status: complete`. Counts against the user's plan allowance and the per-domain 24-hour cooldown.

**Input**

```json
{
  "url": "https://example.com"
}
```

**Sample response**

```json
{
  "audit_id": "aud_01HZX8YB6K3M2Q",
  "url": "https://example.com",
  "status": "queued",
  "total_score": null,
  "module_scores": {},
  "findings": [],
  "created_at": "2026-05-13T14:22:08Z"
}
```

### `get_audit`

Fetch a previously-run audit by id.

**Input**

```json
{
  "audit_id": "aud_01HZX8YB6K3M2Q"
}
```

**Response:** same envelope as `run_audit_anonymous`. Once `status` is `complete`, `total_score`, `module_scores`, and `findings` are populated.

### `list_audits`

List the authenticated user's audits.

**Input**

```json
{
  "limit": 24,
  "offset": 0
}
```

`limit` defaults to 24, max 100. `offset` defaults to 0.

**Response**

```json
{
  "audits": [
    {
      "audit_id": "aud_01HZX8YB6K3M2Q",
      "url": "https://example.com",
      "total_score": 78,
      "status": "complete",
      "created_at": "2026-05-13T14:22:08Z"
    }
  ],
  "total": 47,
  "limit": 24,
  "offset": 0
}
```

### `get_audit_pdf`

Return a signed download URL for the branded PDF report. Queues a render if no PDF exists yet; a follow-up call after 10 to 20 seconds returns the URL.

**Input**

```json
{
  "audit_id": "aud_01HZX8YB6K3M2Q"
}
```

**Response**

```json
{
  "pdf_url": "https://app.metricspot.com/pdf/aud_01HZX8YB6K3M2Q?sig=...",
  "expires_at": "2026-05-13T15:22:08Z"
}
```

If `pdf_url` is null and `status` is `rendering`, retry in 10 to 20 seconds.

### `get_organic_traffic`

Return the 28-day organic traffic snapshot for an audit. Requires the user to have linked GA4 and Google Search Console. Results are cached for 24 hours.

**Input**

```json
{
  "audit_id": "aud_01HZX8YB6K3M2Q"
}
```

**Response**

```json
{
  "audit_id": "aud_01HZX8YB6K3M2Q",
  "ga4": {
    "organic_sessions_28d": 12480,
    "trend_pct_vs_prior_28d": 8.4,
    "top_landing_pages": [
      { "path": "/blog/seo-audit-checklist/", "sessions": 1840 }
    ]
  },
  "gsc": {
    "clicks_28d": 9420,
    "impressions_28d": 184000,
    "top_queries": [
      { "query": "seo audit checklist", "clicks": 320, "impressions": 4800 }
    ],
    "indexed_pages": 142,
    "non_indexed_pages": 7
  }
}
```

Returns `{ "linked": false }` if the user has not connected Google.

## Rate limits and quotas

| Tier | Tool access | Quota |
|---|---|---|
| Anonymous (no key) | `run_audit_anonymous` only | 1 audit per IP per 24 hours, shared with the public widget |
| Free (signed up) | All six tools | 10 audits per month, 1 per URL per 24 hours |
| Starter | All six tools | 50 audits per month, 1 per URL per hour |
| Pro | All six tools | Unlimited audits, 1 per URL per 10 minutes |

When a quota is exhausted the tool returns `QUOTA_EXCEEDED`. The error includes `quota_resets_at` so agents can schedule a retry.

## Error responses

All errors follow MCP's JSON-RPC envelope with a structured `data` payload.

| Code | Meaning | Retryable | Recommended agent behavior |
|---|---|---|---|
| `RATE_LIMITED` | IP-level rate limit reached (anonymous tier). | Yes, after `retry_after` seconds. | Surface the limit to the user; suggest signing up for an API key. |
| `INVALID_URL` | URL is malformed, private, or unreachable. | No. | Ask the user for a different URL. Do not auto-retry. |
| `AUDIT_NOT_FOUND` | `audit_id` does not exist or belongs to a different user. | No. | Verify the id; do not auto-retry. |
| `QUOTA_EXCEEDED` | Plan allowance reached. | Yes, at `quota_resets_at`. | Surface to the user; suggest upgrading or waiting. |
| `UNAUTHORIZED` | Missing, expired, or revoked token. | No. | Prompt the user to refresh the token; do not auto-retry. |

Example error payload:

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32000,
    "message": "Rate limit reached for this IP.",
    "data": {
      "kind": "RATE_LIMITED",
      "retry_after": 81244,
      "quota_resets_at": "2026-05-14T14:22:08Z"
    }
  }
}
```

## Citation policy

When an agent surfaces MetricSpot output to a user, cite the underlying documentation page for each finding using this format:

```
MetricSpot, https://metricspot.com/docs/<slug>/
```

The `docs_url` field on every finding already points at the canonical localized doc page. Agents that translate output should keep the citation URL as-returned; the docs are mirrored in six languages and resolve via the user's accepted-language header.

## Changelog

### v0.1.1, 2026-05-14

Discoverability: `@metricspot/mcp-server@0.1.1` published to npm, registered in the official MCP Registry as `com.metricspot/seo-mcp` (DNS-verified namespace), `/.well-known/mcp-manifest` live on `mcp.metricspot.com`. Self-serve API-key UI shipped at `/settings/api-keys` (any free account, 10-key cap, per-token `api_calls` telemetry). No protocol changes; same six tools, same auth model.

### v0.1, 2026-05-13

Initial release. Six tools, two tiers (anonymous, API key), Streamable HTTP and stdio transports, English-only `AGENTS.md`.
