misp-mcp
Connects MISP to MCP clients, enabling threat indicator lookups and submissions in plain language.
README
<p align="center"> <picture> <source media="(prefers-color-scheme: light)" srcset="assets/banner-light.svg"> <img src="assets/banner-dark.svg" alt="MISP MCP - Malware Information Sharing Platform" width="820"> </picture> </p>
<p align="center"> Ask about a threat indicator in plain language and get an answer from MISP. </p>
<p align="center"> <img src="https://img.shields.io/badge/version-1.4.0-informational" alt="version 1.4.0"> <img src="https://img.shields.io/badge/license-Apache--2.0-lightgrey" alt="license Apache-2.0"> <img src="https://img.shields.io/badge/python-3.10%2B-informational" alt="Python 3.10+"> <img src="https://img.shields.io/badge/tests-65%20passing-lightgrey" alt="65 tests passing"> </p>
misp-mcp connects MISP to any MCP client (Claude Desktop, Claude Code, Cursor, and others). You ask in plain language, the client calls MISP, you get the answer. No MISP UI, no REST calls by hand. Nineteen tools read from MISP - indicators, events, feeds, the galaxy / taxonomy / tag knowledge base, warninglist checks, and worker/job health - and two add indicators (single + bulk), all under your own MISP key.
Connect to the hosted server
The server runs at
https://misp.example.com/mcp. There is nothing to install; point your MCP client at it with your own MISP key. You must be on the your corporate network or VPN (the endpoint is not on the public internet).
1. Get your MISP key (one time)
- Open
https://misp.example.comand log in. - Top-right menu → My Profile → Auth Keys → Add authentication key.
- Comment it
misp-mcp <your-name>, then copy the key - MISP shows it once. A read-only key is enough for lookups.
Keep the key private - every query runs as you.
2. Connect your MCP client
This is a standard remote MCP server over streamable HTTP, so any MCP-capable client works - Claude Desktop, Claude Code, Cursor, Windsurf, Cline, Continue, Zed, VS Code (Copilot/MCP), Goose, and others. Whatever the client, it needs the same three things:
- Transport: HTTP (streamable HTTP / remote MCP)
- URL:
https://misp.example.com/mcp - Two headers:
X-MISP-Key: YOUR_KEY_HEREandX-MISP-User: you@example.com
Generic config - most clients read a JSON block like this (the key names
vary slightly by client - mcpServers, servers, or mcp.servers; check your
client's docs, the values are identical):
{
"mcpServers": {
"misp": {
"type": "http",
"url": "https://misp.example.com/mcp",
"headers": {
"X-MISP-Key": "YOUR_KEY_HERE",
"X-MISP-User": "you@example.com"
}
}
}
}
<details> <summary><b>Per-client examples</b></summary>
Claude Code (one command in a terminal):
claude mcp add --transport http misp https://misp.example.com/mcp \
--scope user \
--header "X-MISP-Key: YOUR_KEY_HERE" \
--header "X-MISP-User: you@example.com"
Claude Desktop / Cursor / Windsurf - add the generic mcpServers block
above to the client's MCP JSON config, then fully restart the app.
VS Code (Copilot MCP) - in .vscode/mcp.json or user settings, under
"servers":
{
"servers": {
"misp": {
"type": "http",
"url": "https://misp.example.com/mcp",
"headers": {
"X-MISP-Key": "YOUR_KEY_HERE",
"X-MISP-User": "you@example.com"
}
}
}
}
Cline / Continue / Zed / Goose and other clients - use the same URL,
http transport, and the two headers, in whatever config format the client
uses. Anything that speaks remote MCP over HTTP and can send custom headers
will work; the two X-MISP-* headers are the only server-specific part.
Any client that cannot send custom HTTP headers is not supported (the key must
ride in X-MISP-Key).
</details>
3. Check it works
Reachability check (client-agnostic) - should print 401, which proves you can
reach the endpoint and that auth is required:
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://misp.example.com/mcp
If your client has a way to list MCP servers, confirm misp shows connected
(Claude Code: claude mcp list → misp ... ✓ Connected; other clients show it
in their MCP/tools panel).
Then ask your assistant these - if they answer from MISP, you're set:
| Ask this | You should get |
|---|---|
Is MISP healthy? |
reachable, MISP + server version |
Look up 102.130.113.9 in MISP. |
hits (Tor / DDoS), detection-flagged |
Look up 45.9.148.99 in MISP. |
"not seen in MISP" (not "safe") |
Triage these against MISP: 8.8.8.8, evil.com, <a hash> |
per-indicator verdicts |
Not working?
- 401 / timeout when asking → you're not on your network / VPN.
claude mcp listnot "Connected" → re-check the key and headers.- Tools missing, stale, or the assistant says "no such tool" → reconnect (below).
Reconnect / restart it
MCP clients cache the tool list when they connect. If the server stops
responding, shows the wrong tools, or was updated, the fix in every client is
the same: fully quit and reopen the app (not just the conversation or
window - an in-app /mcp reconnect or a new chat is often not enough). If tools
are still stale, remove the misp server from the config, save, reopen, add it
back, and reopen again.
New tools added on the server only appear after this full restart.
<details> <summary><b>Claude Code re-register</b></summary>
claude mcp remove misp
claude mcp add --transport http misp https://misp.example.com/mcp \
--scope user \
--header "X-MISP-Key: YOUR_KEY_HERE" \
--header "X-MISP-User: you@example.com"
Then quit Claude Code completely and reopen it. Check with claude mcp list →
misp ... ✓ Connected, and /mcp shows the current tools.
</details>
What you can ask
"Look up 102.130.113.9 in MISP."
"Triage these 30 IOCs from the report."
"What else showed up in the same event as evil[.]com?"
"Review the last 30 days of IOC submissions - who added what."
"Is MISP healthy? How many feeds are on?"
Behind the scenes the client calls a tool and gets structured JSON, e.g. for a lookup:
{
"ioc": "102.130.113.9",
"ioc_type": "ipv4",
"total_hits": 6,
"summary": {
"seen_in_misp": true,
"event_count": 6,
"detection_flagged": true,
"max_threat_level": "Medium",
"restricted_hits": 0
},
"hits": [
{ "event_id": "16989", "event_info": "Tor exit nodes feed",
"attribute_type": "ip-dst", "value": "102.130.113.9",
"to_ids": true, "restricted": false }
]
}
Tools
| Tool | What it does | |
|---|---|---|
misp_lookup_ioc |
read | Sightings of one IPv4/IPv6, domain, URL, or hash, with a verdict |
misp_lookup_iocs |
read | Triage many indicators in one call |
misp_correlate_ioc |
read | Other indicators in the same event, for pivoting |
misp_get_event |
read | One event: info, tags, attributes |
misp_search_events |
read | Search events by title, tag, or date |
misp_feed_stats |
read | How many feeds exist and which are on |
misp_instance_status |
read | Reachability + auth check; run first when a tool fails |
misp_review_submissions |
read | Audit recent submissions: what was added, by whom, what's detection-flagged |
misp_lookup_galaxy |
read | Threat actors, malware, tools, ATT&CK techniques by name or synonym |
misp_list_galaxies |
read | Galaxy types available on the instance |
misp_list_taxonomies |
read | Taxonomies (TLP, kill-chain, PAP) and whether each is enabled |
misp_get_taxonomy |
read | One taxonomy's tags and their meanings |
misp_search_tags |
read | Find tag definitions by name |
misp_get_object |
read | One MISP object (grouped attributes, e.g. a file object) |
misp_get_attribute |
read | One attribute by id, with its event |
misp_search_attributes |
read | Search attributes by type, category, tag, to_ids, or event (paginated) |
misp_check_warninglist |
read | Flag IOCs that hit known-good / noise lists (false-positive control) |
misp_worker_status |
read | Background worker / queue health (admin key) |
misp_jobs |
read | Recent background jobs + failures and why (admin key) |
misp_submit_ioc |
write | Add a new indicator (needs a write-capable key) |
misp_submit_iocs |
write | Bulk: validate + add many indicators (dry-run preview first) |
Paste indicators however you have them - defanged forms (1.2.3[.]4,
hxxp://evil[.]com) are cleaned up automatically. Private/reserved IPs are
rejected (not routable, can't identify an external threat).
Security
- Your key is the authorization. MISP checks it on every call and attributes the action to you. A read-only key cannot write; only write-capable keys (the security team) can add indicators.
- Guarded write path. Submissions are rate-limited, well-known / first-party
infrastructure can never be submitted, and the submitter is read from MISP
itself - not a value the caller sets (so
misp_review_submissionsshows who actually added each IOC). - MISP content is data, not instructions. Don't submit an indicator from a lookup without checking it yourself.
- Keys stay private. No shared key on the server; the key rides in a header over TLS. Logs never contain keys or IOC values.
Architecture
The shape is the same on any host: callers reach misp-mcp over HTTPS through a
TLS-terminating load balancer (or the process's own cert), and misp-mcp calls
your MISP. It holds no credential of its own - every request carries the
caller's own X-MISP-Key, which MISP validates and attributes to that user.
MCP client (any client, on an allowed network / VPN)
│
│ HTTPS + header X-MISP-Key: <your key>
▼
Load balancer TLS termination · ingress limited to allowed CIDRs
│ HTTP :8080 (private)
▼
misp-mcp container / VM, no stored secret
│ HTTPS
▼
your MISP instance
- TLS terminates at the load balancer; misp-mcp serves plain HTTP on
:8080behind it (or give the process its own cert, see DEPLOY.md). - Ingress is scoped to your caller networks; the endpoint is not public.
- Two common topologies: run misp-mcp standalone in front of a separate
MISP (the Terraform module below), or co-locate it beside an existing
MISP behind one load balancer with a
/mcp*path rule so MISP is untouched.
Host it (local, self-host, or cloud)
- Docker (fastest): run the published image against your own MISP.
Bind stays on localhost; front it with your own TLS proxy for remote use (the key is a bearer credential).docker run -d -p 127.0.0.1:8080:8080 \ -e MCP_TRANSPORT=http -e MCP_HOST=0.0.0.0 \ -e MISP_URL=https://misp.example.com \ -e MISP_MCP_ALLOW_INSECURE_BIND=true \ ghcr.io/indranilroy99/misp-mcp:latest # or: MISP_URL=https://misp.example.com docker compose up -d - Local (in your MCP client, against your own MISP): install and add the
misp-mcpbinary - see ONBOARDING.md. - Self-host for a team (HTTP server behind TLS, EC2/VM + systemd): DEPLOY.md.
- Cloud, any provider (AWS / GCP / Azure guidance): CLOUD.md.
- AWS, Terraform - ready modules behind an internal ALB with TLS, in two
flavors sharing one networking module: Fargate (serverless, no VM) or
EC2 (managed VM, SSM access): deploy/terraform/. Fill
in a
.tfvars,terraform apply, and it prints the/mcpendpoint.
Automated local setup (from source):
git clone https://github.com/indranilroy99/misp-mcp.git
cd misp-mcp
./install.sh
<details> <summary><b>All settings</b></summary>
| Setting | Mode | Default | Meaning |
|---|---|---|---|
MISP_URL |
both | required | MISP base URL |
MISP_API_KEY |
local | required | your key (local mode) |
MCP_TRANSPORT |
both | stdio |
stdio for local, http for hosted |
MCP_HOST |
hosted | 127.0.0.1 |
bind address |
MCP_PORT |
hosted | 8080 |
port |
MISP_VERIFY_TLS |
both | true |
set false only for a self-signed lab |
MISP_MCP_SHOW_RESTRICTED |
both | true |
false turns on server-side TLP hiding |
MISP_SUBMISSION_EVENT_ID |
both | required for writes | event that misp_submit_ioc writes to |
MISP_MCP_PROTECTED_DOMAINS |
both | empty | your own domains that can never be submitted |
MISP_MCP_SUBMIT_RATE |
both | 20 |
max submissions per key per minute |
MISP_MCP_TLS_CERT / MISP_MCP_TLS_KEY |
hosted | none | serve HTTPS directly |
MISP_MCP_ALLOW_INSECURE_BIND |
hosted | false |
allow a public plain-HTTP bind (TLS on a proxy) |
</details>
<details> <summary><b>Development</b></summary>
python3 -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q # 65 tests
misp_mcp/
server.py the 21 tools and the MCP server
client.py talks to the MISP REST API (read + write)
config.py reads settings from the environment
http_app.py hosted mode: header auth + web server
context.py carries your identity through one request
validators.py cleans, checks, and safelists indicators
Dependencies are pinned in pyproject.toml: mcp, httpx, pydantic,
uvicorn, starlette.
</details>
<details> <summary><b>Security reporting & license</b></summary>
Report vulnerabilities privately - see SECURITY.md. Licensed under Apache-2.0 (LICENSE). Contributions welcome - see CONTRIBUTING.md.
</details>
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。