This is page 1 of 2. Use http://codebase.md/spathodea-network/opencti-mcp?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .env.example
├── .gitignore
├── Dockerfile
├── LICENSE
├── package.json
├── README.md
├── README.zh-TW.md
├── smithery.yaml
├── src
│ ├── index.ts
│ ├── opencti.graphql
│ └── queries
│ ├── metadata.ts
│ ├── references.ts
│ ├── relationships.ts
│ ├── reports.ts
│ ├── stix_objects.ts
│ ├── system.ts
│ └── users.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
```
# OpenCTI Configuration
OPENCTI_URL=http://localhost:8080
OPENCTI_TOKEN=your-api-token-here
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Dependencies
node_modules/
package-lock.json
# Build output
build/
dist/
# Environment files
.env
.env.local
.env.*.local
# IDE files
.vscode/
.idea/
*.iml
# Logs
logs/
*.log
npm-debug.log*
# System files
.DS_Store
Thumbs.db
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# OpenCTI MCP Server
[](https://smithery.ai/server/opencti-server)
[Traditional Chinese (繁體中文)](README.zh-TW.md)
<a href="https://glama.ai/mcp/servers/ml61kiz1gm"><img width="380" height="200" src="https://glama.ai/mcp/servers/ml61kiz1gm/badge" alt="OpenCTI Server MCP server" /></a>
## Overview
OpenCTI MCP Server is a Model Context Protocol (MCP) server that provides seamless integration with OpenCTI (Open Cyber Threat Intelligence) platform. It enables querying and retrieving threat intelligence data through a standardized interface.
## Features
- Fetch and search threat intelligence data
- Get latest reports and search by ID
- Search for malware information
- Query indicators of compromise
- Search for threat actors
- User and group management
- List all users and groups
- Get user details by ID
- STIX object operations
- List attack patterns
- Get campaign information by name
- System management
- List connectors
- View status templates
- File operations
- List all files
- Get file details by ID
- Reference data access
- List marking definitions
- View available labels
- Customizable query limits
- Full GraphQL query support
## Prerequisites
- Node.js 16 or higher
- Access to an OpenCTI instance
- OpenCTI API token
## Installation
### Installing via Smithery
To install OpenCTI Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/opencti-server):
```bash
npx -y @smithery/cli install opencti-server --client claude
```
### Manual Installation
```bash
# Clone the repository
git clone https://github.com/yourusername/opencti-mcp-server.git
# Install dependencies
cd opencti-mcp-server
npm install
# Build the project
npm run build
```
## Configuration
### Environment Variables
Copy `.env.example` to `.env` and update with your OpenCTI credentials:
```bash
cp .env.example .env
```
Required environment variables:
- `OPENCTI_URL`: Your OpenCTI instance URL
- `OPENCTI_TOKEN`: Your OpenCTI API token
### MCP Settings
Create a configuration file in your MCP settings location:
```json
{
"mcpServers": {
"opencti": {
"command": "node",
"args": ["path/to/opencti-server/build/index.js"],
"env": {
"OPENCTI_URL": "${OPENCTI_URL}", // Will be loaded from .env
"OPENCTI_TOKEN": "${OPENCTI_TOKEN}" // Will be loaded from .env
}
}
}
}
```
### Security Notes
- Never commit `.env` file or API tokens to version control
- Keep your OpenCTI credentials secure
- The `.gitignore` file is configured to exclude sensitive files
## Available Tools
## Available Tools
### Reports
#### get_latest_reports
Retrieves the most recent threat intelligence reports.
```typescript
{
"name": "get_latest_reports",
"arguments": {
"first": 10 // Optional, defaults to 10
}
}
```
#### get_report_by_id
Retrieves a specific report by its ID.
```typescript
{
"name": "get_report_by_id",
"arguments": {
"id": "report-uuid" // Required
}
}
```
### Search Operations
#### search_malware
Searches for malware information in the OpenCTI database.
```typescript
{
"name": "search_malware",
"arguments": {
"query": "ransomware",
"first": 10 // Optional, defaults to 10
}
}
```
#### search_indicators
Searches for indicators of compromise.
```typescript
{
"name": "search_indicators",
"arguments": {
"query": "domain",
"first": 10 // Optional, defaults to 10
}
}
```
#### search_threat_actors
Searches for threat actor information.
```typescript
{
"name": "search_threat_actors",
"arguments": {
"query": "APT",
"first": 10 // Optional, defaults to 10
}
}
```
### User Management
#### get_user_by_id
Retrieves user information by ID.
```typescript
{
"name": "get_user_by_id",
"arguments": {
"id": "user-uuid" // Required
}
}
```
#### list_users
Lists all users in the system.
```typescript
{
"name": "list_users",
"arguments": {}
}
```
#### list_groups
Lists all groups with their members.
```typescript
{
"name": "list_groups",
"arguments": {
"first": 10 // Optional, defaults to 10
}
}
```
### STIX Objects
#### list_attack_patterns
Lists all attack patterns in the system.
```typescript
{
"name": "list_attack_patterns",
"arguments": {
"first": 10 // Optional, defaults to 10
}
}
```
#### get_campaign_by_name
Retrieves campaign information by name.
```typescript
{
"name": "get_campaign_by_name",
"arguments": {
"name": "campaign-name" // Required
}
}
```
### System Management
#### list_connectors
Lists all system connectors.
```typescript
{
"name": "list_connectors",
"arguments": {}
}
```
#### list_status_templates
Lists all status templates.
```typescript
{
"name": "list_status_templates",
"arguments": {}
}
```
### File Operations
#### get_file_by_id
Retrieves file information by ID.
```typescript
{
"name": "get_file_by_id",
"arguments": {
"id": "file-uuid" // Required
}
}
```
#### list_files
Lists all files in the system.
```typescript
{
"name": "list_files",
"arguments": {}
}
```
### Reference Data
#### list_marking_definitions
Lists all marking definitions.
```typescript
{
"name": "list_marking_definitions",
"arguments": {}
}
```
#### list_labels
Lists all available labels.
```typescript
{
"name": "list_labels",
"arguments": {}
}
```
## Contributing
Contributions are welcome! Please feel free to submit pull requests.
## License
MIT License
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "opencti-server",
"version": "0.1.0",
"description": "A Model Context Protocol server",
"private": true,
"type": "module",
"bin": {
"opencti-server": "./build/index.js"
},
"files": [
"build"
],
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"axios": "^1.7.9"
},
"devDependencies": {
"@types/node": "^20.11.24",
"typescript": "^5.3.3"
}
}
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- openctiUrl
- openctiToken
properties:
openctiUrl:
type: string
description: The URL of the OpenCTI instance.
openctiToken:
type: string
description: The API token for the OpenCTI instance.
commandFunction:
# A function that produces the CLI command to start the MCP on stdio.
|-
(config) => ({command: 'node', args: ['build/index.js'], env: {OPENCTI_URL: config.openctiUrl, OPENCTI_TOKEN: config.openctiToken}})
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
# Use a Node.js image with the appropriate version
FROM node:16-alpine AS builder
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json for dependency installation
COPY package.json package.json
# Install dependencies
RUN npm install --ignore-scripts
# Copy the rest of the application source code
COPY . .
# Build the TypeScript project
RUN npm run build
# Prepare the runtime environment
FROM node:16-alpine AS release
# Set working directory
WORKDIR /app
# Copy built files and necessary configuration
COPY --from=builder /app/build /app/build
COPY --from=builder /app/package.json /app/package.json
# Set environment variables
ENV OPENCTI_URL=https://your-opencti-url
ENV OPENCTI_TOKEN=your-opencti-token
# Run the server
ENTRYPOINT ["node", "build/index.js"]
```
--------------------------------------------------------------------------------
/src/queries/references.ts:
--------------------------------------------------------------------------------
```typescript
export const ALL_MARKING_DEFINITIONS_QUERY = `
query AllMarkingDefinitions {
markingDefinitions {
edges {
node {
id
standard_id
entity_type
definition_type
definition
x_opencti_order
x_opencti_color
}
}
}
}
`;
export const ALL_LABELS_QUERY = `
query AllLabels {
labels {
edges {
node {
id
standard_id
entity_type
value
color
}
}
}
}
`;
export const ALL_EXTERNAL_REFERENCES_QUERY = `
query AllExternalReferences {
externalReferences {
edges {
node {
id
standard_id
entity_type
source_name
description
url
hash
external_id
}
}
}
}
`;
export const ALL_KILL_CHAIN_PHASES_QUERY = `
query AllKillChainPhases {
killChainPhases {
edges {
node {
id
standard_id
entity_type
kill_chain_name
phase_name
x_opencti_order
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/users.ts:
--------------------------------------------------------------------------------
```typescript
export const USER_BY_ID_QUERY = `
query UserById($id: String!) {
user(id: $id) {
id
standard_id
entity_type
parent_types
user_email
name
firstname
lastname
groups {
edges {
node {
id
name
}
}
}
}
}
`;
export const ALL_USERS_QUERY = `
query AllUsers {
users {
edges {
node {
id
standard_id
entity_type
user_email
name
firstname
lastname
external
created_at
updated_at
}
}
}
}
`;
export const ALL_GROUPS_QUERY = `
query AllGroups($first: Int, $after: ID) {
groups(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
standard_id
entity_type
parent_types
name
description
members(first: 5) {
edges {
node {
id
name
user_email
}
}
}
}
}
}
}
`;
export const ALL_ROLES_QUERY = `
query AllRoles {
roles {
edges {
node {
id
standard_id
entity_type
name
description
created_at
updated_at
}
}
}
}
`;
export const ALL_CAPABILITIES_QUERY = `
query AllCapabilities {
capabilities {
edges {
node {
id
standard_id
entity_type
name
description
attribute_order
created_at
updated_at
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/metadata.ts:
--------------------------------------------------------------------------------
```typescript
export const FILE_BY_ID_QUERY = `
query FileById($id: String!) {
file(id: $id) {
id
name
size
lastModified
uploadStatus
}
}
`;
export const ALL_FILES_QUERY = `
query AllFiles {
importFiles(first: 100) {
edges {
node {
id
name
size
uploadStatus
lastModified
metaData {
mimetype
version
}
}
}
}
}
`;
export const ALL_INDEXED_FILES_QUERY = `
query AllIndexedFiles {
indexedFiles {
edges {
node {
id
name
file_id
uploaded_at
entity {
id
entity_type
parent_types
standard_id
}
searchOccurrences
}
}
}
}
`;
export const ALL_LOGS_QUERY = `
query AllLogs {
logs {
edges {
node {
id
entity_type
event_type
event_scope
event_status
timestamp
user_id
user {
id
name
entity_type
}
context_uri
context_data {
entity_id
entity_name
entity_type
from_id
to_id
message
commit
external_references {
id
source_name
description
url
hash
external_id
}
}
}
}
}
}
`;
export const ALL_AUDITS_QUERY = `
query AllAudits {
audits {
edges {
node {
id
entity_type
event_type
event_scope
event_status
timestamp
user_id
user {
id
name
entity_type
}
context_uri
context_data {
entity_id
entity_name
entity_type
from_id
to_id
message
commit
external_references {
id
source_name
description
url
hash
external_id
}
}
}
}
}
}
`;
export const ALL_ATTRIBUTES_QUERY = `
query AllAttributes {
runtimeAttributes {
edges {
node {
id
key
value
}
}
}
}
`;
export const ALL_SCHEMA_ATTRIBUTE_NAMES_QUERY = `
query AllSchemaAttributeNames {
schemaAttributeNames(elementType: ["Report", "Note"]) {
edges {
node {
id
key
value
}
}
}
}
`;
export const ALL_FILTER_KEYS_SCHEMA_QUERY = `
query AllFilterKeysSchema {
filterKeysSchema {
entity_type
filters_schema {
filterKey
filterDefinition {
filterKey
label
type
multiple
subEntityTypes
elementsForFilterValuesSearch
subFilters {
filterKey
label
type
multiple
subEntityTypes
elementsForFilterValuesSearch
}
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/relationships.ts:
--------------------------------------------------------------------------------
```typescript
export const ALL_STIX_CORE_RELATIONSHIPS_QUERY = `
query AllStixCoreRelationships($first: Int, $after: ID) {
stixCoreRelationships(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
standard_id
entity_type
parent_types
relationship_type
confidence
start_time
stop_time
from {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
to {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
}
}
}
}
`;
export const ALL_STIX_SIGHTING_RELATIONSHIPS_QUERY = `
query AllStixSightingRelationships($first: Int, $after: ID) {
stixSightingRelationships(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
standard_id
entity_type
parent_types
relationship_type
confidence
first_seen
last_seen
from {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
to {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
}
}
}
}
`;
export const ALL_STIX_REF_RELATIONSHIPS_QUERY = `
query AllStixRefRelationships($first: Int, $after: ID) {
stixRefRelationships(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
standard_id
entity_type
parent_types
relationship_type
confidence
from {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
to {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
}
}
}
}
`;
export const ALL_STIX_RELATIONSHIPS_QUERY = `
query AllStixRelationships {
stixRelationships {
edges {
node {
id
standard_id
entity_type
parent_types
relationship_type
confidence
created_at
updated_at
from {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
to {
... on StixDomainObject {
id
entity_type
name
}
... on StixCyberObservable {
id
entity_type
observable_value
}
}
objectMarking {
id
definition
x_opencti_order
x_opencti_color
}
createdBy {
id
name
entity_type
}
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/reports.ts:
--------------------------------------------------------------------------------
```typescript
export const LATEST_REPORTS_QUERY = `
query LatestReport($first: Int) {
reports(
first: $first,
orderBy: created,
orderMode: desc
) {
edges {
node {
# Basic fields
id
standard_id
entity_type
parent_types
# Report specific fields
name
description
content
content_mapping
report_types
published
confidence
createdBy {
id
name
entity_type
}
objectMarking {
id
definition
x_opencti_order
x_opencti_color
}
objectLabel {
id
value
color
}
externalReferences {
edges {
node {
id
source_name
description
url
hash
external_id
}
}
}
# Relationships and objects
objects(first: 500) {
edges {
node {
... on StixDomainObject {
id
entity_type
parent_types
created
updated_at
standard_id
created
revoked
confidence
lang
status {
id
template {
name
color
}
}
}
... on StixCyberObservable {
id
entity_type
parent_types
observable_value
x_opencti_description
x_opencti_score
}
... on StixCoreRelationship {
id
entity_type
parent_types
relationship_type
description
start_time
stop_time
from {
... on StixDomainObject {
id
entity_type
parent_types
created_at
standard_id
}
}
to {
... on StixDomainObject {
id
entity_type
parent_types
created_at
standard_id
}
}
}
}
}
}
# Additional metadata
created
modified
created_at
updated_at
x_opencti_stix_ids
status {
id
template {
name
color
}
}
workflowEnabled
# Container specific fields
containersNumber {
total
count
}
containers {
edges {
node {
id
entity_type
parent_types
created_at
standard_id
}
}
}
}
}
}
}
`;
export const SEARCH_MALWARE_QUERY = `
query Malware($search: String, $first: Int) {
stixCoreObjects(
search: $search,
first: $first,
types: ["Malware"]
) {
edges {
node {
... on Malware {
id
name
description
created
modified
malware_types
is_family
first_seen
last_seen
}
}
}
}
}
`;
export const SEARCH_INDICATORS_QUERY = `
query Indicators($search: String, $first: Int) {
stixCoreObjects(
search: $search,
first: $first,
types: ["Indicator"]
) {
edges {
node {
... on Indicator {
id
name
description
created_at
pattern
valid_from
valid_until
x_opencti_score
}
}
}
}
}
`;
export const SEARCH_THREAT_ACTORS_QUERY = `
query ThreatActors($search: String, $first: Int) {
stixCoreObjects(
search: $search,
first: $first,
types: ["ThreatActorGroup"]
) {
edges {
node {
... on ThreatActorGroup {
id
name
description
created
modified
threat_actor_types
first_seen
last_seen
sophistication
resource_level
roles
goals
}
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/system.ts:
--------------------------------------------------------------------------------
```typescript
export const ALL_CONNECTORS_QUERY = `
query AllConnectors {
connectors {
id
name
active
auto
only_contextual
playbook_compatible
connector_type
connector_scope
connector_state
connector_schema
connector_schema_ui
connector_state_reset
connector_user_id
updated_at
created_at
config {
connection {
host
vhost
use_ssl
port
user
pass
}
listen
listen_routing
listen_exchange
push
push_routing
push_exchange
}
works {
id
name
status
}
}
}
`;
export const ALL_STATUS_TEMPLATES_QUERY = `
query AllStatusTemplates {
statusTemplates {
edges {
node {
id
name
color
editContext {
name
focusOn
}
usages
}
}
}
}
`;
export const ALL_STATUSES_QUERY = `
query AllStatuses {
statuses {
edges {
node {
id
template_id
template {
id
name
color
}
type
order
disabled
}
}
}
}
`;
export const ALL_SUB_TYPES_QUERY = `
query AllSubTypes {
subTypes {
edges {
node {
id
label
statuses {
id
template {
id
name
color
}
type
order
disabled
}
workflowEnabled
settings {
id
entity_type
parent_types
standard_id
}
}
}
}
}
`;
export const ALL_RETENTION_RULES_QUERY = `
query AllRetentionRules {
retentionRules {
edges {
node {
id
standard_id
name
filters
max_retention
retention_unit
last_execution_date
last_deleted_count
remaining_count
scope
}
}
}
}
`;
export const ALL_BACKGROUND_TASKS_QUERY = `
query AllBackgroundTasks {
backgroundTasks {
edges {
node {
id
type
initiator {
id
name
entity_type
}
actions {
type
context {
field
type
values
}
}
created_at
last_execution_date
completed
task_expected_number
task_processed_number
errors {
id
timestamp
message
}
}
}
}
}
`;
export const ALL_FEEDS_QUERY = `
query AllFeeds {
feeds {
edges {
node {
id
standard_id
name
description
filters
separator
rolling_time
feed_date_attribute
include_header
feed_types
feed_attributes {
attribute
mappings {
type
attribute
}
}
feed_public
authorized_members {
id
name
entity_type
access_right
}
}
}
}
}
`;
export const ALL_TAXII_COLLECTIONS_QUERY = `
query AllTaxiiCollections {
taxiiCollections {
edges {
node {
id
name
description
filters
include_inferences
score_to_confidence
taxii_public
authorized_members {
id
name
entity_type
access_right
}
}
}
}
}
`;
export const ALL_STREAM_COLLECTIONS_QUERY = `
query AllStreamCollections {
streamCollections {
edges {
node {
id
name
description
filters
stream_live
stream_public
authorized_members {
id
name
entity_type
access_right
}
}
}
}
}
`;
export const ALL_RULES_QUERY = `
query AllRules {
rules {
id
name
description
activated
category
display {
if {
source
source_color
relation
target
target_color
identifier
identifier_color
action
}
then {
source
source_color
relation
target
target_color
identifier
identifier_color
action
}
}
}
}
`;
export const ALL_SYNCHRONIZERS_QUERY = `
query AllSynchronizers {
synchronizers {
edges {
node {
id
name
uri
token
stream_id
user {
id
name
entity_type
}
running
current_state_date
listen_deletion
no_dependencies
ssl_verify
synchronized
queue_messages
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/queries/stix_objects.ts:
--------------------------------------------------------------------------------
```typescript
export const REPORT_BY_ID_QUERY = `
query ReportById($id: String!) {
report(id: $id) {
id
standard_id
entity_type
parent_types
name
description
content
content_mapping
report_types
published
confidence
createdBy {
id
name
entity_type
}
objectMarking {
id
definition
x_opencti_order
x_opencti_color
}
objectLabel {
id
value
color
}
externalReferences {
edges {
node {
id
source_name
description
url
hash
external_id
}
}
}
objects(first: 500) {
edges {
node {
... on StixDomainObject {
id
entity_type
parent_types
created
updated_at
standard_id
created
revoked
confidence
lang
status {
id
template {
name
color
}
}
}
... on StixCyberObservable {
id
entity_type
parent_types
observable_value
x_opencti_description
x_opencti_score
}
... on StixCoreRelationship {
id
entity_type
parent_types
relationship_type
description
start_time
stop_time
from {
... on StixDomainObject {
id
entity_type
parent_types
created_at
standard_id
}
}
to {
... on StixDomainObject {
id
entity_type
parent_types
created_at
standard_id
}
}
}
}
}
}
created
modified
created_at
updated_at
x_opencti_stix_ids
status {
id
template {
name
color
}
}
workflowEnabled
containersNumber {
total
count
}
containers {
edges {
node {
id
entity_type
parent_types
created_at
standard_id
}
}
}
}
}
`;
export const ALL_ATTACK_PATTERNS_QUERY = `
query AllAttackPatterns($first: Int, $after: ID) {
attackPatterns(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
standard_id
entity_type
parent_types
name
description
x_mitre_id
killChainPhases {
id
kill_chain_name
phase_name
}
coursesOfAction {
edges {
node {
id
name
description
}
}
}
}
}
}
}
`;
export const CAMPAIGN_BY_NAME_QUERY = `
query CampaignByName($name: Any!) {
campaigns(
first: 1,
filters: {
mode: and,
filters: [
{
key: "name",
values: [$name],
operator: eq,
mode: or
}
],
filterGroups: []
}
) {
edges {
node {
id
standard_id
entity_type
parent_types
name
description
first_seen
last_seen
created
modified
created_at
updated_at
}
}
}
}
`;
export const ALL_STIX_CORE_OBJECTS_QUERY = `
query AllStixCoreObjects {
stixCoreObjects {
edges {
node {
id
standard_id
entity_type
parent_types
representative {
main
secondary
}
x_opencti_stix_ids
is_inferred
spec_version
created_at
updated_at
createdBy {
id
name
entity_type
}
numberOfConnectedElement
objectMarking {
id
definition
x_opencti_order
x_opencti_color
}
objectOrganization {
id
name
}
objectLabel {
id
value
color
}
externalReferences {
edges {
node {
id
source_name
description
url
hash
external_id
}
}
}
containersNumber {
total
count
}
containers {
edges {
node {
id
entity_type
parent_types
created_at
standard_id
}
}
}
reports {
edges {
node {
id
name
}
}
}
notes {
edges {
node {
id
content
}
}
}
opinions {
edges {
node {
id
opinion
}
}
}
observedData {
edges {
node {
id
first_observed
last_observed
number_observed
}
}
}
groupings {
edges {
node {
id
name
}
}
}
cases {
edges {
node {
id
name
}
}
}
}
}
}
}
`;
export const ALL_STIX_DOMAIN_OBJECTS_QUERY = `
query AllStixDomainObjects {
stixDomainObjects {
edges {
node {
id
standard_id
entity_type
parent_types
representative {
main
secondary
}
x_opencti_stix_ids
is_inferred
spec_version
created_at
updated_at
createdBy {
id
name
entity_type
}
numberOfConnectedElement
objectMarking {
id
definition
x_opencti_order
x_opencti_color
}
objectOrganization {
id
name
}
objectLabel {
id
value
color
}
externalReferences {
edges {
node {
id
source_name
description
url
hash
external_id
}
}
}
containersNumber {
total
count
}
containers {
edges {
node {
id
entity_type
parent_types
created_at
standard_id
}
}
}
reports {
edges {
node {
id
name
}
}
}
notes {
edges {
node {
id
content
}
}
}
opinions {
edges {
node {
id
opinion
}
}
}
observedData {
edges {
node {
id
first_observed
last_observed
number_observed
}
}
}
groupings {
edges {
node {
id
name
}
}
}
cases {
edges {
node {
id
name
}
}
}
revoked
confidence
lang
created
modified
x_opencti_graph_data
objectAssignee {
id
name
entity_type
}
objectParticipant {
id
name
entity_type
}
status {
id
template {
name
color
}
}
workflowEnabled
}
}
}
}
`;
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import {
LATEST_REPORTS_QUERY,
SEARCH_MALWARE_QUERY,
SEARCH_INDICATORS_QUERY,
SEARCH_THREAT_ACTORS_QUERY,
} from './queries/reports.js';
import {
USER_BY_ID_QUERY,
ALL_USERS_QUERY,
ALL_GROUPS_QUERY,
ALL_ROLES_QUERY,
ALL_CAPABILITIES_QUERY,
} from './queries/users.js';
import {
REPORT_BY_ID_QUERY,
ALL_ATTACK_PATTERNS_QUERY,
CAMPAIGN_BY_NAME_QUERY,
ALL_STIX_CORE_OBJECTS_QUERY,
ALL_STIX_DOMAIN_OBJECTS_QUERY,
} from './queries/stix_objects.js';
import {
ALL_STIX_CORE_RELATIONSHIPS_QUERY,
ALL_STIX_SIGHTING_RELATIONSHIPS_QUERY,
ALL_STIX_REF_RELATIONSHIPS_QUERY,
ALL_STIX_RELATIONSHIPS_QUERY,
} from './queries/relationships.js';
import {
ALL_CONNECTORS_QUERY,
ALL_STATUS_TEMPLATES_QUERY,
ALL_STATUSES_QUERY,
ALL_SUB_TYPES_QUERY,
ALL_RETENTION_RULES_QUERY,
ALL_BACKGROUND_TASKS_QUERY,
ALL_FEEDS_QUERY,
ALL_TAXII_COLLECTIONS_QUERY,
ALL_STREAM_COLLECTIONS_QUERY,
ALL_RULES_QUERY,
ALL_SYNCHRONIZERS_QUERY,
} from './queries/system.js';
import {
FILE_BY_ID_QUERY,
ALL_FILES_QUERY,
ALL_INDEXED_FILES_QUERY,
ALL_LOGS_QUERY,
ALL_AUDITS_QUERY,
ALL_ATTRIBUTES_QUERY,
ALL_SCHEMA_ATTRIBUTE_NAMES_QUERY,
ALL_FILTER_KEYS_SCHEMA_QUERY,
} from './queries/metadata.js';
import {
ALL_MARKING_DEFINITIONS_QUERY,
ALL_LABELS_QUERY,
ALL_EXTERNAL_REFERENCES_QUERY,
ALL_KILL_CHAIN_PHASES_QUERY,
} from './queries/references.js';
const OPENCTI_URL = process.env.OPENCTI_URL || 'http://localhost:8080';
const OPENCTI_TOKEN = process.env.OPENCTI_TOKEN;
if (!OPENCTI_TOKEN) {
throw new Error('OPENCTI_TOKEN environment variable is required');
}
interface OpenCTIResponse {
data: {
stixObjects: Array<{
id: string;
name?: string;
description?: string;
created_at?: string;
modified_at?: string;
pattern?: string;
valid_from?: string;
valid_until?: string;
x_opencti_score?: number;
[key: string]: any;
}>;
};
}
class OpenCTIServer {
private server: Server;
private axiosInstance;
constructor() {
this.server = new Server(
{
name: 'opencti-server',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
this.axiosInstance = axios.create({
baseURL: OPENCTI_URL,
headers: {
'Authorization': `Bearer ${OPENCTI_TOKEN}`,
'Content-Type': 'application/json',
},
});
this.setupTools();
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupTools() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
// Reports
{
name: 'get_latest_reports',
description: '獲取最新的OpenCTI報告',
inputSchema: {
type: 'object',
properties: {
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
},
},
{
name: 'get_report_by_id',
description: '根據ID獲取OpenCTI報告',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: '報告ID',
},
},
required: ['id'],
},
},
// Search
{
name: 'search_indicators',
description: '搜尋OpenCTI中的指標',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜尋關鍵字',
},
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
required: ['query'],
},
},
{
name: 'search_malware',
description: '搜尋OpenCTI中的惡意程式',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜尋關鍵字',
},
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
required: ['query'],
},
},
{
name: 'search_threat_actors',
description: '搜尋OpenCTI中的威脅行為者',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜尋關鍵字',
},
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
required: ['query'],
},
},
// Users & Groups
{
name: 'get_user_by_id',
description: '根據ID獲取使用者資訊',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: '使用者ID',
},
},
required: ['id'],
},
},
{
name: 'list_users',
description: '列出所有使用者',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'list_groups',
description: '列出所有群組',
inputSchema: {
type: 'object',
properties: {
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
},
},
// STIX Objects
{
name: 'list_attack_patterns',
description: '列出所有攻擊模式',
inputSchema: {
type: 'object',
properties: {
first: {
type: 'number',
description: '返回結果數量限制',
default: 10,
},
},
},
},
{
name: 'get_campaign_by_name',
description: '根據名稱獲取行動資訊',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: '行動名稱',
},
},
required: ['name'],
},
},
// System
{
name: 'list_connectors',
description: '列出所有連接器',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'list_status_templates',
description: '列出所有狀態模板',
inputSchema: {
type: 'object',
properties: {},
},
},
// Files
{
name: 'get_file_by_id',
description: '根據ID獲取檔案資訊',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: '檔案ID',
},
},
required: ['id'],
},
},
{
name: 'list_files',
description: '列出所有檔案',
inputSchema: {
type: 'object',
properties: {},
},
},
// References
{
name: 'list_marking_definitions',
description: '列出所有標記定義',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'list_labels',
description: '列出所有標籤',
inputSchema: {
type: 'object',
properties: {},
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
let query = '';
let variables: any = {};
switch (request.params.name) {
// Reports
case 'get_latest_reports':
query = LATEST_REPORTS_QUERY;
variables = {
first: typeof request.params.arguments?.first === 'number' ? request.params.arguments.first : 10,
};
break;
case 'get_report_by_id':
if (!request.params.arguments?.id) {
throw new McpError(ErrorCode.InvalidParams, 'Report ID is required');
}
query = REPORT_BY_ID_QUERY;
variables = { id: request.params.arguments.id };
break;
// Search
case 'search_indicators':
if (!request.params.arguments?.query) {
throw new McpError(ErrorCode.InvalidParams, 'Query parameter is required');
}
query = SEARCH_INDICATORS_QUERY;
variables = {
search: request.params.arguments.query,
first: typeof request.params.arguments.first === 'number' ? request.params.arguments.first : 10,
};
break;
case 'search_malware':
if (!request.params.arguments?.query) {
throw new McpError(ErrorCode.InvalidParams, 'Query parameter is required');
}
query = SEARCH_MALWARE_QUERY;
variables = {
search: request.params.arguments.query,
first: typeof request.params.arguments.first === 'number' ? request.params.arguments.first : 10,
};
break;
case 'search_threat_actors':
if (!request.params.arguments?.query) {
throw new McpError(ErrorCode.InvalidParams, 'Query parameter is required');
}
query = SEARCH_THREAT_ACTORS_QUERY;
variables = {
search: request.params.arguments.query,
first: typeof request.params.arguments.first === 'number' ? request.params.arguments.first : 10,
};
break;
// Users & Groups
case 'get_user_by_id':
if (!request.params.arguments?.id) {
throw new McpError(ErrorCode.InvalidParams, 'User ID is required');
}
query = USER_BY_ID_QUERY;
variables = { id: request.params.arguments.id };
break;
case 'list_users':
query = ALL_USERS_QUERY;
break;
case 'list_groups':
query = ALL_GROUPS_QUERY;
variables = {
first: typeof request.params.arguments?.first === 'number' ? request.params.arguments.first : 10,
};
break;
// STIX Objects
case 'list_attack_patterns':
query = ALL_ATTACK_PATTERNS_QUERY;
variables = {
first: typeof request.params.arguments?.first === 'number' ? request.params.arguments.first : 10,
};
break;
case 'get_campaign_by_name':
if (!request.params.arguments?.name) {
throw new McpError(ErrorCode.InvalidParams, 'Campaign name is required');
}
query = CAMPAIGN_BY_NAME_QUERY;
variables = { name: request.params.arguments.name };
break;
// System
case 'list_connectors':
query = ALL_CONNECTORS_QUERY;
break;
case 'list_status_templates':
query = ALL_STATUS_TEMPLATES_QUERY;
break;
// Files
case 'get_file_by_id':
if (!request.params.arguments?.id) {
throw new McpError(ErrorCode.InvalidParams, 'File ID is required');
}
query = FILE_BY_ID_QUERY;
variables = { id: request.params.arguments.id };
break;
case 'list_files':
query = ALL_FILES_QUERY;
break;
// References
case 'list_marking_definitions':
query = ALL_MARKING_DEFINITIONS_QUERY;
break;
case 'list_labels':
query = ALL_LABELS_QUERY;
break;
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
}
const response = await this.axiosInstance.post('/graphql', {
query,
variables,
});
console.error('OpenCTI Response:', JSON.stringify(response.data, null, 2));
if (!response.data?.data) {
throw new McpError(
ErrorCode.InternalError,
`Invalid response format from OpenCTI: ${JSON.stringify(response.data)}`
);
}
let formattedResponse;
switch (request.params.name) {
case 'get_latest_reports':
formattedResponse = response.data.data.reports.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
description: edge.node.description || '',
content: edge.node.content || '',
published: edge.node.published,
confidence: edge.node.confidence,
created: edge.node.created,
modified: edge.node.modified,
reportTypes: edge.node.report_types || [],
}));
break;
case 'get_report_by_id':
formattedResponse = {
...response.data.data.report,
name: response.data.data.report.name || 'Unnamed',
description: response.data.data.report.description || '',
};
break;
case 'search_indicators':
case 'search_malware':
case 'search_threat_actors':
formattedResponse = response.data.data.stixCoreObjects.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
description: edge.node.description || '',
created: edge.node.created,
modified: edge.node.modified,
type: edge.node.malware_types?.join(', ') || edge.node.threat_actor_types?.join(', ') || '',
family: edge.node.is_family ? 'Yes' : 'No',
firstSeen: edge.node.first_seen || '',
lastSeen: edge.node.last_seen || '',
pattern: edge.node.pattern || '',
validFrom: edge.node.valid_from || '',
validUntil: edge.node.valid_until || '',
score: edge.node.x_opencti_score,
}));
break;
case 'get_user_by_id':
formattedResponse = {
...response.data.data.user,
name: response.data.data.user.name || 'Unnamed',
};
break;
case 'list_users':
formattedResponse = response.data.data.users.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
email: edge.node.user_email,
firstname: edge.node.firstname,
lastname: edge.node.lastname,
created: edge.node.created_at,
modified: edge.node.updated_at,
}));
break;
case 'list_groups':
formattedResponse = response.data.data.groups.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
description: edge.node.description || '',
members: edge.node.members?.edges?.map((memberEdge: any) => ({
id: memberEdge.node.id,
name: memberEdge.node.name,
email: memberEdge.node.user_email,
})) || [],
}));
break;
case 'list_attack_patterns':
formattedResponse = response.data.data.attackPatterns.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
description: edge.node.description || '',
created: edge.node.created_at,
modified: edge.node.updated_at,
killChainPhases: edge.node.killChainPhases?.edges?.map((phaseEdge: any) => ({
id: phaseEdge.node.id,
name: phaseEdge.node.phase_name,
})) || [],
}));
break;
case 'list_connectors':
formattedResponse = response.data.data.connectors.map((connector: any) => ({
id: connector.id,
name: connector.name || 'Unnamed',
type: connector.connector_type,
scope: connector.connector_scope,
state: connector.connector_state,
active: connector.active,
updated: connector.updated_at,
created: connector.created_at,
}));
break;
case 'list_status_templates':
formattedResponse = response.data.data.statusTemplates.edges.map((edge: any) => ({
id: edge.node.id,
name: edge.node.name || 'Unnamed',
color: edge.node.color,
usages: edge.node.usages,
}));
break;
case 'list_marking_definitions':
formattedResponse = response.data.data.markingDefinitions.edges.map((edge: any) => ({
id: edge.node.id,
definition: edge.node.definition,
color: edge.node.x_opencti_color,
order: edge.node.x_opencti_order,
}));
break;
case 'list_labels':
formattedResponse = response.data.data.labels.edges.map((edge: any) => ({
id: edge.node.id,
value: edge.node.value,
color: edge.node.color,
}));
break;
default:
formattedResponse = response.data.data;
}
return {
content: [{
type: 'text',
text: JSON.stringify(formattedResponse, null, 2)
}]
};
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Axios Error:', error.response?.data);
return {
content: [{
type: 'text',
text: `OpenCTI API error: ${JSON.stringify(error.response?.data) || error.message}`
}],
isError: true,
};
}
throw error;
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('OpenCTI MCP server running on stdio');
}
}
const server = new OpenCTIServer();
server.run().catch(console.error);
```