HelpMCP Protocol Guide

MCP Guide — the audit tools an AI assistant can call

This guide covers the MCP endpoint (POST /mcp) — a Model Context Protocol server that lets an AI assistant read a client's audits, mark issues fixed, add domains, and manage projects.

Quick Start

1

Generate a token

In your client portal, go to Domains → API access → Generate token.

2

Add the server

The portal shows this command pre-filled with your token:

claude mcp add --transport http wsc-crawler https://your-server/mcp \
  --header "Authorization: Bearer $TOKEN"

Then ask your assistant: "what's still outstanding on the latest audit for example.com?"

Transport

Stateless JSON-RPC 2.0 over a single POST. Deliberately minimal — the endpoint has no server-initiated messages to deliver, so the streaming half of the spec is not implemented.

Authentication

Uses the same per-client token as the REST API. An unauthenticated request gets a 401 Unauthorized with a WWW-Authenticate: Bearer header.

Origin Validation

The Origin header is checked as a DNS-rebinding guard. Native MCP clients (Claude Code/Desktop) send no origin and are allowed by default.

Methods

initialize

Handshake. Returns capabilities, server info, and usage instructions.

tools/list

All 29 tools with full JSON Schemas.

tools/call

Invoke one tool.

ping

Returns an empty result.

resources/list

Returns empty — answered politely though not advertised.

prompts/list

Returns empty — same.

Version negotiation is permissive: recognized versions include 2024-11-05 through 2026-07-28.

Tools

29 tools mapping 1:1 onto the REST endpoints. All schemas are additionalProperties: false.

Audits

list_domains

GET /domains

add_domain

POST /domains

list_crawls

GET /{domain}/crawls

get_audit_results

GET /{domain}/{crawlId}/{type}

resolve_issue

POST /{domain}/{crawlId}/resolve

start_audit

POST /{domain}/audit

Monitoring

  • get_monitoring — status
  • request_monitoring — request schedule
  • cancel_monitoring — cancel slot

Feedback

  • feedback_overview — all sites
  • list_feedback — domain items
  • set_feedback_status — open/done
  • add_feedback_note — internal note

Launch & Migration

list_projects

GET /{domain}/projects

start_project

POST /{domain}/projects

list_project_tasks

GET /{domain}/projects/{type}/tasks

set_task_state

POST /{domain}/projects/{type}/tasks/{taskKey}/state

import_redirects

POST /{domain}/redirects/import

check_redirects

POST /{domain}/redirects/check

Time & Account

get_time_report

GET /time/report

log_time_entry

POST /time/entries

get_usage

GET /account

Parameters

ParameterTypeNotes
domainstringMust be on the caller's account.
crawlIdstringA crawl UUID, or the literal "latest".
type (results)stringpages, issues, links, images, slow-pages, etc.
status (results)enumactive (default), fixed, ignored, all.
modeenumbasic or advanced (full audit).

Response Shapes

These are the payloads an agent hits first, transcribed field-for-field from the query columns each operation actually selects.

get_usage

{
  "budget": { 
    "budget": 20000, "used": 4300, "reserved": 0, "remaining": 15700,
    "windowStart": "2026-06-15T00:00:00.000Z", "resetsAt": "2026-06-22T00:00:00.000Z" 
  },
  "residentialProxy": { "cap": 5000, "used": 120, "remaining": 4880 },
  "monitoring": { "slotsTotal": 10, "slotsUsed": 3, "slotsRemaining": 7 }
}

list_crawls

{
  "domain": "example.com",
  "crawls": [
    { 
      "crawlId": "8f1c2e4a-...", "startTime": "...", "endTime": "...", "status": "Completed",
      "mode": "advanced", "pages": 84, "brokenPages": 1, "brokenLinks": 3,
      "brokenImages": 0, "brokenCss": 0, "brokenJs": 0, "warnings": 12 
    }
  ]
}

get_monitoring

{
  "domain": "example.com", "monitored": true, "pending": false,
  "schedule": "0 6 * * 1", "lastCheckedAt": "2026-06-20T06:00:00.000Z",
  "lastCrawlId": "8f1c2e4a-...", "maxPages": 200
}

list_feedback (one item)

{
  "id": "...", "pageUrl": "https://example.com/pricing", "pagePath": "/pricing",
  "note": "typo in the second bullet", "status": "open",
  "viewport": "1440x900", "createdAt": "...",
  "attachments": [ 
    { "id": "...", "kind": "screenshot", "mime": "image/webp", "bytes": 48213,
      "url": "https://.../api/project/{editToken}/feedback/{attachmentId}" } 
  ],
  "targetSelector": ".hero h1", "category": "content", "hidden": false,
  "internalNote": null, "noteSentAt": null
}

list_project_tasks (one task)

{
  "key": "dns-live", "type": "Pre", "category": "DNS", "group": "Go-live",
  "task": "Point DNS at the new host", "auto": true, "custom": false,
  "edited": false, "state": "open", "source": null, "hidden": false
}

list_redirects (one row)

{
  "id": "...", "oldUrl": "https://old.example.com/about-us",
  "expectedNewUrl": "https://new.example.com/about",
  "lastStatus": 301, "resolvedTo": "https://new.example.com/about",
  "result": "ok_301", "hidden": false
}

Errors

The distinction that matters: a tool that refuses is a successful JSON-RPC call.

Tool Failures (200 OK)

Returns isError: true and a message. The model needs to see why it failed (e.g., rate limit, invalid mode) so it can correct itself.

Protocol Faults

Reserved for things a model cannot fix — malformed JSON (-32700), unauthorized (-32600), or unknown method (-32601).

Two things that will bite you

1. issueId is only valid inside its own crawl

issues.id is re-assigned on everycrawl. An ID from Monday's audit points at a different issue in Friday's — or at nothing. Always pass the crawlId the ID came from.

2. "Fixed" is scoped to one crawl, on purpose

An assistant marking its own homework is a risk. resolve_issuemarks it fixed in the current audit, but a re-crawl will re-detect the issue if the fix didn't really land.The honest loop: fix → resolve → deploy → re-audit to verify.

Implementation Notes

Hand-rolled, no SDK: The MCP handler framing is smaller than an SDK shim. It handles JSON-RPC dispatch directly onto raw node:http responses.

No logic in transport: The MCP handler does framing only; it calls the same internal handlers as the REST API. Auth, ownership, and rate limits exist once.

Rate Limits: Shared with the REST API. Reads (600/15min), Writes (300/15min), and Audits (30/15min).