The Threadlinqs Intelligence MCP server is now at v8.1.0. The headline is structural rather than cosmetic: the intelthreadlinqs-mcp npm package has been rewritten as a thin stdio-to-HTTP proxy over the platform's /mcp endpoint, which makes the worker's tool registry the single source of truth for every surface. Both transports now expose the same 73 tools with the same argument names, the rewrite closed a real access-control gap, and v8.1 layers on 25 analyst prompts covering all 73 tools, an offline catalog snapshot, and a downloadable agent skill that teaches any MCP client how to use the catalog properly on the first call instead of the fifth.
TL;DR — v8.1.0 makes the worker's/mcpregistry the only catalog. The npm server went from 1,798 lines to 436 and now proxies every call instead of re-implementing it, so the 31-tool drift between the two surfaces is gone and same-named tools no longer disagree on arguments. It also closed a gap where roughly twenty stdio tools reached tier-0 REST endpoints without a key. v8.1 adds 25 prompts spanning all 73 tools, a compiled catalog snapshot sotools/listworks with no network, and an agent skill at/mcp/skill.md. Tool calls remain Purple/Gold only (tier ≥ 3); the unauthenticated catalog twins areGET /mcp/catalog.jsonandGET /mcp.md.
What's New in v8.1
v8.0 was the rewrite. v8.1 is what the rewrite made possible — three things that were impractical while two catalogs had to be kept in step by hand:
- 25 analyst prompts, covering all 73 tools. Every tool now appears in at least one prompt, so a client that starts from a prompt never has to guess a tool name. The set is published over
prompts/list— ask your client for the current list rather than trusting a count in a blog post. - A bundled catalog snapshot. The package now compiles a generated snapshot of the catalog into its own build. Introspection therefore works with no network at all. This fixed a concrete failure: a sandboxed scanner with no egress used to see an empty tool list and conclude the server exposed nothing.
- A downloadable agent skill. One instruction file that routes a client to the right tool on the first call, teaches the
huntgrammar, and encodes the tier gate and the data-honesty rules. Details below.
The documentation page was rebuilt at the same time and now renders from the live registry, so the /mcp page, the JSON catalog and the Markdown twin can no longer disagree with the server about what exists.
The Proxy Rewrite: One Registry, One Source of Truth
Until v8, Threadlinqs shipped two MCP implementations. The remote endpoint at https://intel.threadlinqs.com/mcp was generated from a registry inside the worker. The npm stdio package carried its own hand-maintained tool definitions and called the platform's REST API directly. Two catalogs, two release cadences, one inevitable outcome.
They drifted by 31 tools. Worse than the missing tools were the ones that existed on both sides and disagreed: search_iocs took value remotely and query over stdio, so a prompt that worked in a chat client failed silently in a terminal agent — the argument was ignored, the search ran unfiltered, and the model reported whatever came back as if it were the answer.
v8 deleted the second implementation. src/index.ts went from 1,798 lines to 436: it now speaks stdio to the client, forwards tools/list, tools/call, prompts/* and resources/* to /mcp over HTTP with the user's key, and returns the response. There is nothing left in the package that can disagree with the server, because the package no longer knows anything the server did not tell it.
The rewrite also closed an access-control gap that was invisible from the outside. Because the old stdio server called tier-0 REST endpoints directly, roughly twenty tools returned real data with no valid key — the tier gate lived on /mcp, and those calls never went through it. Every call now routes through /mcp, where the gate is enforced once, in one place.
Two JSON-RPC error codes carry that gate, and both are terminal:
| Code | Meaning | What a client should do |
|---|---|---|
-32001 | Missing or invalid API key | Stop. Say the key is missing or invalid and point at the profile page. Do not retry other tools. |
-32002 | Authenticated, but below Purple | Stop. Surface the upgrade path. No other tool will succeed either. |
The failure mode worth naming: an agent that treats -32002 as a per-tool problem will walk the whole catalog, collect 73 identical denials, and burn a context window proving one fact it knew after the first call.
25 Analyst Playbooks
MCP prompts are parameterized workflows a client can offer directly — the user picks one, fills in an argument, and the prompt supplies the tool sequence. v8.1 ships a set that covers every tool in the catalog, so no tool is reachable only by guesswork.
They span the same ground an analyst does: triaging a CVE, profiling an actor, hunting an indicator, mapping detections to MITRE ATT&CK, reading the day's brief, assessing exposure, running a corpus-wide aggregate, explaining why two entities are linked, mapping a campaign, reviewing detection gaps, writing a rule, checking an attribution, predicting a next move, building a malware dossier, pivoting infrastructure, sweeping OSINT, exporting for tooling, planning a purple-team exercise, sweeping vulnerabilities, reviewing a period, checking platform status, orienting from cold, answering an open research question, and bulk-enriching a list.
Ask the server for the authoritative set rather than copying a list:
{ "jsonrpc": "2.0", "id": 1, "method": "prompts/list" }json
Prompts are a routing aid, not a replacement for the tools. A prompt that ends in a chain of five single-purpose calls is still the wrong shape — which is what the composite tools exist to prevent.
The 73 Tools
The catalog covers the whole intelligence lifecycle over a live corpus: 1,787 threats, 16,522 detections, 47,863 IOCs and 533 threat actors at the time of writing (the nightly ingest moves those numbers). Thirteen lanes, every tool in exactly one:
| Lane | Count | Tools |
|---|---|---|
| Orient | 7 | get_started, health, get_platform_stats, get_engine_status, get_enrichment_overview, get_changelog, get_roadmap |
| Find | 7 | search_threats, search_corpus_semantic, hunt, hunt_schema, resolve_entity, list_threat_categories, get_recent_threats |
| Threat | 6 | get_threat, get_threat_enrichment, get_threat_bundle, get_threat_hunting_bundle, bulk_get_threats, get_threat_transcripts |
| Actor | 5 | get_actor, search_actors, get_actor_intelligence, get_attribution_evidence, get_attribution_coverage |
| IOC / Infra | 8 | search_iocs, get_ioc_intelligence, get_ioc_dns, get_ioc_blast_radius, get_infrastructure_pivots, get_c2, get_c2_dns_intel, generate_c2_blocklist |
| Vuln | 5 | search_vulnerabilities, get_cve, get_cve_intelligence, bulk_get_cves, get_cwe |
| Detection | 7 | get_detections, search_detections, get_detection_detail, export_detection, get_threat_simulations, list_simulations, get_mitre_gap_analysis |
| MITRE | 4 | get_mitre_coverage, get_mitre_technique, predict_mitre_transitions, get_technique_rules |
| Graph | 8 | get_similar_threats, explain_correlation, get_correlation_path, get_entity_profile, get_correlation_subgraph, get_pivotal_entities, get_graph_campaigns, get_correlations |
| Pivots | 3 | get_malware_intelligence, get_tool_intelligence, get_campaign_intelligence |
| OSINT | 4 | get_osint, search_xscan_indicators, get_osint_trends, get_community_campaigns |
| Reporting | 7 | get_daily_intel_bundle, get_latest_debrief, get_debrief, list_debriefs, get_landscape_briefing, get_daily_theme, get_threat_level |
| Export | 2 | export_stix, export_attack_navigator |
Hosts prefix tool names differently — Claude Code exposes mcp__threadlinqs-intel__get_threat, other clients expose get_threat. This post writes bare names throughout; match by suffix.
Three limits worth knowing before the first call
Responses are capped at 90,000 characters. A truncated payload ends with the literal marker ... [truncated: response exceeded 90000 chars — narrow your query. That string is an instruction, not an error — add filters, lower limit, or move to a more specific tool. Re-issuing the same call gets the same truncation.
Paging is honored by four tools only — search_threats, get_recent_threats, get_detections and search_xscan_indicators. search_iocs, list_debriefs and search_actors do not page; narrow the query instead of walking offsets that will not move.
hunt_schema comes before hunt, once per session. It returns the field vocabulary, the operators and the stats-pipe form. Guessing field names produces empty results, and an empty result looks exactly like a real "nothing found".
Composite Tools: One Call Instead of Five
Most of the wasted work we see in agent traces is the same pattern: a model reconstructs, call by call, something the server already composes in one round trip. Each hop costs latency, tokens, and a chance to lose the thread. The composite tools exist specifically to collapse those chains:
| The chain agents reach for | Use instead |
|---|---|
get_threat → get_similar_threats → get_threat_simulations → get_infrastructure_pivots | get_threat_hunting_bundle |
search_iocs → N × get_threat → N × get_actor | get_ioc_intelligence |
get_cve + exploitation velocity + linked detections | get_cve_intelligence |
get_threat looped over a list of known IDs | bulk_get_threats (up to 20 per call) |
N × search_threats, then counting the rows yourself | hunt with a | stats count by pipe |
| debrief + stats + recent threats + correlations overview | get_daily_intel_bundle |
The aggregate case is the sharpest. "How many threats using Cobalt Strike hit healthcare, broken down by nation-state?" is not a search problem; it is one query against the pre-joined observation index:
{
"name": "hunt",
"arguments": {
"query": "tool = \"cobalt strike\" AND sector = \"healthcare\" | stats count by nation"
}
}json
Two more habits worth breaking. Call list_threat_categories before filtering on a category rather than guessing an enum — a wrong value returns an empty set, not an error. And run resolve_entity before pivoting on an alias: fancy bear and APT28 are the same actor, but only one of them is the canonical key the graph is indexed on. On the graph side, start get_correlation_subgraph at depth 1 and expand deliberately; depth 3 on a well-connected seed exceeds every response budget you have.
The Agent Skill
A tool catalog tells a model what exists. It does not tell it which of 73 options answers the question in front of it, and a model that picks wrong usually picks plausibly — get_threat four times instead of one bundle, search_threats in a loop instead of a hunt aggregate.
So v8.1 ships an agent skill: a portable instruction file any MCP-capable client can load before its first call.
- Packaged skill: intel.threadlinqs.com/skills/intelthreadlinqs-mcp-skill.zip
- Readable source: intel.threadlinqs.com/mcp/skill.md
- Also in the repository: github.com/threadlinqs-cmd/intelthreadlinqs-mcp
What it actually encodes:
- A routing table. Question shape in, tool out — so the right call happens first, not after three wrong ones. It names the composite tools explicitly and lists the chains they replace.
- The hunt grammar. Fields, operators, the stats-pipe form, and the rule that
hunt_schemais called once before the firsthunt. - The tier gate. That tool calls need tier ≥ 3, that
tools/listis gated while/mcp/catalog.jsonis not, and that-32001and-32002are terminal. - The response cap and paging map. How to recognize the truncation marker and which four tools actually page.
- The honesty rules — the part we care most about.
The honesty rules
Threat intelligence is where a confident agent does the most damage. These constraints are in the skill because the underlying data is genuinely qualified, and a model that flattens the qualifiers produces something that reads like an assessment and is not one:
get_attribution_evidencedistinguishes a researched assessment from a pending intake stub, and reports which it is in the payload. A stub means the machine intake has queued the threat for research — it is not attribution and must never be presented as one.get_pivotal_entitiesbetweenness is an ego-bridge heuristic, not exact Brandes centrality. Its own response says so. Report it as a ranking hint, not a graph-theoretic fact.- OSINT is community-sourced.
get_osint,search_xscan_indicatorsandget_osint_trendscorroborate; they never adjudicate. Community lead time is a signal about coverage, not a verdict about an indicator. get_community_campaignslabels are upstream AI output. Useful for clustering, not citable as analyst-authored.get_ioc_dnsis stored enrichment, never a live lookup. If a question depends on what resolves right now, say so — this tool answers what was observed, not what is true this second.
Two Transports, One Catalog
The choice between transports is now purely about where your agent runs. Since v8 the catalog is not merely similar across the two — it is the same registry, served twice.
| Transport | Endpoint / package | Auth | Best for |
|---|---|---|---|
| Remote (Streamable-HTTP) | POST https://intel.threadlinqs.com/mcp | OAuth 2.1 (PKCE S256) | Claude Desktop, Claude.ai custom connectors |
| Local (stdio proxy) | npx -y intelthreadlinqs-mcp | Bearer tl_ key via THREADLINQS_API_KEY | CLI agents, IDE-embedded MCP clients |
Both speak JSON-RPC 2.0 and negotiate protocol version 2025-11-25, with older revisions still accepted. Alongside the tools, the server publishes resources and resource templates — threadlinqs://threat/{id}, threadlinqs://cve/{id} and threadlinqs://actor/{name} — so a client can address an entity by URI instead of a tool call.
One asymmetry to plan for: MCP tools/list is tier-gated like every other method. Its unauthenticated twins are the HTTP catalog endpoints, which is how registries and scanners index the server without an account:
curl -s https://intel.threadlinqs.com/mcp/catalog.json | jq '.tools | length'
# 73
curl -s https://intel.threadlinqs.com/mcp.md | head -40bash
Those two are the ground truth for tool names, arguments and enum values. Anything else — a cached README, a third-party directory listing, a model's memory of an older version — is a copy, and copies drift. That is the lesson v8 was built around.
A Purple-Tier Feature
Tool calls require Purple or Gold (tier ≥ 3). There is no anonymous access on either transport: the remote endpoint gates every session behind OAuth 2.1, and the local package needs a valid Purple or Gold tl_ key. Even get_started, which is cheap because it answers from the registry with no database round-trip, is gated.
What is public is the shape of the server: /mcp/catalog.json, /mcp.md and the documentation page. You can read every tool, argument and annotation before paying for anything; what a key buys is the data behind them.
Purple is $11.99/mo, Gold is enterprise. Full breakdown on the pricing page.
How to Connect
Remote — Claude Desktop and Claude.ai
- Open connector settings and choose Add custom connector.
- Enter the endpoint:
https://intel.threadlinqs.com/mcp - Complete the OAuth 2.1 sign-in (authorization code, PKCE S256) with a Purple or Gold account and approve access.
- The 73 tools and the prompt set appear in the client. No API key to manage.
POST https://intel.threadlinqs.com/mcp
Content-Type: application/json
Accept: application/json, text/event-stream
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }http
Local — CLI agents and IDE clients
Generate a key from Profile → API Key on the platform (Purple or Gold account required), then add the package to your MCP client config:
{
"mcpServers": {
"threadlinqs-intel": {
"command": "npx",
"args": ["-y", "intelthreadlinqs-mcp"],
"env": {
"THREADLINQS_API_KEY": "tl_your_key"
}
}
}
}json
Restart the client and the same 73 tools are there — proxied, not re-implemented. To sanity-check the package before wiring it in:
npx -y intelthreadlinqs-mcp --version
THREADLINQS_API_KEY=tl_your_key npx -y intelthreadlinqs-mcpbash
Why This Matters
A tool catalog is an interface contract with a model. When the contract is wrong the model does not error — it improvises, and improvisation in threat intelligence looks like an answer. The value-versus-query split in search_iocs never threw an exception; it just quietly returned an unfiltered indicator search that a model then summarized as if it had been asked about one indicator. That is the class of bug worth structural work to eliminate, and the only durable fix is to stop maintaining a second copy of the truth.
The same reasoning drives the rest of v8.1. Prompts exist so a client does not have to guess which of 73 tools starts a workflow. The snapshot exists so an offline scanner sees the real catalog instead of an empty one. The skill exists because knowing that get_threat_hunting_bundle is available is different from knowing to reach for it instead of four separate calls — and because an agent reporting a pending intake stub as an attribution is worse than an agent that returns nothing.
Intelligence is only useful at the speed of decision. v8.1 is aimed at the part of that loop nobody sees: the agent picking the right call, once, with the qualifiers intact.
The Threadlinqs Intelligence MCP server is available to Purple and Gold subscribers. Browse the catalog without an account at /mcp/catalog.json, read the docs at intel.threadlinqs.com/mcp, load the agent skill, or read the source at github.com/threadlinqs-cmd/intelthreadlinqs-mcp. Questions or enterprise inquiries: contact@threadlinqs.com.