This is page 1 of 2. Use http://codebase.md/spathodea-network/opencti-mcp?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); ```