@ayazdata/trace-mcp 1.0.0

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.
Files changed (3) hide show
  1. package/README.md +75 -0
  2. package/dist/index.js +217 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # Trace MCP Server
2
+
3
+ Connect Claude Code, Codex, and Gemini to [Trace](https://trace.akpinar.dev) malware analysis platform.
4
+
5
+ ## Install — Claude Code
6
+
7
+ ```bash
8
+ claude mcp add trace \
9
+ -e TRACE_URL=https://trace.akpinar.dev \
10
+ -e TRACE_TOKEN=your_token_here \
11
+ -- npx -y trace-mcp
12
+ ```
13
+
14
+ Or for the public instance (no token required):
15
+ ```bash
16
+ claude mcp add trace -e TRACE_URL=https://trace.akpinar.dev -- npx -y trace-mcp
17
+ ```
18
+
19
+ ## Install — Codex CLI
20
+
21
+ Add to `~/.codex/config.toml`:
22
+ ```toml
23
+ [mcp_servers.trace]
24
+ command = "npx"
25
+ args = ["-y", "trace-mcp"]
26
+ startup_timeout_sec = 15
27
+
28
+ [mcp_servers.trace.env]
29
+ TRACE_URL = "https://trace.akpinar.dev"
30
+ TRACE_TOKEN = ""
31
+ ```
32
+
33
+ ## Install — Gemini CLI
34
+
35
+ Add to `~/.gemini/settings.json`:
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "trace": {
40
+ "command": "npx",
41
+ "args": ["-y", "trace-mcp"],
42
+ "env": {
43
+ "TRACE_URL": "https://trace.akpinar.dev",
44
+ "TRACE_TOKEN": ""
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Self-hosted
52
+
53
+ Replace `https://trace.akpinar.dev` with your own instance URL.
54
+
55
+ ## Tools
56
+
57
+ | Tool | Description |
58
+ |------|-------------|
59
+ | `trace_upload_file` | Upload file for malware analysis |
60
+ | `trace_get_analysis` | Full analysis results (risk score, YARA, MITRE, IOCs) |
61
+ | `trace_list_analyses` | List recent analyses |
62
+ | `trace_get_iocs` | Extract IOCs from analysis |
63
+ | `trace_get_sigma_matches` | Sigma rule matches |
64
+ | `trace_get_ai_summary` | AI-generated threat summary |
65
+ | `trace_lookup_hash` | SHA256 threat intel lookup |
66
+ | `trace_dashboard_stats` | Dashboard statistics |
67
+ | `trace_get_mitre` | Top MITRE ATT&CK techniques |
68
+ | `trace_get_process_tree` | Process execution tree |
69
+
70
+ ## Environment Variables
71
+
72
+ | Variable | Default | Description |
73
+ |----------|---------|-------------|
74
+ | `TRACE_URL` | `http://localhost:8090` | Trace instance URL |
75
+ | `TRACE_TOKEN` | _(empty)_ | JWT token for authenticated endpoints |
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { readFileSync, existsSync } from "fs";
6
+ const BASE_URL = process.env.TRACE_URL ?? "http://localhost";
7
+ const API_TOKEN = process.env.TRACE_TOKEN ?? "";
8
+ async function traceGet(path) {
9
+ const res = await fetch(`${BASE_URL}${path}`, {
10
+ headers: API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {},
11
+ });
12
+ if (!res.ok)
13
+ throw new Error(`${res.status} ${res.statusText}: ${path}`);
14
+ return res.json();
15
+ }
16
+ async function tracePost(path, body) {
17
+ const res = await fetch(`${BASE_URL}${path}`, {
18
+ method: "POST",
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...(API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {}),
22
+ },
23
+ body: JSON.stringify(body),
24
+ });
25
+ if (!res.ok)
26
+ throw new Error(`${res.status} ${res.statusText}: ${path}`);
27
+ return res.json();
28
+ }
29
+ async function uploadFile(filePath) {
30
+ if (!existsSync(filePath))
31
+ throw new Error(`File not found: ${filePath}`);
32
+ const fileBuffer = readFileSync(filePath);
33
+ const fileName = filePath.split("/").pop();
34
+ const formData = new FormData();
35
+ formData.append("file", new Blob([fileBuffer]), fileName);
36
+ const res = await fetch(`${BASE_URL}/api/v1/upload/`, {
37
+ method: "POST",
38
+ headers: API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {},
39
+ body: formData,
40
+ });
41
+ if (!res.ok)
42
+ throw new Error(`Upload failed: ${res.status}`);
43
+ return res.json();
44
+ }
45
+ const server = new Server({ name: "trace-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
46
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
47
+ tools: [
48
+ {
49
+ name: "trace_upload_file",
50
+ description: "Upload a local file to Trace for malware analysis. Returns analysis_id to track progress.",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: {
54
+ file_path: { type: "string", description: "Absolute path to the file to analyze" },
55
+ },
56
+ required: ["file_path"],
57
+ },
58
+ },
59
+ {
60
+ name: "trace_get_analysis",
61
+ description: "Get full analysis results for a given analysis ID. Includes risk score, YARA matches, MITRE techniques, IOCs, AI summary.",
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ analysis_id: { type: "string", description: "UUID of the analysis" },
66
+ },
67
+ required: ["analysis_id"],
68
+ },
69
+ },
70
+ {
71
+ name: "trace_list_analyses",
72
+ description: "List recent analyses with status and risk levels.",
73
+ inputSchema: {
74
+ type: "object",
75
+ properties: {
76
+ limit: { type: "number", description: "Max results (default 20)" },
77
+ risk_level: { type: "string", enum: ["critical", "high", "medium", "low", "clean"], description: "Filter by risk level" },
78
+ status: { type: "string", enum: ["pending", "running", "completed", "failed"], description: "Filter by status" },
79
+ },
80
+ },
81
+ },
82
+ {
83
+ name: "trace_get_iocs",
84
+ description: "Extract Indicators of Compromise (IPs, domains, hashes, URLs, registry keys) from an analysis.",
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ analysis_id: { type: "string", description: "UUID of the analysis" },
89
+ },
90
+ required: ["analysis_id"],
91
+ },
92
+ },
93
+ {
94
+ name: "trace_get_sigma_matches",
95
+ description: "Get Sigma rule matches from an analysis. Shows which detection rules triggered.",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: {
99
+ analysis_id: { type: "string", description: "UUID of the analysis" },
100
+ },
101
+ required: ["analysis_id"],
102
+ },
103
+ },
104
+ {
105
+ name: "trace_get_ai_summary",
106
+ description: "Get AI-generated threat summary for an analysis.",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ analysis_id: { type: "string", description: "UUID of the analysis" },
111
+ },
112
+ required: ["analysis_id"],
113
+ },
114
+ },
115
+ {
116
+ name: "trace_lookup_hash",
117
+ description: "Look up a SHA256 hash in threat intelligence (VirusTotal, internal DB).",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ sha256: { type: "string", description: "SHA256 hash to look up" },
122
+ },
123
+ required: ["sha256"],
124
+ },
125
+ },
126
+ {
127
+ name: "trace_dashboard_stats",
128
+ description: "Get dashboard statistics: total analyses, risk distribution, detection rates.",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {},
132
+ },
133
+ },
134
+ {
135
+ name: "trace_get_mitre",
136
+ description: "Get top MITRE ATT&CK techniques observed across all analyses.",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ limit: { type: "number", description: "Number of techniques to return (default 10)" },
141
+ },
142
+ },
143
+ },
144
+ {
145
+ name: "trace_get_process_tree",
146
+ description: "Get the process execution tree from a dynamic analysis.",
147
+ inputSchema: {
148
+ type: "object",
149
+ properties: {
150
+ analysis_id: { type: "string", description: "UUID of the analysis" },
151
+ },
152
+ required: ["analysis_id"],
153
+ },
154
+ },
155
+ ],
156
+ }));
157
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
158
+ const { name, arguments: args } = request.params;
159
+ try {
160
+ let result;
161
+ switch (name) {
162
+ case "trace_upload_file":
163
+ result = await uploadFile(args.file_path);
164
+ break;
165
+ case "trace_get_analysis":
166
+ result = await traceGet(`/api/v1/analysis/${args.analysis_id}`);
167
+ break;
168
+ case "trace_list_analyses": {
169
+ const params = new URLSearchParams();
170
+ if (args?.limit)
171
+ params.set("limit", String(args.limit));
172
+ if (args?.risk_level)
173
+ params.set("risk_level", args.risk_level);
174
+ if (args?.status)
175
+ params.set("status", args.status);
176
+ result = await traceGet(`/api/v1/analysis/?${params}`);
177
+ break;
178
+ }
179
+ case "trace_get_iocs":
180
+ result = await traceGet(`/api/v1/analysis/${args.analysis_id}/iocs`);
181
+ break;
182
+ case "trace_get_sigma_matches":
183
+ result = await traceGet(`/api/v1/analysis/${args.analysis_id}/sigma`);
184
+ break;
185
+ case "trace_get_ai_summary":
186
+ result = await traceGet(`/api/v1/analysis/${args.analysis_id}/ai-summary`);
187
+ break;
188
+ case "trace_lookup_hash":
189
+ result = await traceGet(`/api/v1/analysis/threat-intel/lookup/${args.sha256}`);
190
+ break;
191
+ case "trace_dashboard_stats":
192
+ result = await traceGet("/api/v1/dashboard/stats");
193
+ break;
194
+ case "trace_get_mitre": {
195
+ const limit = args?.limit ?? 10;
196
+ result = await traceGet(`/api/v1/dashboard/top-mitre?limit=${limit}`);
197
+ break;
198
+ }
199
+ case "trace_get_process_tree":
200
+ result = await traceGet(`/api/v1/analysis/${args.analysis_id}/process-tree`);
201
+ break;
202
+ default:
203
+ throw new Error(`Unknown tool: ${name}`);
204
+ }
205
+ return {
206
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
207
+ };
208
+ }
209
+ catch (err) {
210
+ return {
211
+ content: [{ type: "text", text: `Error: ${err.message}` }],
212
+ isError: true,
213
+ };
214
+ }
215
+ });
216
+ const transport = new StdioServerTransport();
217
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@ayazdata/trace-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Trace Malware Analysis Platform — connect Claude, Codex, and Gemini to your Trace instance",
5
+ "type": "module",
6
+ "bin": {
7
+ "trace-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "malware-analysis",
25
+ "security",
26
+ "trace",
27
+ "claude",
28
+ "ai"
29
+ ],
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.12.0"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.4.0",
36
+ "@types/node": "^20.0.0"
37
+ }
38
+ }