This guide compares OpenCTI and MISP, the two free threat intelligence platforms most teams meet first, and then runs both on one laptop. We load the same small sample into each and let an AI agent query both through MCP. The agent can also write one thing, a MISP sighting, but only after a person allows it.
It is Part 3 of Agentic Threat Intelligence 101. Part 2 built an alert triage agent with the Claude Agent SDK. Every command below was run on 25 October 2026 with MISP 2.5.50, OpenCTI 7.261023.1, PyMISP 2.5.34.1, pycti 7.261023.1, FastMCP 4.1.3 and Claude Code 2.1.302.
Plan for about 10 GB of free disk and about 6 GB of free memory. The images alone came to 9.1 GB (MISP 4.83 GB, OpenCTI 4.30 GB), and the two stacks used 5.0 GiB of memory at idle. You also need Docker, git, uv and a logged-in Claude Code. The lab override in Step 2 switches off Elasticsearch’s disk checks; that is for this lab only, never for production.
OpenCTI and MISP Side by Side
Read the table first. The rows marked as measured come from our own run on an Apple Silicon laptop. The rest come from each project’s README and licence file.
| Topic | MISP | OpenCTI |
|---|---|---|
| Core idea | Collect, store and share structured indicators between teams (MISP README) | Store threat knowledge and observables as linked entities with their sources (OpenCTI README) |
| Data model | Events that hold attributes and objects. Our test: 2 events, 5 vulnerability attributes | Knowledge schema based on STIX 2. Our test: 4 vulnerability entities and 1 report |
| STIX 2.1 | Exports it. Our event came out as a bundle with grouping, vulnerability and sighting objects | Schema based on the STIX 2 standards (OpenCTI README) |
| API | REST API, PyMISP client | GraphQL API, pycti client |
| Services we started | 6 containers: MariaDB, Valkey, MISP core, modules, nginx, mail | 6 of 21 services: Elasticsearch, Redis, RabbitMQ, S3 store, platform, 1 worker |
| Idle memory (measured) | 1.65 GiB for the stack | 3.39 GiB, with the Elasticsearch heap set to 2 GB |
| Data on disk after first start (measured) | MariaDB volume 1.56 GB, measured at first start with the sample data | Elasticsearch volume 10 MB, measured at first start with the sample data (not a real storage difference) |
| Images downloaded (measured) | 4.83 GB | 4.30 GB for the core services, no connectors |
| Licence | AGPL-3.0 | Community Edition Apache 2.0; Enterprise Edition has its own licence |
| Agent tools we found | MISP project’s misp-mcp: read-only, 14 tools, AGPL-3.0 | Filigran’s xtm-mcp: GraphQL, 9 tools, queries only per its README, Apache 2.0, archived on GitHub; Filigran now documents an MCP server inside XTM One (Community Edition, can also write; not tested here) |
How to Read This Table
MISP describes itself as a platform for collecting, storing, distributing and sharing indicators. It fits teams that exchange events with partners and feed detection tools. OpenCTI describes itself as a way to structure threat knowledge and link each piece of information to its source. It fits analysts who work with relationships between actors, tools and reports.
The two are not rivals by design. The OpenCTI README lists MISP as an integration it can connect with, and many teams run both. Our resource rows show the cost of that choice: OpenCTI needed about twice the memory of MISP at idle, mostly for Elasticsearch.
Three cautions apply. Idle memory is not load memory. We set the Elasticsearch heap to 2 GB, while the official sample file sets 4 GB. And our data was tiny: a real instance with feeds will grow far beyond these numbers.
For the background terms, see our glossary entries on STIX and TAXII and on what a TIP is, and the longer Threat Intelligence Platforms 101 guide.
How the Agent Reaches Both Platforms
An AI agent cannot talk to a platform directly. It calls tools, and an MCP server turns each tool call into an API request. We used three servers, as the diagram shows. If you have not built one yet, Part 1 walks through it.
- 1Claude Code MCP serversone search tool per platform
- 2MCP servers MISPPyMISP search over the REST API
- 3MCP servers OpenCTIGraphQL query for vulnerabilities
- 4MCP servers Claude CodeJSON from both platforms
- misp-mcp is the MISP project’s own server (github.com/MISP/misp-mcp). It is read-only and builds on PyMISP. Its last commit was on 5 April 2026. Its README says
pip install misp-mcp, but the name is not on PyPI (we got a 404), so we installed it from source. - xtm-mcp is Filigran’s GraphQL server for OpenCTI (github.com/FiligranHQ/xtm-mcp). It lets the agent read the schema and run queries. Its README says it focuses on read queries, and GitHub marks the repository as archived, with a last commit on 7 June 2026. It still ran against OpenCTI 7.261023.1.
- intel-write is ours, about 40 lines of FastMCP, saved as
write_server.pyand shown in full in Step 6. It has one tool that writes a MISP sighting. Both READMEs describe query access only, so for MISP a write path has to be your own code.
One caution. Filigran now documents a newer route: once OpenCTI is registered with XTM One, XTM One exposes an MCP server for it (Community Edition, at /mcp/opencti on your XTM One host). It can also create and update data, with the calling user’s permissions.
We used xtm-mcp because it runs without XTM One and only reads. We did not run the XTM One server. For a new setup, prefer it and connect it as a read-only OpenCTI user.
Step 1: Start MISP in Docker
Work inside the lab folder ($LAB) and clone the official MISP Docker repository into misp/src, so the environment file ends up at misp/src/.env. Copy the template, then set the values below in that file. The key and password are throwaway lab values that we generated with openssl rand; here they are placeholders. We used http:// because the container refuses an https:// base URL when no certificate is present.
mkdir -p misp
git clone --depth 1 https://github.com/MISP/misp-docker misp/src
cd misp/src
cp template.env .env
[email protected]
ADMIN_KEY=<MISP_API_KEY>
ADMIN_PASSWORD=<MISP_ADMIN_PASSWORD>
BASE_URL=http://localhost:8481
NGINX_HTTP_PORT=8481
NGINX_HTTPS_PORT=8444
COMPOSE_PROJECT_NAME=misplab
docker compose up -d
curl -s -H "Authorization: <MISP_API_KEY>" -H "Accept: application/json" \
http://localhost:8481/servers/getVersion
The second command returns "version": "2.5.50" once MISP is ready. The first start takes a couple of minutes because MISP loads its warning lists, taxonomies and galaxies.
Step 2: Start OpenCTI in Docker
Do the same with the official OpenCTI Docker repository, cloned into opencti/src, so its environment file is opencti/src/.env (start from $LAB again). Its compose file defines 21 services, including ten connectors and a companion service, XTM One. OpenCTI itself needs only six, and we ran one worker instead of the default three.
mkdir -p opencti
git clone --depth 1 https://github.com/OpenCTI-Platform/docker opencti/src
cd opencti/src
cp .env.sample .env
COMPOSE_PROJECT_NAME=octilab
OPENCTI_PORT=8480
[email protected]
OPENCTI_ADMIN_PASSWORD=<OPENCTI_ADMIN_PASSWORD>
OPENCTI_ADMIN_TOKEN=<OPENCTI_TOKEN>
ELASTIC_MEMORY_SIZE=2G
MINIO_ROOT_PASSWORD=<S3_PASSWORD>
RABBITMQ_DEFAULT_PASS=<RABBITMQ_PASSWORD>
Our machine already used host port 9000, so we added a small override file. It also switches off Elasticsearch’s disk checks, because our Docker disk was above 95 percent full, a level where Elasticsearch normally stops placing shards (see the problems section). Do not copy that second setting to a real server.
# Lab override.
# 1) Free host port 9000 (used by another local project); S3 stays internal.
# 2) The Docker disk of this machine is above 95% full, so Elasticsearch's disk
# watermarks would block shard allocation. Switch them off for the lab only.
services:
minio:
ports: !reset []
elasticsearch:
environment:
- discovery.type=single-node
- xpack.ml.enabled=false
- xpack.security.enabled=false
- thread_pool.search.queue_size=5000
- logger.org.elasticsearch.discovery="ERROR"
- cluster.routing.allocation.disk.threshold_enabled=false
- "ES_JAVA_OPTS=-Xms${ELASTIC_MEMORY_SIZE} -Xmx${ELASTIC_MEMORY_SIZE}"
docker compose -f docker-compose.yml -f docker-compose.lab.yml \
up -d --scale worker=1 worker
The worker depends on the platform, which depends on Redis, Elasticsearch, the S3 store and RabbitMQ. So naming the worker starts exactly those six.
On pulled images with fresh volumes, the platform reported healthy 51 seconds after this command. Check the API with a GraphQL call; the answer shows the platform version, 7.261023.1.
Step 3: Load the Same Sample Into Both
First download the catalog into the lab folder, next to the script (it reads kev.json from there):
curl -sL -o "$LAB/kev.json" \
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
We loaded real public data: six entries from the CISA Known Exploited Vulnerabilities catalog (catalog version 2026.10.23). The split is deliberate. Three CVEs go to both platforms, two only to MISP and one only to OpenCTI, so a cross-platform question has a real answer. See our entry on the KEV catalog for what the list means.
"""Load a small public sample into MISP and OpenCTI: a few CISA KEV entries.
Reads kev.json (downloaded from the CISA KEV feed). Needs MISP_URL, MISP_API_KEY, OPENCTI_URL, OPENCTI_TOKEN."""
import json
import os
import pathlib
from pycti import OpenCTIApiClient
from pymisp import MISPEvent, PyMISP
HERE = pathlib.Path(__file__).parent
kev = {v["cveID"]: v for v in json.loads((HERE / "kev.json").read_text())["vulnerabilities"]}
MISP_EVENTS = {
"KEV lab sample A: edge devices and Java logging": ["CVE-2024-3400", "CVE-2023-4966", "CVE-2021-44228"],
"KEV lab sample B: file transfer and remote access": ["CVE-2023-34362", "CVE-2024-1709"],
}
OCTI_CVES = ["CVE-2024-3400", "CVE-2023-4966", "CVE-2023-34362", "CVE-2023-22515"]
def note(cve: str) -> str:
v = kev[cve]
return f"{v['vendorProject']} {v['product']}; CISA KEV added {v['dateAdded']}; ransomware use: {v['knownRansomwareCampaignUse']}"
misp = PyMISP(os.environ["MISP_URL"], os.environ["MISP_API_KEY"], ssl=False)
for info, cves in MISP_EVENTS.items():
ev = MISPEvent()
ev.info = info
ev.distribution = 0
ev.threat_level_id = 2
ev.analysis = 2
ev.add_tag("tlp:clear")
ev.add_tag("cisa-kev")
for cve in cves:
a = ev.add_attribute("vulnerability", cve, comment=note(cve), to_ids=False)
res = misp.add_event(ev, pythonify=True)
print("MISP event", res.id, res.info, len(res.attributes), "attributes")
octi = OpenCTIApiClient(os.environ["OPENCTI_URL"], os.environ["OPENCTI_TOKEN"])
label = octi.label.create(value="cisa-kev", color="#d32f2f")
ids = []
for cve in OCTI_CVES:
v = kev[cve]
obj = octi.vulnerability.create(
name=cve,
description=f"{v['vulnerabilityName']}. {v['shortDescription']}",
labels=["cisa-kev"],
)
ids.append(obj["id"])
print("OpenCTI vulnerability", cve, obj["id"])
rep = octi.report.create(
name="KEV lab sample (OpenCTI side)",
description="Four CISA KEV entries loaded for the Part 3 lab.",
published="2026-10-25T00:00:00Z",
report_types=["internal-report"],
objects=ids,
labels=["cisa-kev"],
)
print("OpenCTI report", rep["id"])
MISP gets two events with vulnerability attributes and the tags tlp:clear and cisa-kev. OpenCTI gets four vulnerability entities, a label and one report that holds them. The script prints the new ids. Run it with the third environment from Step 4 (venv-writer/bin/python load_data.py) once both platforms are up.
Step 4: Install and Connect the MCP Servers
First build the environments, all on Python 3.12. We cloned misp-mcp and built it from source with an editable install, because the package is not on PyPI. We cloned xtm-mcp and installed its pinned requirements; it runs straight from the clone.
A third environment holds FastMCP, PyMISP and pycti for our write server and the loader script from Step 3, pinned to the versions the lab used (FastMCP 4.1.3, PyMISP 2.5.34.1, pycti 7.261023.1). $LAB is the lab folder.
# Build the three Python environments of the lab (uv 0.11.8, Python 3.12).
# LAB is the lab folder. Run once more from scratch on 25 Oct 2026 (exit 0).
cd "$LAB"
# 1. misp-mcp: clone, build from source (editable install), pin the MCP SDK to 1.x
git clone https://github.com/MISP/misp-mcp mcp-src/misp-mcp
git -C mcp-src/misp-mcp checkout 3d03382
uv venv --python 3.12 venv-misp
uv pip install --python venv-misp/bin/python -e mcp-src/misp-mcp "mcp[cli]==1.26.0"
# 2. xtm-mcp: clone and install its pinned requirements (it is run from the clone, no install step)
git clone https://github.com/FiligranHQ/xtm-mcp mcp-src/xtm-mcp
git -C mcp-src/xtm-mcp checkout 456c188
uv venv --python 3.12 venv-octi
uv pip install --python venv-octi/bin/python -r mcp-src/xtm-mcp/requirements.txt
# 3. intel-write and the loader script: FastMCP plus the two platform clients
uv venv --python 3.12 venv-writer
uv pip install --python venv-writer/bin/python fastmcp==4.1.3 pymisp==2.5.34.1 pycti==7.261023.1
We ran this script once more in a fresh folder to check it. It finished with exit code 0, and each server module imported.
Last, we export the variables the servers read. That step needs the two .env files from the Docker steps (misp/src/.env and opencti/src/.env), so it was not part of the check. The file below is whole: the key is read from the MISP .env file at run time and never written into a config.
HERE="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
export MISP_URL="http://127.0.0.1:8481"
export MISP_API_KEY="$(grep '^ADMIN_KEY=' "$HERE/misp/src/.env" | cut -d= -f2)"
export MISP_VERIFYCERT=false
export OPENCTI_URL="http://127.0.0.1:8480"
export OPENCTI_TOKEN="$(grep '^OPENCTI_ADMIN_TOKEN=' "$HERE/opencti/src/.env" | cut -d= -f2)"
One JSON file, saved as mcp.lab.json in the lab folder, lists the three servers. It reads the URLs and keys from environment variables, so no secret is stored in the file. $LAB stands for our lab folder; we shortened the paths here.
{
"mcpServers": {
"misp": {
"command": "$LAB/venv-misp/bin/misp-mcp",
"env": {
"MISP_URL": "${MISP_URL}",
"MISP_API_KEY": "${MISP_API_KEY}",
"MISP_VERIFYCERT": "false"
}
},
"opencti": {
"command": "$LAB/venv-octi/bin/python",
"args": ["-m", "opencti_mcp.server"],
"env": {
"PYTHONPATH": "$LAB/mcp-src/xtm-mcp",
"OPENCTI_URL": "${OPENCTI_URL}",
"OPENCTI_TOKEN": "${OPENCTI_TOKEN}"
}
},
"intel-write": {
"command": "$LAB/venv-writer/bin/python",
"args": ["$LAB/write_server.py"],
"env": {
"MISP_URL": "${MISP_URL}",
"MISP_API_KEY": "${MISP_API_KEY}"
}
}
}
}
MISP’s server needed one fix. Its package accepts any MCP SDK version from 1.0, but it imports a module that SDK 2.x removed, so it crashed on start until we pinned mcp[cli]==1.26.0 in its environment. Run each server in its own virtual environment so such pins do not collide.
Step 5: Ask a Question That Needs Both
We ran Claude Code in print mode from an empty folder, with only these servers and only the read servers allowed. The flag --setting-sources "" keeps personal settings out of the run.
claude -p "<your question>" \
--mcp-config mcp.lab.json --strict-mcp-config \
--setting-sources "" \
--allowedTools mcp__misp mcp__opencti \
--output-format stream-json --verbose \
--max-turns 12 --max-budget-usd 1.00
The question: which KEV vulnerabilities are stored in MISP, which in OpenCTI, and which in both. These are the tool calls, shortened. The first attribute filter and OpenCTI’s text search for “KEV” returned nothing, because the agent guessed the tag as “CISA KEV” while we used cisa-kev. The event search did return one sample event, so the agent read the attributes of both MISP events, listed every OpenCTI vulnerability and compared.
mcp__misp__search_attributes {"type_attribute": "vulnerability", "tags": ["CISA KEV"], "limit": 100}
mcp__misp__search_events {"quick_filter": "CISA KEV", "metadata": true}
mcp__opencti__execute_graphql_query { vulnerabilities(first: 200, search: "KEV") { edges { node { id name x_opencti_cisa_kev description } } } }
-> MISP attributes: [] MISP events: event 1 (KEV lab sample A) OpenCTI edges: []
mcp__misp__search_attributes {"eventid": [1, 2], "include_event_tags": false}
mcp__opencti__execute_graphql_query { vulnerabilities(first: 200) { edges { node { id name description x_opencti_cisa_kev x_opencti_cvss_base_score objectLabel { value } } } } }
This is the final answer. It cost 0.098 US dollars over 7 turns on Claude Sonnet 5.5, taken from the run’s result line.
| CVE | MISP | OpenCTI | Vendor and product |
|---|---|---|---|
| CVE-2023-34362 | Yes | Yes | Progress MOVEit Transfer |
| CVE-2024-3400 | Yes | Yes | Palo Alto Networks PAN-OS |
| CVE-2023-4966 | Yes | Yes | Citrix NetScaler ADC and Gateway |
| CVE-2021-44228 | Yes | No | Apache Log4j2 |
| CVE-2024-1709 | Yes | No | ConnectWise ScreenConnect |
| CVE-2023-22515 | No | Yes | Atlassian Confluence Data Center and Server |
MISP returned the KEV added dates (for example 2024-04-12 for CVE-2024-3400), a "ransomware use: Known" note on all five, and the `cisa-kev` tag on its two sample events. OpenCTI returned CISA-style titles and descriptions, including the one KEV-style CVE MISP lacks (CVE-2023-22515), but no dates or ransomware flag.
- **KEV flag:** OpenCTI has `x_opencti_cisa_kev` set to false on all four of its CVEs. I counted them as KEV only because their descriptions use CISA's KEV wording, so treat the OpenCTI column as likely, not confirmed.
- **MISP source:** Both MISP events are lab samples ("KEV lab sample A" and "KEV lab sample B"), not a full KEV feed.
I only ran read queries and changed nothing.
Two details matter. The table matches what we loaded, so the agent did not invent a row.
It also flagged that OpenCTI’s own KEV field is false on all four entities. That is true: our script did not set it. The agent counted them as KEV only by their description and said so.
Step 6: A Write That Needs a Person’s Approval
Reading is low risk. Writing changes the record other analysts rely on. Our intel-write server has one tool, and it has two guards.
"""intel-write: the one write tool of the lab, a MISP sighting.
Run it as an MCP server over stdio. It has two guards:
1. the client must allow the tool by name (a person adds it to the allow list),
2. the tool itself does nothing unless confirm=true; without it, it only returns a preview."""
import os
import re
from fastmcp import FastMCP
from pymisp import PyMISP
mcp = FastMCP("intel-write")
_misp = PyMISP(os.environ["MISP_URL"], os.environ["MISP_API_KEY"], ssl=False)
VALUE = re.compile(r"^CVE-\d{4}-\d{4,7}$")
@mcp.tool
def misp_add_sighting(value: str, source: str, confirm: bool = False) -> dict:
"""Record that a CVE from the MISP data was seen in our environment (a MISP sighting).
This CHANGES data in MISP. Call it first with confirm=false to get a preview,
show the preview to the user, and call with confirm=true only after the user says yes.
value: a CVE id that already exists as an attribute in MISP.
source: a short label for where it was seen, for example 'soc-ticket-123'."""
if not VALUE.match(value):
return {"error": "value must be a CVE id such as CVE-2024-3400"}
found = _misp.search(controller="attributes", value=value, pythonify=True)
if not found:
return {"error": f"{value} is not an attribute in MISP"}
if not confirm:
return {
"status": "preview",
"would_do": f"add one sighting for {value} with source '{source}' to attribute {found[0].id}",
"changed": False,
}
res = _misp.add_sighting({"value": value, "source": source, "type": "0"})
return {"status": "written", "result": res, "changed": True}
if __name__ == "__main__":
mcp.run()
The first guard is Claude Code’s permission system. In print mode nobody can click Allow. In a run like ours, with no PermissionRequest hook and no --permission-prompt-tool, the allow rule is the approval: a call to a tool that is not in --allowedTools is denied.
The second guard is the confirm parameter. Be clear about its limit: the model sets it, so on its own it proves nothing. The person’s allow rule is the real decision.
First run: the write tool is not allowed. We asked the agent to record a sighting for CVE-2024-3400, seen in a firewall alert with ticket soc-ticket-123.
mcp__intel-write__misp_add_sighting {"value": "CVE-2024-3400", "source": "soc-ticket-123", "confirm": false}
-> Claude requested permissions to use mcp__intel-write__misp_add_sighting, but you haven't granted it yet.
I couldn't record the sighting. The call to `misp_add_sighting` on the intel-write server was blocked because this session hasn't been granted permission to use it.
**What I tried:** I loaded the tool and called it with `value=CVE-2024-3400`, `source=soc-ticket-123` and `confirm=false`. That is the preview call the tool requires before any write. It was denied before it ran, so nothing was sent to MISP and nothing changed.
...
The call was refused before it reached MISP. A read of the sighting list afterwards, saved as sightings-before.json, was an empty list. The result line recorded the refusal in permission_denials, so a script can detect it.
Second run: a person reads that proposal and decides yes. In a script, that decision is one added line, mcp__intel-write__misp_add_sighting in the allowed tools. We also told the agent in the prompt what had been approved. The agent previewed, wrote, and checked the result with the read-only MISP server.
mcp__intel-write__misp_add_sighting {"value": "CVE-2024-3400", "source": "soc-ticket-123", "confirm": false}
-> {"status":"preview","would_do":"add one sighting for CVE-2024-3400 with source 'soc-ticket-123' to attribute 1","changed":false}
mcp__intel-write__misp_add_sighting {"value": "CVE-2024-3400", "source": "soc-ticket-123", "confirm": true}
-> {"status":"written","result":{"Sighting":{"id":"1","attribute_id":"1","event_id":"1", ... "source":"soc-ticket-123", ...
mcp__misp__search_attributes {"value": "CVE-2024-3400", "include_sightings": true}
The sighting is in MISP, and I confirmed it by searching the attribute afterward.
- **Preview (confirm=false):** it would add one sighting for CVE-2024-3400 with source `soc-ticket-123` to attribute 1, and it changed nothing.
- **Write (confirm=true):** the write succeeded and created sighting ID 1.
- **Check:** the MISP search for CVE-2024-3400 with sightings included shows exactly one sighting on attribute 1 (event 1, "KEV lab sample A: edge devices and Java logging"). Its source is `soc-ticket-123` and its organisation is ADMIN. Its UUID is `536af283-8f66-410f-a016-314d3df45ddc`.
...
We then read the sighting back through the MISP API ourselves (sightings-after.json). It shows one sighting on attribute 1 with source soc-ticket-123. The three runs cost 0.098, 0.040 and 0.059 US dollars. In an interactive session Claude Code would ask you at the first call instead of refusing.
The CVE we used has its own page: PAN-OS GlobalProtect command injection (CVE-2024-3400). For safe tool access in general, read securing MCP servers and tool access.
Where OpenCTI and MISP Setups Break
Four things went wrong in our lab. Each cost time, and each is likely on your machine too.
- The disk was full. The first start failed with “No space left on device”. We freed about 10 GB by removing seven unused public images. A half-written RabbitMQ volume then had an empty Erlang cookie, and Elasticsearch could not place its shards. We recreated both volumes and switched off Elasticsearch’s disk checks for the lab.
- The base URL needed a certificate. MISP’s nginx container restarted in a loop until we set
BASE_URLto plain http for the lab. - A published port did not reach localhost. On our Colima setup, the new ports were not forwarded until we added them by hand. Test with curl before blaming the platform.
- An agent guesses filters. Two of the first three queries in Step 5 came back empty. An empty answer is not proof that no data exists, so make the agent check, as ours did.
- Keep read servers and the write server separate, so a write can be denied on its own.
- Give each tool the narrowest scope: one tool, one action, one kind of record.
- Put the allow rule in a place a person controls, not in the prompt.
- Check any write yourself afterwards, from the platform and not from the agent’s summary.
- Pin the MCP SDK per server and read the licence of each server you install.
- Give an agent a platform admin key when a limited API user is enough. Our lab used admin keys for convenience.
- Treat
confirm=trueas consent. The model supplies it. - Run a lab with default passwords or open ports on a shared network.
- Trust an archived server in production without reading its code.
- Paste platform data into a prompt as if it were instructions; tool results are data.
Text stored in MISP or OpenCTI can come from feeds you do not control, and an agent reads it as plain text. Our guide to agentic SOC and threat intelligence covers that risk. For a product view of MCP access to Brandefense data, see the Brandefense MCP server page.
We stopped both stacks when we finished with docker compose down. The volumes stay on disk until you add -v, so you can delete them when you are done.
Next: Turn a Threat Report Into Sigma Rules
This part connected an agent to the places where intelligence is stored. Part 4 works on the input side: it takes a threat report, maps it to ATT&CK, and turns it into Sigma detection rules that tools validate. The full write-up is From Threat Report to ATT&CK Mapping and Sigma Rules.
The map of all eight parts is on the Agentic Threat Intelligence 101 guide.
