Skip to main content
Glama
scriptedstatement

opencti-mcp

README.md
> [!IMPORTANT]
> **This repository has been retired.** It is no longer maintained.
>
> This package is now part of the **[AppliedIR/sift-mcp](https://github.com/AppliedIR/sift-mcp)** monorepo.
>
> Documentation: [appliedir.github.io/aiir](https://appliedir.github.io/aiir/)

---

# OpenCTI MCP Server

An MCP (Model Context Protocol) server providing comprehensive threat intelligence access to OpenCTI for Claude Code and other MCP clients.

> **Note:** Validate and harden appropriately for your environment before production use.

## Installation Options

### Option A: As Part of Claude-IR (Recommended)

This MCP is designed as a component of the [Claude-IR](https://github.com/scriptedstatement/claude-ir) AI-assisted incident response workstation.

```bash
git clone https://github.com/scriptedstatement/claude-ir.git
cd claude-ir
./setup.sh
claude
```

**Benefits of Claude-IR installation:**
- Guided setup with component selection
- Pre-configured MCP integration
- Works alongside forensic-rag-mcp (knowledge search) and windows-triage-mcp (file validation)
- Forensic discipline rules and investigation workflows

**Note:** This MCP requires an OpenCTI instance. See `SETUP.md` for guidance on connecting to or deploying OpenCTI.

### Option B: Standalone Installation

Use standalone when you only need threat intelligence lookups without the full IR workstation.

```bash
git clone https://github.com/scriptedstatement/opencti-mcp.git
cd opencti-mcp

# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate

# Install
pip install -e .

# Configure (requires OpenCTI instance - see SETUP.md)
export OPENCTI_TOKEN="your-api-token"
export OPENCTI_URL="http://localhost:8080"          # Local Docker
# export OPENCTI_URL="https://opencti.example.com"  # Remote/cloud

# Run server
python -m opencti_mcp
```

**For OpenCTI setup guidance:** See `SETUP.md`

## Features

### Search Operations (32 tools, 28 visible in read-only mode)

| Category | Tools | Description |
|----------|-------|-------------|
| **Unified Search** | `search_threat_intel` | Search across all entity types |
| **Threats** | `search_threat_actor`, `search_campaign` | APT groups, campaigns |
| **Arsenal** | `search_malware`, `search_tool`, `search_vulnerability` | Malware, tools, CVEs |
| **Techniques** | `search_attack_pattern`, `search_course_of_action` | MITRE ATT&CK, mitigations |
| **Observations** | `search_observable`, `search_sighting` | IOCs, detection events |
| **Events** | `search_incident` | Security incidents |
| **Analysis** | `search_reports`, `search_grouping`, `search_note` | Reports, groupings, notes |
| **Entities** | `search_organization`, `search_sector` | Organizations, industries |
| **Locations** | `search_location` | Countries, regions, cities |
| **Infrastructure** | `search_infrastructure` | C2, hosting, botnets |

### Entity Operations

| Tool | Description |
|------|-------------|
| `lookup_ioc` | Get full IOC context with relationships |
| `lookup_hash` | Look up file hash (MD5/SHA1/SHA256) |
| `get_entity` | Get any entity by ID |
| `get_relationships` | Get entity relationships |
| `get_recent_indicators` | Get indicators from last N days |

### Write Operations (requires `OPENCTI_READ_ONLY=false`)

| Tool | Description |
|------|-------------|
| `create_indicator` | Create new IOC |
| `create_note` | Add analyst note to entities |
| `create_sighting` | Record detection event |
| `trigger_enrichment` | Trigger VirusTotal/Shodan enrichment |

### System Operations

| Tool | Description |
|------|-------------|
| `get_health` | Check OpenCTI connectivity |
| `list_connectors` | List enrichment connectors |
| `get_network_status` | View adaptive metrics and recommendations |
| `force_reconnect` | Force reconnection (clears caches, resets circuit breaker) |
| `get_cache_stats` | View response cache statistics |

## Advanced Filtering

All search tools support advanced filtering:

```json
{
  "query": "APT29",
  "limit": 10,
  "offset": 0,
  "labels": ["tlp:amber", "apt"],
  "confidence_min": 70,
  "created_after": "2024-01-01",
  "created_before": "2024-12-31"
}
```

## Configuration

Settings are loaded via `Config.load()` classmethod (`config.py`) with `SecretStr` token protection and helper parsers for typed env vars.

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `OPENCTI_URL` | `http://localhost:8080` | OpenCTI instance URL (use `https://` for remote) |
| `OPENCTI_TOKEN` | - | API token (required) |
| `OPENCTI_READ_ONLY` | `true` | Disable write operations |
| `OPENCTI_TIMEOUT` | `60` | Request timeout in seconds |
| `OPENCTI_MAX_RESULTS` | `100` | Maximum results per query |
| `OPENCTI_MAX_RETRIES` | `3` | Retry attempts for failures |
| `OPENCTI_RETRY_DELAY` | `1.0` | Initial retry delay (seconds) |
| `OPENCTI_RETRY_MAX_DELAY` | `30.0` | Maximum retry delay (seconds) |
| `OPENCTI_SSL_VERIFY` | `true` | Verify SSL certificates (set `false` for self-signed) |
| `OPENCTI_CIRCUIT_THRESHOLD` | `5` | Failures before circuit opens |
| `OPENCTI_CIRCUIT_TIMEOUT` | `60` | Seconds before circuit recovery |
| `OPENCTI_EXTRA_OBSERVABLE_TYPES` | - | Custom observable types (comma-separated) |
| `OPENCTI_EXTRA_PATTERN_TYPES` | - | Custom pattern types (comma-separated) |
| `OPENCTI_LOG_FORMAT` | `json` | Log format: "json" or "text" |

### Feature Flags

Control optional features via environment variables (prefix: `FF_`):

| Variable | Default | Description |
|----------|---------|-------------|
| `FF_STARTUP_VALIDATION` | `true` | Test API connectivity on server start |
| `FF_RESPONSE_CACHING` | `false` | Cache search results (reduces API calls) |
| `FF_GRACEFUL_DEGRADATION` | `true` | Return cached results when service unavailable |
| `FF_NEGATIVE_CACHING` | `true` | Cache "not found" results |

### Token Configuration

**Option 1: Environment variable (recommended for production)**
```bash
export OPENCTI_TOKEN="your-api-token"
```

**Option 2: Token file**
```bash
mkdir -p ~/.config/opencti-mcp
echo "your-api-token" > ~/.config/opencti-mcp/token
chmod 600 ~/.config/opencti-mcp/token
```

**Option 3: .env file (development)**
```
OPENCTI_TOKEN=your-api-token
```

### Custom Types for Extended OpenCTI

If your OpenCTI instance has custom observable types or pattern types (e.g., proprietary IOC formats, additional detection languages), configure them via environment variables:

```bash
# Add custom observable types (case-sensitive, comma-separated)
export OPENCTI_EXTRA_OBSERVABLE_TYPES="Internal-Host,Cloud-Resource,Custom-IOC"

# Add custom pattern types (case-insensitive, comma-separated)
export OPENCTI_EXTRA_PATTERN_TYPES="osquery,kql,custom-sig"
```

These extend the built-in allow-lists without removing standard STIX types.

### Claude Code Configuration

Add to your project-local `.mcp.json` (or see the parent [claude-ir](https://github.com/scriptedstatement/claude-ir) project for automated setup):

```json
{
  "mcpServers": {
    "opencti": {
      "command": "/path/to/venv/bin/python",
      "args": ["-m", "opencti_mcp"],
      "cwd": "/path/to/opencti-mcp",
      "env": {
        "PYTHONPATH": "/path/to/opencti-mcp/src",
        "OPENCTI_TOKEN": "your-api-token",
        "OPENCTI_URL": "http://localhost:8080",
        "OPENCTI_READ_ONLY": "true",
        "OPENCTI_SSL_VERIFY": "true"
      }
    }
  }
}
```

## Project Structure

```
opencti-mcp/
├── src/opencti_mcp/
│   ├── __init__.py       # Package exports
│   ├── __main__.py       # Entry point (with startup validation)
│   ├── server.py         # MCP server (32 tools)
│   ├── client.py         # OpenCTI API client (with caching)
│   ├── config.py         # Configuration management
│   ├── validation.py     # Input validation
│   ├── errors.py         # Error hierarchy
│   ├── logging.py        # Structured logging
│   ├── adaptive.py       # Network metrics
│   ├── cache.py          # TTL-based response caching
│   └── feature_flags.py  # Feature flag management
├── tests/                # Test suite (1530 tests)
├── docs/                 # Documentation
├── README.md             # This file
├── CLAUDE.md             # Development guide
├── IMPLEMENTATION.md     # Technical architecture
└── pyproject.toml        # Package configuration
```

## Development

### Run Tests

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run all tests
pytest

# With coverage
pytest --cov=opencti_mcp --cov-report=html

# Type checking
mypy src/opencti_mcp
```

### Test with MCP Inspector

```bash
npx @anthropic/mcp-inspector python -m opencti_mcp
```

### Key Commands

```bash
# Run MCP server
python -m opencti_mcp

# Test connection (original CLI)
python opencti_query.py "APT29" --type threat_actor

# Quick health check
python -c "from opencti_mcp import OpenCTIClient, Config; c = OpenCTIClient(Config.load()); print('OK' if c.is_available() else 'FAIL')"
```

## Production Considerations

### Local Docker vs Remote/Cloud

The default `OPENCTI_URL=http://localhost:8080` matches OpenCTI's standard Docker deployment, where the platform serves HTTP on port 8080. This is correct for local instances — traffic never leaves the machine.

For remote or cloud instances, use HTTPS. OpenCTI supports TLS either natively (`APP__HTTPS_CERT__*` env vars) or via a reverse proxy (Nginx, Caddy, Traefik) — the reverse proxy approach is more common in production.

### Recommended Settings for Remote/Cloud Instances

```bash
export OPENCTI_URL=https://opencti.example.com  # HTTPS for remote
export OPENCTI_TIMEOUT=120         # Higher for cloud (default 60 may be tight for complex queries)
export OPENCTI_MAX_RETRIES=3       # Retry on transient failures
export OPENCTI_SSL_VERIFY=true     # Always for production (false only for self-signed certs)
export OPENCTI_READ_ONLY=true      # Unless writes needed
```

> **Cloud users:** If you experience timeouts or circuit breaker trips, increase `OPENCTI_TIMEOUT` to 120-180. Complex threat intel queries on remote instances can take 60+ seconds under load.

### Adaptive Metrics

Use `get_network_status` tool to view:
- Latency statistics (P50/P95/P99)
- Success rates
- Circuit breaker state
- Recommended timeout/retry settings

## Requirements

- Python 3.10+
- OpenCTI 6.x instance
- pycti 6.x
- mcp 1.x

## Acknowledgments

Architecture and direction by Steve Anson. Implementation by Claude Code (Anthropic).

## License

MIT

TDQS

B3.4/5.0

Scored across 28 tools

Disambiguation4/5

Most tools are neatly separated by STIX entity type via the search_<entity> family, so an agent can usually pick the right one. However, search_threat_intel broadly overlaps with every specific search, and lookup_ioc vs. lookup_hash creates ambiguity for hash lookups.

Naming Consistency4/5

Tool names consistently follow a lowercase snake_case verb_noun structure, with search_<entity> dominating the set. The mix of search, lookup, get, list, and force verbs is mostly predictable, though force_reconnect and the generic search_threat_intel are minor deviations from the clearer noun patterns.

Tool Count3/5

At 28 tools this is on the heavy side, but the count is largely driven by comprehensive coverage of OpenCTI's many STIX entity types. Several operational tools like get_cache_stats and get_network_status add bulk and could be trimmed, yet each search tool has a distinct target.

Completeness4/5

For a read-only threat intelligence investigation server, the surface is quite complete: it covers major STIX entities, IOC lookup, generic entity details, relationships, recent indicators, and health/connector status. It lacks create/update/delete operations, but the tool names suggest the server is intentionally query-focused, so this is a minor gap rather than a critical one.

Maintenance

ActivityInactive
ResponsivenessNo issues