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 as-filed values. Proof-backed results carry an evidence_url that opens the SEC filing at the supporting figure, ready for the caller to inspect.

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 without an MCP entitlement receives HTTP 403. MCP entitlements and limits are currently provisioned only through direct contact and written confirmation. 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 as-filed values with fiscal periods, filing provenance, and an evidence_url on proof-backed facts. 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 with filing provenance and an evidence_url where a supported source locator exists. 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.