MCP
Connect an MCP client and submit change events to March.
March ships a remote, stateless Streamable HTTP MCP endpoint at
https://<your-march-site>/api/mcp. The current server exposes one tool:
submit_event.
Connect a client
Sign in, open Sources, and use the MCP section to generate a key. The
endpoint shown there is the authoritative URL. March displays the plaintext
march_sk_... key only when it is generated; copy it immediately.
Paste this standard HTTP MCP configuration into a compatible client:
{
"mcpServers": {
"march": {
"type": "http",
"url": "https://your-march-site.example/api/mcp",
"headers": {
"Authorization": "Bearer march_sk_REPLACE_ME"
}
}
}
}The endpoint requires Authorization: Bearer <key>. Missing, malformed,
wrong, revoked, or superseded keys return HTTP 401 with
WWW-Authenticate: Bearer. Keys are managed only by the signed-in owner; a
client cannot generate, read, or revoke its own key through MCP. Regenerating
revokes the previous key immediately. Revocation is immediate and idempotent.
Clients must send Accept: application/json, text/event-stream and
Content-Type: application/json. After authentication, a missing or
incompatible Accept header is rejected with HTTP 406 by the Streamable HTTP
transport.
submit_event
Required fields:
| Field | Type and limit | Meaning |
|---|---|---|
source | non-empty string, max 100 | System that produced the event |
type | non-empty string, max 100 | Source-specific event type |
title | non-empty string, max 300 | Human-readable title |
description | non-empty string, max 10,000 | What changed and its context |
Optional fields are url (a valid URL, max 2,000 characters), external_id
(non-empty string, max 300), and metadata (a JSON object whose serialized
form is at most 16 KB). The outer request is also subject to the hosting
platform's request-size limit.
Example of a harmless documentation event:
{
"source": "release-notes",
"type": "documentation_update",
"title": "Search guide now recommends semantic search",
"description": "The release notes changed the recommended search workflow; check public docs and support guidance for stale instructions.",
"url": "https://example.com/releases/2026-10-05",
"external_id": "release-2026-10-05-search-guide",
"metadata": { "environment": "example" }
}March stores the event, stamps the authenticated MCP key owner for attribution, and starts the normal investigation workflow using the configured company context. The tool returns structured content in this shape:
{
"event_id": "…",
"investigation_id": "…",
"status": "pending",
"deduplicated": false,
"march_url": "https://your-march-site.example/changes/…"
}status is one of pending, running, completed, or failed.
march_url is omitted when March cannot resolve its site URL. Validation or
start failures are returned as an MCP tool error with a human-readable message.
Starting work needs NETLIFY_DB_URL, configured company context, and
MARCH_BACKGROUND_SECRET, plus a resolvable site URL. The trigger resolves the
URL in this order: DEPLOY_URL, DEPLOY_PRIME_URL, URL, then the local
fallback MARCH_BACKGROUND_BASE_URL.
If the MCP background trigger fails after the row is created, submit_event
returns an InvestigationTriggerError but does not mark the row failed. It
remains pending until its 20-minute timeout, and resubmitting the same event
can join that live pending investigation.
Deduplication
With external_id, March uses source plus external_id as the stable source
identity. Without it, March derives an identity from a SHA-256 hash of the
trimmed source, type, title, description, and url (empty when URL is
omitted). MCP deduplication joins only live pending or running
investigations through the in-flight createPendingOrGetExisting path;
completed or failed rows do not permanently block a new run. The stored
sourceExternalId is lowercased, and source lookup compares it case-
insensitively.
Common fixes: check the exact /api/mcp URL, use the full key as a Bearer
token (not the masked Sources display), send valid JSON and all four required
fields, keep metadata under 16 KB serialized, and generate a new key if the
old one was regenerated or revoked. A valid event still needs the start
prerequisites above.