Skip to content

MCP server

The StockAlloy MCP server lets AI agents pull verified, evidence-linked SEC facts directly into their answers. Instead of a model recalling a plausible-sounding revenue figure from training data, it calls search_facts(ticker: "DECK", query: "direct-to-consumer revenue") and gets exact as-filed values — each carrying an evidence_url that opens the SEC filing with the figure highlighted, ready to cite.

POST https://api.stockalloy.com/mcp

Stateless MCP over streamable HTTP: one JSON-RPC 2.0 message per request, JSON response, no sessions and no SSE. The server handles initialize, ping, tools/list, and tools/call; notifications receive 202 Accepted. Supported protocol versions: 2025-06-18, 2025-03-26, 2024-11-05. JSON-RPC batch requests are not supported (removed in MCP 2025-06-18). Request bodies are capped at 64KB (HTTP 413 beyond that).

Authenticate every request with your API key in the X-API-Key header — the same sa_b2b_... key used for the REST API. A valid key on a plan without MCP access receives HTTP 403 with a JSON-RPC error: MCP requires the Builder plan or higher. Rate limits and monthly call quotas are shared with the REST API; each MCP call is metered as mcp:{tool} in your developer portal usage view.

Claude Code speaks streamable HTTP natively — no proxy needed:

Terminal window
claude mcp add --transport http stockalloy https://api.stockalloy.com/mcp \
--header "X-API-Key: $STOCKALLOY_API_KEY"

Then just ask:

What was Deckers’ direct-to-consumer revenue in FY2024, and how fast has it grown over 3 years? Cite your sources.

Claude will call search_facts, answer with the exact filed values, and cite the evidence links.

Claude Desktop launches MCP servers over stdio, so use the mcp-stdio-proxy.mjs bridge — a dependency-free Node 18+ script:

Terminal window
curl -O https://docs.stockalloy.com/mcp-stdio-proxy.mjs

Then add to claude_desktop_config.json:

{
"mcpServers": {
"stockalloy": {
"command": "node",
"args": ["/absolute/path/to/mcp-stdio-proxy.mjs"],
"env": {
"STOCKALLOY_MCP_URL": "https://api.stockalloy.com/mcp",
"STOCKALLOY_API_KEY": "sa_b2b_..."
}
}
}
}

The proxy forwards each stdio JSON-RPC message to the HTTP endpoint. Set MCP_PROXY_DEBUG=1 for request/response logging on stderr, and MCP_PROXY_TIMEOUT_MS to change the per-request timeout (default 30000).

Three read tools. Results come back as a single JSON text content block; tool-domain failures return isError: true with a structured body (see error codes).

Search verified SEC financial facts for a company. Give a ticker and a plain-English metric (e.g. 'revenue', 'direct-to-consumer revenue', 'launch backlog'). Returns exact as-filed values with fiscal periods, filing provenance, and an evidence_url per fact. If the metric is ambiguous, the response lists candidates — pick one and call again with its metric_id.

{
"type": "object",
"properties": {
"ticker": { "type": "string", "description": "Stock ticker, e.g. AAPL" },
"query": { "type": "string", "description": "Metric in plain English, e.g. 'gross margin', 'cloud revenue'" },
"metric_id": { "type": "string", "description": "Exact metric_id (e.g. us-gaap#Revenues) or series_key from a prior candidates list. Skips free-text resolution." },
"period_type": { "type": "string", "enum": ["annual", "quarterly", "any"], "description": "Default: annual" },
"fiscal_year": { "type": "number", "description": "Filter to one fiscal year, e.g. 2024" },
"limit": { "type": "number", "description": "Max periods returned (default 8, max 40)" },
"include_unproven": { "type": "boolean", "description": "Include rows lacking evidence links (default false)" }
},
"required": ["ticker"],
"anyOf": [{ "required": ["query"] }, { "required": ["metric_id"] }]
}

The result carries a resolution block explaining how the metric was resolved (headline_recipe, series_search, or exact_metric_id), a confidence level, and up to 3 alternatives so the caller can self-correct. Headline metrics (revenue, net income, EPS, …) route through the financials recipe system, which bridges the ~2018 XBRL revenue-tag succession that raw concept series don’t.

Fetch a full time series by series_id (from search_facts alternatives/candidates) or by ticker + headline metric. Returns titled, unit-labeled points, each with filing provenance and an evidence_url. Use for trends, growth rates, and charts.

{
"type": "object",
"properties": {
"series_id": { "type": "string", "description": "Series ID from search_facts resolution/candidates" },
"ticker": { "type": "string", "description": "Alternative to series_id: ticker + metric" },
"metric": { "type": "string", "description": "Headline metric name when using ticker form, e.g. 'revenue'" },
"period_type": { "type": "string", "enum": ["annual", "quarterly", "any"] },
"limit": { "type": "number", "description": "Max most-recent points (default 20, max 60)" }
},
"anyOf": [{ "required": ["series_id"] }, { "required": ["ticker", "metric"] }]
}

Responses include a caveats array — e.g. raw-concept series don’t merge XBRL predecessor/successor tags, so prefer the ticker+metric form for headline metrics.

Fetch the proof behind a fact: the source snippet from the SEC filing, the table row/column labels, the parsed value, and links (viewer URL with the figure highlighted, SEC EDGAR URL). Accepts an evidence_id or a full evidence_url from a prior result. Use it when the agent needs to quote the filing text or double-check what a number means.

{
"type": "object",
"properties": {
"evidence_id": { "type": "string" },
"evidence_url": { "type": "string", "description": "A full evidence_url from a prior tool result; the id is parsed out" }
},
"anyOf": [{ "required": ["evidence_id"] }, { "required": ["evidence_url"] }]
}

Returns snippet, locator_type, row_label, column_header, parsed_value, normalized_unit, source (cik/accession/form type/filed-at), viewer_url, and sec_url.

Tool-domain failures return isError: true with body { "error": { "code", "message", … } }:

Code Meaning
UNKNOWN_TICKER Company not found
METRIC_NOT_FOUND No metric matched the query (includes suggestions)
METRIC_AMBIGUOUS Multiple plausible series — includes candidates; call again with a candidate’s metric_id/series_key
NO_DATA_FOR_PERIOD Metric exists but not for the requested period filter
SERIES_NOT_FOUND Unknown or empty series
EVIDENCE_NOT_FOUND Evidence ID/URL didn’t resolve
UPSTREAM_UNAVAILABLE Transient backend outage — retry shortly; not a data gap
INTERNAL_ERROR Unexpected failure

The server never guesses silently: search_facts auto-selects only when the top hit clearly dominates; otherwise you get METRIC_AMBIGUOUS with candidates and no fact rows.

Tool results are edge-cached: 15 minutes for search_facts and get_series, 30 days for get_evidence (filings are immutable). Facts for covered tickers are typically available within days of filing; there is no hard latency SLA today.