Threat intelligence API / integrations
MCP server
Give any MCP client live threat intelligence: threatcluster-mcp exposes the ThreatCluster API as ten read-only tools for Claude Desktop, Claude Code, Cursor, VS Code, Windsurf and Zed. Incident clusters with scores and timelines, entity profiles, CVE records with KEV and exploit status, and ransomware leak-site victims, each result with the URLs to cite and the credits it cost. A free key covers all of it.
Get itnpm: threatcluster-mcpSource on GitHubPyPI package: with the next release
The server is a thin wrapper over the REST API: every tool call is one or two GETs to threatcluster.io/api/public/v1 with your key in the X-API-Key header. It runs on your machine over stdio, sends nothing anywhere else and has no telemetry. It is published as one spec in two runtimes, npx -y threatcluster-mcp (Node 18+) and uvx threatcluster-mcp (Python 3.10+), and both are tested to produce identical output. Source: github.com/Jam0k/threatcluster-mcp.
Prerequisites
- A ThreatCluster account (Free works) and an API key from Settings → API. Free keys get 100 credits per day, 30 requests per minute, a 7-day lookback window and the five read scopes.
- Node 18+ (for
npx) or Python 3.10+ with uv (foruvx). If you already use thetcCLI and have runtc auth login, the Python build picks that credential up and needs no key of its own.
Setup: Claude Code
claude mcp add threatcluster -e THREATCLUSTER_API_KEY=tc_live_... -- npx -y threatcluster-mcp
Or the Python build: claude mcp add threatcluster -e THREATCLUSTER_API_KEY=tc_live_... -- uvx threatcluster-mcp. Then ask a question that needs live data, for example "an alert just came in mentioning CVE-2026-83549, what does ThreatCluster know and which clusters is it in"; Claude calls get_vulnerability and search_threats itself and cites the URLs it gets back.
Setup: Claude Desktop
Settings → Developer → Edit Config opens claude_desktop_config.json. Add the server and restart Claude Desktop:
{
"mcpServers": {
"threatcluster": {
"command": "npx",
"args": ["-y", "threatcluster-mcp"],
"env": { "THREATCLUSTER_API_KEY": "tc_live_..." }
}
}
}
Setup: Cursor
One click: install ThreatCluster in Cursor, then paste your key into the server's environment in Cursor Settings → MCP. Or add it by hand to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):
{
"mcpServers": {
"threatcluster": {
"command": "npx",
"args": ["-y", "threatcluster-mcp"],
"env": { "THREATCLUSTER_API_KEY": "tc_live_..." }
}
}
}
Setup: VS Code
Create .vscode/mcp.json in the workspace. The inputs entry makes VS Code prompt for the key and store it in its secret store rather than in the file:
{
"inputs": [
{ "type": "promptString", "id": "tc-key", "description": "ThreatCluster API key", "password": true }
],
"servers": {
"threatcluster": {
"type": "stdio",
"command": "npx",
"args": ["-y", "threatcluster-mcp"],
"env": { "THREATCLUSTER_API_KEY": "${input:tc-key}" }
}
}
}
Setup: Windsurf and Zed
Windsurf reads ~/.codeium/windsurf/mcp_config.json with the same mcpServers block as Claude Desktop. Zed takes a context_servers entry in settings.json:
{
"context_servers": {
"threatcluster": {
"command": { "path": "npx", "args": ["-y", "threatcluster-mcp"], "env": { "THREATCLUSTER_API_KEY": "tc_live_..." } }
}
}
}
To check a configuration without a client: THREATCLUSTER_API_KEY=... npx -y threatcluster-mcp --check prints where the key came from and where it will be sent, never the key itself.
Tools and what they cost
Costs are API credits (a free key has 100 a day); an empty result or an error is refunded, so a miss costs nothing. Every result carries cost, budget (remaining today, read from the response headers) and a url on every cluster, entity, victim and CVE so the model can cite what it used.
| Tool | Answers | API | Credits |
|---|---|---|---|
search_threats | keyword search over incident clusters; tries the phrase, then all words, then any word | GET /threats?keyword= | 1 per term searched, max 8 calls |
search_everything | clusters, entity profiles and dark-web hits in one call | GET /search | 5 |
newest_threats | what is new in 1h / 24h / 7d / 30d, by first report or by momentum | GET /threats | 1 |
get_threat | one cluster in full: summary, timeline, articles, entities; include_iocs adds validated indicators | GET /threats/{identifier}, GET /threats/{identifier}/iocs | 1 (+1 with include_iocs) |
leak_site_victims | leak-site listings by sector / group / country / victim, plus a tally of the whole window | GET /darkweb/ransomware/victims, GET /darkweb/ransomware/victims/facets | 2 |
lookup_entity | profile of an actor, malware family, tool, vendor, product, country, industry or CVE | GET /entities/search, GET /entities/{type}/{value} | 2 |
get_vulnerability | one CVE: CVSS, EPSS, KEV with due date, exploits, vendors, products | GET /vulnerabilities/{cve_id} | 1 |
exploited_vulnerabilities | CVEs in a window filtered to KEV / public exploit / severity / vendor / product | GET /vulnerabilities | 1 |
trending_entities | actors, malware, tools, vendors, CVEs, countries rising over a window | GET /entities/trending | 1 |
api_budget | credits and rate state from the last responses; no API call | — | 0 |
One prompt, threatcluster_analyst, carries the analyst rules: call tools first, say what period you searched, cite every fact with the returned URL, treat leak-site listings as claims. Errors come back as tool errors with the API's own message: a 401 tells the agent to set THREATCLUSTER_API_KEY and where a free key comes from, a 429 carries the Retry-After, a 403 names the missing scope or the lookback window and the plan that lifts it.
What a result looks like
search_threats with {"query": "Cleo Harmony", "days": 7, "limit": 3}, trimmed:
{
"tool": "search_threats",
"as_of": "2026-09-04T16:05:34Z",
"cost": 3,
"budget": {
"remaining": 97,
"limit": 100,
"resets_at": "2026-09-05T00:00:00Z"
},
"query": "Cleo Harmony",
"matched_stage": "any word",
"window_searched": "7d",
"api_calls": 3,
"count": 2,
"clusters": [
{
"short_id": "2532fa3d",
"title": "Critical Cleo Harmony Vulnerability Exploited for Privilege Escalation",
"date": "2026-09-02",
"threat_score": 72.9,
"urgency": "medium",
"summary": "A newly discovered authentication bypass vulnerability in Cleo Harmony, tracked as CVE-2026-84115, allows remote attacke…",
"entities": {
"ransomware_group": [
"Cl0p"
],
"cve": [
"CVE-2026-0768",
"CVE-2026-81578",
"CVE-2026-82078"
],
"platform": [
"Cleo Harmony",
"JFrog Artifactory",
"Langflow"
]
},
"url": "https://threatcluster.io/cluster/critical-cleo-harmony-vulnerability-exploited-2532fa3d"
},
"…"
]
}
Budgets for agents
Cheap reads cost 1 credit, search_everything costs 5 and search_threats costs 1 per term it tries (it stops as soon as three clusters match, and never makes more than eight calls). api_budget costs nothing and reports what the last responses said about the day's remaining credits, so an agent can pace itself before a burst of lookups. An exhausted budget returns a 429 with Retry-After; the budget refills at 00:00 UTC.
For unattended agents, mint a scoped tc_agent_ key rather than handing over a personal key; it supports scope downgrade and per-session request caps. Details at /cli.
Security
The key is read from THREATCLUSTER_API_KEY (or the tc CLI's credential store) and leaves the process in exactly one place: the X-API-Key header of requests to the API. It is never logged, never printed by --check, never written to disk, and is scrubbed from every error string; both packages' test suites assert that the key is absent from every byte written to stdout and stderr, including on a 401 whose body quotes it. All tools are read-only and validate their arguments before any request is made.
Prefer a bare function to a server? The agent-tool recipe is a 60-line threatcluster_lookup(query) for OpenAI or Claude function calling. Full endpoint reference: /api/public/v1/docs. Plans and credits: /pricing.