opencti-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencti_mcp-0.1.0/.env.example +11 -0
- opencti_mcp-0.1.0/.github/workflows/ci.yml +18 -0
- opencti_mcp-0.1.0/.github/workflows/publish.yml +23 -0
- opencti_mcp-0.1.0/.gitignore +16 -0
- opencti_mcp-0.1.0/CHANGELOG.md +5 -0
- opencti_mcp-0.1.0/CLAUDE.md +65 -0
- opencti_mcp-0.1.0/LICENSE +21 -0
- opencti_mcp-0.1.0/PKG-INFO +212 -0
- opencti_mcp-0.1.0/README.md +179 -0
- opencti_mcp-0.1.0/docs/ENDPOINTS.md +201 -0
- opencti_mcp-0.1.0/pyproject.toml +54 -0
- opencti_mcp-0.1.0/skills/opencti-usage/SKILL.md +93 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/admin-connectors.md +83 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/api-and-graphql.md +47 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/confidence-and-scoring.md +49 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/containers.md +53 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/data-model.md +147 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/entities.md +82 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/indicators-vs-observables.md +138 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/markings-and-tlp.md +84 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/mcp-tool-map.md +42 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/rbac-and-orgs.md +120 -0
- opencti_mcp-0.1.0/skills/opencti-usage/references/relationships.md +60 -0
- opencti_mcp-0.1.0/src/opencti_mcp/__init__.py +1 -0
- opencti_mcp-0.1.0/src/opencti_mcp/client.py +43 -0
- opencti_mcp-0.1.0/src/opencti_mcp/config.py +81 -0
- opencti_mcp-0.1.0/src/opencti_mcp/errors.py +2 -0
- opencti_mcp-0.1.0/src/opencti_mcp/server.py +53 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/__init__.py +52 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/_common.py +156 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/attack_patterns.py +31 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/campaigns.py +28 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/cases.py +32 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/graphql.py +42 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/identities.py +52 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/incidents.py +31 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/indicators.py +51 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/intrusion_sets.py +39 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/labels_markings.py +28 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/malware.py +45 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/observables.py +108 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/platform.py +55 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/relationships.py +66 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/reports.py +49 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/search.py +32 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/stix_bundle.py +85 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/threat_actors.py +33 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/tools.py +43 -0
- opencti_mcp-0.1.0/src/opencti_mcp/tools/vulnerabilities.py +28 -0
- opencti_mcp-0.1.0/tests/__init__.py +1 -0
- opencti_mcp-0.1.0/tests/test_array_param_invocation.py +165 -0
- opencti_mcp-0.1.0/tests/test_array_param_schema.py +87 -0
- opencti_mcp-0.1.0/tests/test_client.py +35 -0
- opencti_mcp-0.1.0/tests/test_common_factories.py +158 -0
- opencti_mcp-0.1.0/tests/test_config.py +50 -0
- opencti_mcp-0.1.0/tests/test_enhancements.py +168 -0
- opencti_mcp-0.1.0/tests/test_graphql_tool.py +28 -0
- opencti_mcp-0.1.0/tests/test_identities.py +150 -0
- opencti_mcp-0.1.0/tests/test_indicators.py +50 -0
- opencti_mcp-0.1.0/tests/test_labels_markings.py +67 -0
- opencti_mcp-0.1.0/tests/test_observables.py +26 -0
- opencti_mcp-0.1.0/tests/test_platform.py +225 -0
- opencti_mcp-0.1.0/tests/test_register_all.py +40 -0
- opencti_mcp-0.1.0/tests/test_reports_rel_cases.py +42 -0
- opencti_mcp-0.1.0/tests/test_server.py +46 -0
- opencti_mcp-0.1.0/tests/test_simple_domains.py +80 -0
- opencti_mcp-0.1.0/tests/test_slim.py +232 -0
- opencti_mcp-0.1.0/tests/test_stix_bundle.py +218 -0
- opencti_mcp-0.1.0/tests/test_tool_invocation.py +486 -0
- opencti_mcp-0.1.0/uv.lock +2699 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v3
|
|
15
|
+
- run: uv venv --python ${{ matrix.python-version }}
|
|
16
|
+
- run: uv pip install -e ".[dev]"
|
|
17
|
+
- run: uv run ruff check .
|
|
18
|
+
- run: uv run pytest -q
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a GitHub Release is published, using PyPI Trusted
|
|
4
|
+
# Publishing (OIDC) — no API token or secret is stored anywhere.
|
|
5
|
+
on:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # REQUIRED for PyPI Trusted Publishing (OIDC)
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v3
|
|
20
|
+
- name: Build sdist and wheel
|
|
21
|
+
run: uv build
|
|
22
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
23
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.env
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
|
|
12
|
+
# Local Claude Code / editor state
|
|
13
|
+
.claude/
|
|
14
|
+
|
|
15
|
+
# Internal development docs (brainstorm specs + implementation plans) — kept local, not published
|
|
16
|
+
docs/superpowers/
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
|
4
|
+
|
|
5
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
6
|
+
|
|
7
|
+
## 1. Think Before Coding
|
|
8
|
+
|
|
9
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
10
|
+
|
|
11
|
+
Before implementing:
|
|
12
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
13
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
14
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
15
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
16
|
+
|
|
17
|
+
## 2. Simplicity First
|
|
18
|
+
|
|
19
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
20
|
+
|
|
21
|
+
- No features beyond what was asked.
|
|
22
|
+
- No abstractions for single-use code.
|
|
23
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
24
|
+
- No error handling for impossible scenarios.
|
|
25
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
26
|
+
|
|
27
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
28
|
+
|
|
29
|
+
## 3. Surgical Changes
|
|
30
|
+
|
|
31
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
32
|
+
|
|
33
|
+
When editing existing code:
|
|
34
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
35
|
+
- Don't refactor things that aren't broken.
|
|
36
|
+
- Match existing style, even if you'd do it differently.
|
|
37
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
38
|
+
|
|
39
|
+
When your changes create orphans:
|
|
40
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
41
|
+
- Don't remove pre-existing dead code unless asked.
|
|
42
|
+
|
|
43
|
+
The test: Every changed line should trace directly to the user's request.
|
|
44
|
+
|
|
45
|
+
## 4. Goal-Driven Execution
|
|
46
|
+
|
|
47
|
+
**Define success criteria. Loop until verified.**
|
|
48
|
+
|
|
49
|
+
Transform tasks into verifiable goals:
|
|
50
|
+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
|
51
|
+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
|
52
|
+
- "Refactor X" → "Ensure tests pass before and after"
|
|
53
|
+
|
|
54
|
+
For multi-step tasks, state a brief plan:
|
|
55
|
+
```
|
|
56
|
+
1. [Step] → verify: [check]
|
|
57
|
+
2. [Step] → verify: [check]
|
|
58
|
+
3. [Step] → verify: [check]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kierston Grantham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opencti-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server exposing the OpenCTI threat-intelligence platform to AI assistants.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Space-C0wboy/OpenCTI-MCP-Server
|
|
6
|
+
Project-URL: Repository, https://github.com/Space-C0wboy/OpenCTI-MCP-Server
|
|
7
|
+
Project-URL: Issues, https://github.com/Space-C0wboy/OpenCTI-MCP-Server/issues
|
|
8
|
+
Author: Kierston Grantham
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cti,fastmcp,mcp,model-context-protocol,opencti,pycti,security,stix,threat-intelligence
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: fastmcp<3.0,>=2.0.0
|
|
25
|
+
Requires-Dist: pycti>=6.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0
|
|
27
|
+
Requires-Dist: python-dotenv>=1.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# OpenCTI MCP Server
|
|
35
|
+
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://modelcontextprotocol.io)
|
|
39
|
+
[](https://github.com/Space-C0wboy/OpenCTI-MCP-Server/actions/workflows/ci.yml)
|
|
40
|
+
|
|
41
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes **OpenCTI**, the open-source threat-intelligence platform, to AI assistants. Built on the official `pycti` client, it registers 45 typed tools (91 when writes are enabled) across 15 threat-intel domains — indicators, observables, reports, malware, threat actors, intrusion sets, campaigns, attack patterns, tools, vulnerabilities, incidents, cases, relationships, identities, and labels/markings — plus platform/operations tools (connectors, users, groups, files), STIX bundle import/export, a `graphql_query` escape-hatch, and cross-cutting search/relationship/observable-lookup tools for anything the typed per-domain tools don't cover.
|
|
42
|
+
|
|
43
|
+
> [!IMPORTANT]
|
|
44
|
+
> This is an unofficial, community-maintained project. It is not affiliated with, endorsed by, or supported by Filigran or the OpenCTI project.
|
|
45
|
+
|
|
46
|
+
> [!WARNING]
|
|
47
|
+
> **Ships read-only by default.** With the default configuration, only list/get/query tools are registered and `graphql_query` rejects mutations. Setting `OPENCTI_READ_ONLY=false` exposes `*_create`, `*_update`, and the destructive `*_delete` tools, and lets `graphql_query` run mutations. Only enable writes with a least-privilege OpenCTI API token, and understand that an assistant with write access can create, modify, and permanently delete intelligence data in your instance.
|
|
48
|
+
|
|
49
|
+
## Tools
|
|
50
|
+
|
|
51
|
+
| Domain | Reads | Writes | Notable tools |
|
|
52
|
+
| --- | :---: | :---: | --- |
|
|
53
|
+
| Indicators | 2 | 3 | `indicators_list`, `indicators_get`, `indicators_create` |
|
|
54
|
+
| Observables | 2 | 3 | `observables_list`, `observables_get`, `observables_create` |
|
|
55
|
+
| Reports | 2 | 3 | `reports_list`, `reports_get`, `reports_create` |
|
|
56
|
+
| Malware | 2 | 3 | `malware_list`, `malware_get`, `malware_create` |
|
|
57
|
+
| Threat actors | 2 | 3 | `threat_actors_list`, `threat_actors_get`, `threat_actors_create` |
|
|
58
|
+
| Intrusion sets | 2 | 3 | `intrusion_sets_list`, `intrusion_sets_get`, `intrusion_sets_create` |
|
|
59
|
+
| Campaigns | 2 | 3 | `campaigns_list`, `campaigns_get`, `campaigns_create` |
|
|
60
|
+
| Attack patterns | 2 | 3 | `attack_patterns_list`, `attack_patterns_get`, `attack_patterns_create` |
|
|
61
|
+
| Tools | 2 | 3 | `tools_list`, `tools_get`, `tools_create` |
|
|
62
|
+
| Vulnerabilities | 2 | 3 | `vulnerabilities_list`, `vulnerabilities_get`, `vulnerabilities_create` |
|
|
63
|
+
| Incidents | 2 | 3 | `incidents_list`, `incidents_get`, `incidents_create` |
|
|
64
|
+
| Cases | 2 | 3 | `cases_list`, `cases_get`, `cases_create` |
|
|
65
|
+
| Relationships | 2 | 3 | `relationships_list`, `relationships_get`, `relationships_create` |
|
|
66
|
+
| Identities | 2 | 2 | `identities_list`, `identities_get`, `identities_create` (organizations/individuals/systems/sectors), `identities_delete` |
|
|
67
|
+
| Labels & markings | 4 | 3 | `labels_list`, `labels_create`, `markings_list`, `markings_get` |
|
|
68
|
+
|
|
69
|
+
Every domain also has an `_update` and `_delete` tool once writes are enabled (markings are read-only; identities has `_create`/`_delete` but no `_update`). In addition, `graphql_query` runs a raw GraphQL query/mutation against the OpenCTI API for anything not covered by a typed tool above — reads always work, mutations are rejected unless `OPENCTI_READ_ONLY=false`. See [`docs/ENDPOINTS.md`](docs/ENDPOINTS.md) for the full tool-to-`pycti`-call mapping.
|
|
70
|
+
|
|
71
|
+
Every `*_list` tool also accepts `order_by` (a field name, e.g. `"created_at"`) and `descending` (default `true`), so `order_by="created_at"` gives most-recent-first without any extra filtering.
|
|
72
|
+
|
|
73
|
+
`observables_create` also accepts `StixFile.*` observable keys (normalized to `File.*`), coerces `Autonomous-System.number` values to an integer, and takes an optional `additional_hashes` param (a list of `"ALGO:VALUE"` entries, e.g. `"SHA-1:..."`) to attach extra hashes to a file observable alongside the primary one.
|
|
74
|
+
|
|
75
|
+
### Cross-cutting read tools
|
|
76
|
+
|
|
77
|
+
- `search_all(query, types?, first?)` — full-text search across all STIX object types at once, optionally restricted to specific `types`.
|
|
78
|
+
- `relationships_for_entity(entity_id, relationship_type?, first?)` — lists relationships touching an entity as either source or target, e.g. "what is related to X".
|
|
79
|
+
- `observables_by_value(value, first?)` — exact-value lookup for an observable (IP, domain, URL, hash, etc.) without knowing its id.
|
|
80
|
+
|
|
81
|
+
### Platform & operations
|
|
82
|
+
|
|
83
|
+
- `connectors_list` / `connectors_get(connector_id)` — list connectors and check one's status/health (active, state, queue details).
|
|
84
|
+
- `users_list` / `users_get(id)` — list/get OpenCTI users.
|
|
85
|
+
- `groups_list` / `groups_get(id)` — list/get OpenCTI groups.
|
|
86
|
+
- `files_for_entity(entity_id)` — list files/attachments on an entity.
|
|
87
|
+
- `stix_export_entity(entity_type, entity_id, mode?)` / `stix_export_list(entity_type, first=25, mode?)` — export a single entity, or up to `first` entities of a type (max 500), as a STIX 2 bundle; large exports are capped by `first`, and if the bundle is still too large a small note (`_note`, `entity_type`, `first`, `object_count`) is returned instead.
|
|
88
|
+
- `stix_import_bundle(bundle_json, update?)` — **write, requires `OPENCTI_READ_ONLY=false`** — bulk create/update entities and relationships from a STIX bundle.
|
|
89
|
+
- `entity_ask_enrichment(entity_id, connector_id)` — **write, requires `OPENCTI_READ_ONLY=false`** — trigger a connector to enrich an entity.
|
|
90
|
+
|
|
91
|
+
Two related capabilities are intentionally not exposed as typed tools and remain available via `graphql_query`: OpenCTI status templates, and connector start/stop/reset (`pycti` exposes no dedicated method for either).
|
|
92
|
+
|
|
93
|
+
## Bundled skill
|
|
94
|
+
|
|
95
|
+
A **Claude Skill** lives at [`skills/opencti-usage/`](skills/opencti-usage/) that teaches an AI agent OpenCTI's model — the STIX data model, entity types, indicators vs observables, relationships, containers, TLP markings, confidence, connectors, and RBAC — and maps each concept to the right MCP tool above. An agent operating the `opencti` tools loads this skill to understand the platform before creating, relating, or reasoning about OpenCTI data.
|
|
96
|
+
|
|
97
|
+
- [`skills/opencti-usage/SKILL.md`](skills/opencti-usage/SKILL.md) is the entry point; the `references/` folder holds the per-topic detail.
|
|
98
|
+
- The content is distilled from the official OpenCTI docs (`docs.opencti.io/latest`), distilled 2026-07-10. Each reference file has a `## Source` link back to the docs it was drawn from, so it can be refreshed later.
|
|
99
|
+
|
|
100
|
+
## Quick start
|
|
101
|
+
|
|
102
|
+
### Install
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git clone https://github.com/Space-C0wboy/OpenCTI-MCP-Server.git
|
|
106
|
+
cd OpenCTI-MCP-Server
|
|
107
|
+
uv venv
|
|
108
|
+
uv pip install -e ".[dev]"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Or with plain `pip`:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install -e .
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Get an API token
|
|
118
|
+
|
|
119
|
+
In OpenCTI, open your profile menu → **API access**, then copy your personal access token.
|
|
120
|
+
|
|
121
|
+
### Configuration
|
|
122
|
+
|
|
123
|
+
Configuration is read from environment variables, or from a `.env` file in the working directory (either a standard `KEY=value` dotenv file or a JSON-style fragment). OS environment variables always take precedence over the file.
|
|
124
|
+
|
|
125
|
+
| Variable | Required | Default | Description |
|
|
126
|
+
| --- | :---: | --- | --- |
|
|
127
|
+
| `OPENCTI_URL` | Yes | — | Base URL of your OpenCTI instance |
|
|
128
|
+
| `OPENCTI_TOKEN` | Yes | — | OpenCTI API token |
|
|
129
|
+
| `OPENCTI_READ_ONLY` | No | `true` | When `true`, hides all write tools and blocks mutations |
|
|
130
|
+
| `OPENCTI_TIMEOUT` | No | `30` | Request timeout in seconds |
|
|
131
|
+
| `OPENCTI_SSL_VERIFY` | No | `true` | Verify TLS certificates |
|
|
132
|
+
| `LOG_LEVEL` | No | `INFO` | Logging verbosity |
|
|
133
|
+
| `MCP_HTTP_HOST` | No | `127.0.0.1` | Bind host when using `--transport http` |
|
|
134
|
+
| `MCP_HTTP_PORT` | No | `8765` | Bind port when using `--transport http` |
|
|
135
|
+
|
|
136
|
+
### Run
|
|
137
|
+
|
|
138
|
+
Stdio (default, for editor/IDE integrations):
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run opencti-mcp
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
HTTP transport:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv run opencti-mcp --transport http --host 127.0.0.1 --port 8765
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Read-only mode
|
|
151
|
+
|
|
152
|
+
`OPENCTI_READ_ONLY` defaults to `true`. In read-only mode, only list/get tools and `markings_list`/`markings_get` are registered — no `*_create`, `*_update`, or `*_delete` tool is available for any domain, and `graphql_query` raises an error if the query document contains a `mutation`. Set `OPENCTI_READ_ONLY=false` to register the write tools and allow `graphql_query` to run mutations.
|
|
153
|
+
|
|
154
|
+
## Editor integration
|
|
155
|
+
|
|
156
|
+
### Claude Desktop
|
|
157
|
+
|
|
158
|
+
Add to `claude_desktop_config.json`:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"mcpServers": {
|
|
163
|
+
"opencti": {
|
|
164
|
+
"command": "uv",
|
|
165
|
+
"args": ["--directory", "/absolute/path/to/OpenCTI-MCP-Server", "run", "opencti-mcp"],
|
|
166
|
+
"env": {
|
|
167
|
+
"OPENCTI_URL": "https://your-opencti-instance.example.com/",
|
|
168
|
+
"OPENCTI_TOKEN": "your-api-token"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Claude Code
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
claude mcp add opencti -- uv --directory /absolute/path/to/OpenCTI-MCP-Server run opencti-mcp
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Example prompts
|
|
182
|
+
|
|
183
|
+
- "List the 10 most recent indicators."
|
|
184
|
+
- "Search reports mentioning 'Emotet'."
|
|
185
|
+
- "Show me the MITRE ATT&CK technique T1059."
|
|
186
|
+
- "Create an IPv4 observable for 1.2.3.4." (requires `OPENCTI_READ_ONLY=false`)
|
|
187
|
+
- "Which threat actors are linked to intrusion set APT29?"
|
|
188
|
+
- "Get the full detail on case CASE-ID, including its labels and markings."
|
|
189
|
+
- "What are the 10 most recent reports?"
|
|
190
|
+
- "Is 1.2.3.4 in OpenCTI?"
|
|
191
|
+
- "What is this threat actor related to?"
|
|
192
|
+
- "Search across everything for 'Emotet'."
|
|
193
|
+
- "Are our OpenCTI connectors healthy?"
|
|
194
|
+
- "Export this report as a STIX bundle."
|
|
195
|
+
- "List the groups in OpenCTI."
|
|
196
|
+
- "Add the victim organization 'Acme Corp' to OpenCTI." (requires `OPENCTI_READ_ONLY=false`)
|
|
197
|
+
- "Create an individual identity for John Doe with a description."
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
uv run pytest
|
|
203
|
+
uv run ruff check .
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT — see [LICENSE](LICENSE).
|
|
209
|
+
|
|
210
|
+
## Support
|
|
211
|
+
|
|
212
|
+
This is a community-maintained project provided as-is, with no guaranteed support SLA. Issues and pull requests are welcome.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# OpenCTI MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
[](https://github.com/Space-C0wboy/OpenCTI-MCP-Server/actions/workflows/ci.yml)
|
|
7
|
+
|
|
8
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes **OpenCTI**, the open-source threat-intelligence platform, to AI assistants. Built on the official `pycti` client, it registers 45 typed tools (91 when writes are enabled) across 15 threat-intel domains — indicators, observables, reports, malware, threat actors, intrusion sets, campaigns, attack patterns, tools, vulnerabilities, incidents, cases, relationships, identities, and labels/markings — plus platform/operations tools (connectors, users, groups, files), STIX bundle import/export, a `graphql_query` escape-hatch, and cross-cutting search/relationship/observable-lookup tools for anything the typed per-domain tools don't cover.
|
|
9
|
+
|
|
10
|
+
> [!IMPORTANT]
|
|
11
|
+
> This is an unofficial, community-maintained project. It is not affiliated with, endorsed by, or supported by Filigran or the OpenCTI project.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> **Ships read-only by default.** With the default configuration, only list/get/query tools are registered and `graphql_query` rejects mutations. Setting `OPENCTI_READ_ONLY=false` exposes `*_create`, `*_update`, and the destructive `*_delete` tools, and lets `graphql_query` run mutations. Only enable writes with a least-privilege OpenCTI API token, and understand that an assistant with write access can create, modify, and permanently delete intelligence data in your instance.
|
|
15
|
+
|
|
16
|
+
## Tools
|
|
17
|
+
|
|
18
|
+
| Domain | Reads | Writes | Notable tools |
|
|
19
|
+
| --- | :---: | :---: | --- |
|
|
20
|
+
| Indicators | 2 | 3 | `indicators_list`, `indicators_get`, `indicators_create` |
|
|
21
|
+
| Observables | 2 | 3 | `observables_list`, `observables_get`, `observables_create` |
|
|
22
|
+
| Reports | 2 | 3 | `reports_list`, `reports_get`, `reports_create` |
|
|
23
|
+
| Malware | 2 | 3 | `malware_list`, `malware_get`, `malware_create` |
|
|
24
|
+
| Threat actors | 2 | 3 | `threat_actors_list`, `threat_actors_get`, `threat_actors_create` |
|
|
25
|
+
| Intrusion sets | 2 | 3 | `intrusion_sets_list`, `intrusion_sets_get`, `intrusion_sets_create` |
|
|
26
|
+
| Campaigns | 2 | 3 | `campaigns_list`, `campaigns_get`, `campaigns_create` |
|
|
27
|
+
| Attack patterns | 2 | 3 | `attack_patterns_list`, `attack_patterns_get`, `attack_patterns_create` |
|
|
28
|
+
| Tools | 2 | 3 | `tools_list`, `tools_get`, `tools_create` |
|
|
29
|
+
| Vulnerabilities | 2 | 3 | `vulnerabilities_list`, `vulnerabilities_get`, `vulnerabilities_create` |
|
|
30
|
+
| Incidents | 2 | 3 | `incidents_list`, `incidents_get`, `incidents_create` |
|
|
31
|
+
| Cases | 2 | 3 | `cases_list`, `cases_get`, `cases_create` |
|
|
32
|
+
| Relationships | 2 | 3 | `relationships_list`, `relationships_get`, `relationships_create` |
|
|
33
|
+
| Identities | 2 | 2 | `identities_list`, `identities_get`, `identities_create` (organizations/individuals/systems/sectors), `identities_delete` |
|
|
34
|
+
| Labels & markings | 4 | 3 | `labels_list`, `labels_create`, `markings_list`, `markings_get` |
|
|
35
|
+
|
|
36
|
+
Every domain also has an `_update` and `_delete` tool once writes are enabled (markings are read-only; identities has `_create`/`_delete` but no `_update`). In addition, `graphql_query` runs a raw GraphQL query/mutation against the OpenCTI API for anything not covered by a typed tool above — reads always work, mutations are rejected unless `OPENCTI_READ_ONLY=false`. See [`docs/ENDPOINTS.md`](docs/ENDPOINTS.md) for the full tool-to-`pycti`-call mapping.
|
|
37
|
+
|
|
38
|
+
Every `*_list` tool also accepts `order_by` (a field name, e.g. `"created_at"`) and `descending` (default `true`), so `order_by="created_at"` gives most-recent-first without any extra filtering.
|
|
39
|
+
|
|
40
|
+
`observables_create` also accepts `StixFile.*` observable keys (normalized to `File.*`), coerces `Autonomous-System.number` values to an integer, and takes an optional `additional_hashes` param (a list of `"ALGO:VALUE"` entries, e.g. `"SHA-1:..."`) to attach extra hashes to a file observable alongside the primary one.
|
|
41
|
+
|
|
42
|
+
### Cross-cutting read tools
|
|
43
|
+
|
|
44
|
+
- `search_all(query, types?, first?)` — full-text search across all STIX object types at once, optionally restricted to specific `types`.
|
|
45
|
+
- `relationships_for_entity(entity_id, relationship_type?, first?)` — lists relationships touching an entity as either source or target, e.g. "what is related to X".
|
|
46
|
+
- `observables_by_value(value, first?)` — exact-value lookup for an observable (IP, domain, URL, hash, etc.) without knowing its id.
|
|
47
|
+
|
|
48
|
+
### Platform & operations
|
|
49
|
+
|
|
50
|
+
- `connectors_list` / `connectors_get(connector_id)` — list connectors and check one's status/health (active, state, queue details).
|
|
51
|
+
- `users_list` / `users_get(id)` — list/get OpenCTI users.
|
|
52
|
+
- `groups_list` / `groups_get(id)` — list/get OpenCTI groups.
|
|
53
|
+
- `files_for_entity(entity_id)` — list files/attachments on an entity.
|
|
54
|
+
- `stix_export_entity(entity_type, entity_id, mode?)` / `stix_export_list(entity_type, first=25, mode?)` — export a single entity, or up to `first` entities of a type (max 500), as a STIX 2 bundle; large exports are capped by `first`, and if the bundle is still too large a small note (`_note`, `entity_type`, `first`, `object_count`) is returned instead.
|
|
55
|
+
- `stix_import_bundle(bundle_json, update?)` — **write, requires `OPENCTI_READ_ONLY=false`** — bulk create/update entities and relationships from a STIX bundle.
|
|
56
|
+
- `entity_ask_enrichment(entity_id, connector_id)` — **write, requires `OPENCTI_READ_ONLY=false`** — trigger a connector to enrich an entity.
|
|
57
|
+
|
|
58
|
+
Two related capabilities are intentionally not exposed as typed tools and remain available via `graphql_query`: OpenCTI status templates, and connector start/stop/reset (`pycti` exposes no dedicated method for either).
|
|
59
|
+
|
|
60
|
+
## Bundled skill
|
|
61
|
+
|
|
62
|
+
A **Claude Skill** lives at [`skills/opencti-usage/`](skills/opencti-usage/) that teaches an AI agent OpenCTI's model — the STIX data model, entity types, indicators vs observables, relationships, containers, TLP markings, confidence, connectors, and RBAC — and maps each concept to the right MCP tool above. An agent operating the `opencti` tools loads this skill to understand the platform before creating, relating, or reasoning about OpenCTI data.
|
|
63
|
+
|
|
64
|
+
- [`skills/opencti-usage/SKILL.md`](skills/opencti-usage/SKILL.md) is the entry point; the `references/` folder holds the per-topic detail.
|
|
65
|
+
- The content is distilled from the official OpenCTI docs (`docs.opencti.io/latest`), distilled 2026-07-10. Each reference file has a `## Source` link back to the docs it was drawn from, so it can be refreshed later.
|
|
66
|
+
|
|
67
|
+
## Quick start
|
|
68
|
+
|
|
69
|
+
### Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/Space-C0wboy/OpenCTI-MCP-Server.git
|
|
73
|
+
cd OpenCTI-MCP-Server
|
|
74
|
+
uv venv
|
|
75
|
+
uv pip install -e ".[dev]"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or with plain `pip`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install -e .
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Get an API token
|
|
85
|
+
|
|
86
|
+
In OpenCTI, open your profile menu → **API access**, then copy your personal access token.
|
|
87
|
+
|
|
88
|
+
### Configuration
|
|
89
|
+
|
|
90
|
+
Configuration is read from environment variables, or from a `.env` file in the working directory (either a standard `KEY=value` dotenv file or a JSON-style fragment). OS environment variables always take precedence over the file.
|
|
91
|
+
|
|
92
|
+
| Variable | Required | Default | Description |
|
|
93
|
+
| --- | :---: | --- | --- |
|
|
94
|
+
| `OPENCTI_URL` | Yes | — | Base URL of your OpenCTI instance |
|
|
95
|
+
| `OPENCTI_TOKEN` | Yes | — | OpenCTI API token |
|
|
96
|
+
| `OPENCTI_READ_ONLY` | No | `true` | When `true`, hides all write tools and blocks mutations |
|
|
97
|
+
| `OPENCTI_TIMEOUT` | No | `30` | Request timeout in seconds |
|
|
98
|
+
| `OPENCTI_SSL_VERIFY` | No | `true` | Verify TLS certificates |
|
|
99
|
+
| `LOG_LEVEL` | No | `INFO` | Logging verbosity |
|
|
100
|
+
| `MCP_HTTP_HOST` | No | `127.0.0.1` | Bind host when using `--transport http` |
|
|
101
|
+
| `MCP_HTTP_PORT` | No | `8765` | Bind port when using `--transport http` |
|
|
102
|
+
|
|
103
|
+
### Run
|
|
104
|
+
|
|
105
|
+
Stdio (default, for editor/IDE integrations):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv run opencti-mcp
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
HTTP transport:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run opencti-mcp --transport http --host 127.0.0.1 --port 8765
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Read-only mode
|
|
118
|
+
|
|
119
|
+
`OPENCTI_READ_ONLY` defaults to `true`. In read-only mode, only list/get tools and `markings_list`/`markings_get` are registered — no `*_create`, `*_update`, or `*_delete` tool is available for any domain, and `graphql_query` raises an error if the query document contains a `mutation`. Set `OPENCTI_READ_ONLY=false` to register the write tools and allow `graphql_query` to run mutations.
|
|
120
|
+
|
|
121
|
+
## Editor integration
|
|
122
|
+
|
|
123
|
+
### Claude Desktop
|
|
124
|
+
|
|
125
|
+
Add to `claude_desktop_config.json`:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"opencti": {
|
|
131
|
+
"command": "uv",
|
|
132
|
+
"args": ["--directory", "/absolute/path/to/OpenCTI-MCP-Server", "run", "opencti-mcp"],
|
|
133
|
+
"env": {
|
|
134
|
+
"OPENCTI_URL": "https://your-opencti-instance.example.com/",
|
|
135
|
+
"OPENCTI_TOKEN": "your-api-token"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Claude Code
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
claude mcp add opencti -- uv --directory /absolute/path/to/OpenCTI-MCP-Server run opencti-mcp
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Example prompts
|
|
149
|
+
|
|
150
|
+
- "List the 10 most recent indicators."
|
|
151
|
+
- "Search reports mentioning 'Emotet'."
|
|
152
|
+
- "Show me the MITRE ATT&CK technique T1059."
|
|
153
|
+
- "Create an IPv4 observable for 1.2.3.4." (requires `OPENCTI_READ_ONLY=false`)
|
|
154
|
+
- "Which threat actors are linked to intrusion set APT29?"
|
|
155
|
+
- "Get the full detail on case CASE-ID, including its labels and markings."
|
|
156
|
+
- "What are the 10 most recent reports?"
|
|
157
|
+
- "Is 1.2.3.4 in OpenCTI?"
|
|
158
|
+
- "What is this threat actor related to?"
|
|
159
|
+
- "Search across everything for 'Emotet'."
|
|
160
|
+
- "Are our OpenCTI connectors healthy?"
|
|
161
|
+
- "Export this report as a STIX bundle."
|
|
162
|
+
- "List the groups in OpenCTI."
|
|
163
|
+
- "Add the victim organization 'Acme Corp' to OpenCTI." (requires `OPENCTI_READ_ONLY=false`)
|
|
164
|
+
- "Create an individual identity for John Doe with a description."
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv run pytest
|
|
170
|
+
uv run ruff check .
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT — see [LICENSE](LICENSE).
|
|
176
|
+
|
|
177
|
+
## Support
|
|
178
|
+
|
|
179
|
+
This is a community-maintained project provided as-is, with no guaranteed support SLA. Issues and pull requests are welcome.
|