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.
Endpoint
Section titled “Endpoint”POST https://api.stockalloy.com/mcpStateless 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.
Connect from Claude Code
Section titled “Connect from Claude Code”Claude Code speaks streamable HTTP natively — no proxy needed:
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.
Connect from Claude Desktop
Section titled “Connect from Claude Desktop”Claude Desktop launches MCP servers over stdio, so use the
mcp-stdio-proxy.mjs bridge — a dependency-free Node 18+ script:
curl -O https://docs.stockalloy.com/mcp-stdio-proxy.mjsThen 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_facts
Section titled “search_facts”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.
get_series
Section titled “get_series”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.
get_evidence
Section titled “get_evidence”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 error codes
Section titled “Tool error codes”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.
Caching & freshness
Section titled “Caching & freshness”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.