Messora MCP Server
Messora exposes a Model Context Protocol (MCP) remote server over Streamable HTTP. It lets LLM agents (Claude, Codex, Cursor and other MCP clients) call scrape, crawl, search and query jobs and usage — reusing the same account, API key and credit pool as the REST API.Endpoint
The
/health endpoint is independent of the backend and returns
{"status": "ok", "service": "messora-mcp"} without requiring authentication.
Authentication
The MCP uses only theAuthorization: Bearer header:
Configuring clients
Claude Desktop
Claude Desktop uses a JSON config at~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
Claude Desktop does not support environment variable interpolation in config.
Use a placeholder and replace it, or set the key directly — the file is local
and not versioned.
Cursor
Cursor uses an.mcp.json file at the project root or ~/.cursor/mcp.json:
Cursor → Settings → MCP → Refresh to load the server.
Codex CLI
Codex uses aconfig.toml file at ~/.codex/config.toml:
MCP Inspector
- Transport type: Streamable HTTP
- URL:
https://mcp.messora.dev/mcp - Authentication: Bearer token
- Token env var:
MESSORA_API_KEY
Never hardcode your API key in versioned config files. For Codex, always use
an environment variable (
$MESSORA_API_KEY). For Claude Desktop and Cursor,
the config is local (not versioned) — replace the placeholder with your key.Available tools
The server exposes exactly five tools:scrape_url
Extracts content from a URL. Consumes credit only when the backend returns
scrape_status = success.
start_crawl
Starts an async crawl and returns job_id. Poll get_job until the state
becomes SUCCESS, FAILURE or REVOKED.
start_search
Starts an async premium search and returns job_id.
get_job
Queries the state of a crawl or search job.
States:
PENDING → STARTED → SUCCESS | FAILURE | REVOKED
We recommend polling every 2 seconds with backoff. The MCP adapter does not
perform internal polling or automatic retries of charged operations.
get_usage
Queries plan, credits used and account balance. No product arguments.
Same payload as the REST GET /account/usage.
Complete walkthrough: All five tools in one flow
The five tools have different patterns: sync (scrape_url), async polling (start_crawl, start_search → get_job), and query (get_usage). Here’s a realistic sequence:
scrape_url— one URL, need result now (markdown/structured JSON/raw HTML)start_crawl+get_job— many URLs on same domain, in background, final batch of resultsstart_search+get_job— search query across indexed content, in backgroundget_usage— check account plan and credit balance before/after bulk operations
Async flow: start_* → get_job
Crawl and search are async in the backend. The recommended flow is:
Response size
Every tool response is capped at 100,000 characters. If it exceeds that, text fields (markdown, rawHtml) are truncated while preserving structure,
status, metering and valid JSON:
Errors
On a non-2xx response the adapter discards the backend body entirely and
derives
error.message from the HTTP status alone. Branch on error.code,
never on error.message:
data.json always holds exactly the fields your json_schema declares —
the backend never injects extra keys into it, including a field named
error. If extraction fails entirely, every declared field is null and a
sibling field, data.json_extraction_error_code (currently only
"extraction_failed"), reports it instead. When that code is present the
MCP adds data.json_extraction_error_message with the fixed English text
"Structured extraction failed for this schema. Try again or adjust json_prompt/json_schema.". If your schema declares its own error
property, it is never touched — that field only ever holds what your schema
requested, success or failure. _extraction_failed is reserved for this
internal signal and rejected as a json_schema property name
(validation_error).
Input the adapter validates itself (duplicate formats, json_schema missing
for the json format, pdf_ocr without parse_pdf, overlapping
include_domains/exclude_domains, malformed job_id) returns
validation_error with a specific message naming the offending field.
A 2xx body is forwarded, so system-generated text inside it is normalized
to English instead: a job in FAILURE returns the fixed
data.error = "The job could not be completed. Try again.", and a search job
in STARTED reports meta.stage as starting or running. Extracted web
content (results) is never rewritten.
The MCP is a thin adapter: auth, billing, rate limiting and jobs belong to the
FastAPI backend. The same limits and costs as the REST API apply.