tracecat-mcp-community 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.
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ export function registerCaseTools(server, client) {
3
+ server.tool("tracecat_list_cases", "List all cases in the current workspace", {
4
+ limit: z.number().optional().describe("Maximum number of results"),
5
+ status: z.string().optional().describe("Filter by status (new, in_progress, resolved, closed)"),
6
+ }, async ({ limit, status }) => {
7
+ const params = {};
8
+ if (limit)
9
+ params.limit = limit.toString();
10
+ if (status)
11
+ params.status = status;
12
+ const cases = await client.get("/cases", params);
13
+ return { content: [{ type: "text", text: JSON.stringify(cases, null, 2) }] };
14
+ });
15
+ server.tool("tracecat_create_case", "Create a new case", {
16
+ workflow_id: z.string().describe("Workflow ID to associate with"),
17
+ case_title: z.string().describe("Title of the case"),
18
+ payload: z.record(z.unknown()).optional().describe("Case payload data"),
19
+ malice: z.string().optional().describe("Malice level (benign, malicious, unknown)"),
20
+ status: z.string().optional().describe("Initial status"),
21
+ priority: z.string().optional().describe("Priority level (low, medium, high, critical)"),
22
+ action: z.string().optional().describe("Recommended action"),
23
+ }, async ({ workflow_id, case_title, payload, malice, status, priority, action }) => {
24
+ const body = {
25
+ workflow_id,
26
+ case_title,
27
+ payload: payload ?? {},
28
+ malice: malice ?? "unknown",
29
+ status: status ?? "new",
30
+ priority: priority ?? "medium",
31
+ action: action ?? "",
32
+ };
33
+ const result = await client.post("/cases", body);
34
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
35
+ });
36
+ server.tool("tracecat_update_case", "Update an existing case", {
37
+ case_id: z.string().describe("Case ID"),
38
+ case_title: z.string().optional().describe("New title"),
39
+ status: z.string().optional().describe("New status"),
40
+ priority: z.string().optional().describe("New priority"),
41
+ malice: z.string().optional().describe("New malice level"),
42
+ action: z.string().optional().describe("New action"),
43
+ }, async ({ case_id, ...updates }) => {
44
+ const body = Object.fromEntries(Object.entries(updates).filter(([, v]) => v !== undefined));
45
+ const result = await client.patch(`/cases/${case_id}`, body);
46
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
47
+ });
48
+ server.tool("tracecat_add_comment", "Add a comment to a case", {
49
+ case_id: z.string().describe("Case ID"),
50
+ content: z.string().describe("Comment content"),
51
+ }, async ({ case_id, content }) => {
52
+ const result = await client.post(`/cases/${case_id}/comments`, { content });
53
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
54
+ });
55
+ server.tool("tracecat_get_case", "Get details of a specific case by ID", {
56
+ case_id: z.string().describe("Case ID"),
57
+ }, async ({ case_id }) => {
58
+ const caseData = await client.get(`/cases/${case_id}`);
59
+ return { content: [{ type: "text", text: JSON.stringify(caseData, null, 2) }] };
60
+ });
61
+ server.tool("tracecat_delete_case", "Delete a case permanently", {
62
+ case_id: z.string().describe("Case ID"),
63
+ }, async ({ case_id }) => {
64
+ const result = await client.delete(`/cases/${case_id}`);
65
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
66
+ });
67
+ server.tool("tracecat_list_comments", "List all comments on a case", {
68
+ case_id: z.string().describe("Case ID"),
69
+ }, async ({ case_id }) => {
70
+ const comments = await client.get(`/cases/${case_id}/comments`);
71
+ return { content: [{ type: "text", text: JSON.stringify(comments, null, 2) }] };
72
+ });
73
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TracecatClient } from "../client.js";
3
+ export declare function registerDocTools(server: McpServer, _client: TracecatClient): void;
@@ -0,0 +1,389 @@
1
+ import { z } from "zod";
2
+ const DOCS = {
3
+ action_types: `# Tracecat Action Types Reference
4
+
5
+ ## Core Actions
6
+ | Type | Description | Key Inputs |
7
+ |------|-------------|------------|
8
+ | \`core.transform.reshape\` | Transform/reshape data | \`value\`: any expression |
9
+ | \`core.http_request\` | Make HTTP requests | \`url\`, \`method\`, \`headers\`, \`payload\` |
10
+ | \`core.send_email\` | Send email via SMTP | \`recipients\`, \`subject\`, \`body\` |
11
+ | \`core.workflow.execute\` | Execute another workflow | \`workflow_id\`, \`trigger_inputs\` |
12
+ | \`core.script.run_python\` | Run Python script | \`script\` (NOT \`code\`), \`inputs\`, \`dependencies\`, \`timeout_seconds\`, \`allow_network\` |
13
+ | \`core.cases.create_case\` | Create a case | \`workflow_id\`, \`case_title\`, \`payload\`, \`malice\`, \`status\`, \`priority\`, \`action\` |
14
+ | \`core.cases.update_case\` | Update a case | \`case_id\`, \`status\`, \`priority\`, \`malice\`, \`action\` |
15
+ | \`core.cases.create_comment\` | Add comment to case | \`case_id\`, \`content\` |
16
+
17
+ ## Integration Actions (tools.*)
18
+ Format: \`tools.<provider>.<action>\`
19
+
20
+ Examples:
21
+ - \`tools.virustotal.get_file_report\` — VirusTotal file analysis
22
+ - \`tools.crowdstrike.list_detections\` — CrowdStrike detections
23
+ - \`tools.slack.post_message\` — Slack notification
24
+ - \`tools.abuseipdb.check_ip\` — AbuseIPDB lookup
25
+ - \`tools.greynoise.lookup_ip\` — GreyNoise IP context
26
+
27
+ ## IMPORTANT
28
+ - HTTP request type is \`core.http_request\` (underscore, NOT \`core.http.request\`)
29
+ - Python script field is \`script\` (NOT \`code\`)
30
+ - Always use native action types (core.cases.*) before falling back to core.http_request`,
31
+ expressions: `# Tracecat Expressions Reference
32
+
33
+ ## Syntax
34
+ All expressions use: \`\${{ CONTEXT.path }}\`
35
+
36
+ ## Contexts
37
+ | Context | Description | Example |
38
+ |---------|-------------|---------|
39
+ | \`TRIGGER\` | Webhook/schedule input data | \`\${{ TRIGGER.alert.source_ip }}\` |
40
+ | \`ACTIONS\` | Results from previous actions | \`\${{ ACTIONS.my_action.result }}\` |
41
+ | \`ACTIONS.*.error\` | Error from failed action | \`\${{ ACTIONS.my_action.error }}\` |
42
+ | \`SECRETS\` | Secret values | \`\${{ SECRETS.my_secret.API_KEY }}\` |
43
+ | \`ENV\` | Environment variables | \`\${{ ENV.MY_VAR }}\` |
44
+ | \`FN\` | Built-in functions | \`\${{ FN.is_empty(TRIGGER.data) }}\` |
45
+
46
+ ## Nested Access
47
+ - Dot notation: \`\${{ ACTIONS.enrich.result.data.score }}\`
48
+ - Dynamic context: \`\${{ ACTIONS.enrich.result }}\` returns full result object
49
+
50
+ ## In YAML Inputs
51
+ \`\`\`yaml
52
+ url: https://api.example.com/\${{ TRIGGER.ip }}
53
+ headers:
54
+ Authorization: Bearer \${{ SECRETS.api_creds.TOKEN }}
55
+ payload:
56
+ ip: \${{ TRIGGER.source_ip }}
57
+ previous_score: \${{ ACTIONS.score_ip.result.score }}
58
+ \`\`\`
59
+
60
+ ## In Control Flow (run_if)
61
+ \`\`\`yaml
62
+ # Truthy check (NOT == true)
63
+ run_if: \${{ ACTIONS.check_ip.result }}
64
+ # Falsy check (NOT == false)
65
+ run_if: \${{ not ACTIONS.check_ip.result }}
66
+ # Comparison
67
+ run_if: \${{ ACTIONS.score.result.score > 70 }}
68
+ # Logical operators: && and || (NOT 'and'/'or')
69
+ run_if: \${{ ACTIONS.is_malicious.result && ACTIONS.is_public.result }}
70
+ \`\`\``,
71
+ functions: `# Tracecat Built-in Functions (FN.*)
72
+
73
+ ## String Functions
74
+ | Function | Description | Example |
75
+ |----------|-------------|---------|
76
+ | \`FN.upper(s)\` | Uppercase | \`\${{ FN.upper(TRIGGER.name) }}\` |
77
+ | \`FN.lower(s)\` | Lowercase | \`\${{ FN.lower(TRIGGER.name) }}\` |
78
+ | \`FN.contains(s, sub)\` | Check substring | \`\${{ FN.contains(TRIGGER.url, "malware") }}\` |
79
+ | \`FN.format(fmt, ...)\` | Format string | \`\${{ FN.format("IP: {}", TRIGGER.ip) }}\` |
80
+ | \`FN.regex_match(pattern, s)\` | Regex match | \`\${{ FN.regex_match("\\\\d+", TRIGGER.text) }}\` |
81
+ | \`FN.regex_not_match(pattern, s)\` | Regex no match | \`\${{ FN.regex_not_match("safe", TRIGGER.text) }}\` |
82
+
83
+ ## Type / Conversion Functions
84
+ | Function | Description | Example |
85
+ |----------|-------------|---------|
86
+ | \`FN.is_empty(v)\` | Check if null/empty | \`\${{ FN.is_empty(ACTIONS.x.result) }}\` |
87
+ | \`FN.is_not_empty(v)\` | Check if not empty | \`\${{ FN.is_not_empty(ACTIONS.x.result) }}\` |
88
+ | \`FN.serialize_json(v)\` | Object → JSON string | \`\${{ FN.serialize_json(ACTIONS.x.result) }}\` |
89
+ | \`FN.deserialize_json(s)\` | JSON string → object | \`\${{ FN.deserialize_json(TRIGGER.raw) }}\` |
90
+ | \`FN.to_int(v)\` | Convert to integer | \`\${{ FN.to_int(TRIGGER.count) }}\` |
91
+ | \`FN.to_float(v)\` | Convert to float | \`\${{ FN.to_float(TRIGGER.score) }}\` |
92
+
93
+ ## Network Functions
94
+ | Function | Description | Example |
95
+ |----------|-------------|---------|
96
+ | \`FN.ipv4_is_public(ip)\` | Check if IPv4 is public | \`\${{ FN.ipv4_is_public(TRIGGER.ip) }}\` |
97
+ | \`FN.ipv4_in_subnet(ip, cidr)\` | Check if in subnet | \`\${{ FN.ipv4_in_subnet(TRIGGER.ip, "10.0.0.0/8") }}\` |
98
+
99
+ ## Collection Functions
100
+ | Function | Description | Example |
101
+ |----------|-------------|---------|
102
+ | \`FN.length(list)\` | Length of list | \`\${{ FN.length(ACTIONS.x.result) }}\` |
103
+ | \`FN.flatten(list)\` | Flatten nested list | \`\${{ FN.flatten(ACTIONS.x.result) }}\` |
104
+ | \`FN.unique(list)\` | Deduplicate list | \`\${{ FN.unique(ACTIONS.x.result) }}\` |
105
+ | \`FN.join(list, sep)\` | Join list to string | \`\${{ FN.join(ACTIONS.x.result, ", ") }}\` |
106
+
107
+ ## Math Functions
108
+ | Function | Description | Example |
109
+ |----------|-------------|---------|
110
+ | \`FN.add(a, b)\` | Addition | \`\${{ FN.add(ACTIONS.x.result, 1) }}\` |
111
+ | \`FN.sub(a, b)\` | Subtraction | \`\${{ FN.sub(ACTIONS.x.result, 1) }}\` |
112
+ | \`FN.less_than(a, b)\` | a < b | \`\${{ FN.less_than(ACTIONS.x.result, 50) }}\` |
113
+ | \`FN.greater_than(a, b)\` | a > b | \`\${{ FN.greater_than(ACTIONS.x.result, 50) }}\` |
114
+
115
+ ## Conditional
116
+ | Function | Description | Example |
117
+ |----------|-------------|---------|
118
+ | \`FN.conditional(cond, a, b)\` | Ternary | \`\${{ FN.conditional(ACTIONS.x.result, "yes", "no") }}\` |`,
119
+ control_flow: `# Tracecat Control Flow Reference
120
+
121
+ ## Conditional Execution (run_if)
122
+ Only execute an action if the condition is truthy.
123
+
124
+ \`\`\`yaml
125
+ # In action update control_flow:
126
+ control_flow:
127
+ run_if: \${{ ACTIONS.check_ip.result }}
128
+ \`\`\`
129
+
130
+ ### Important Rules
131
+ - Do NOT use \`== true\` or \`== false\`
132
+ - Use truthy: \`\${{ ACTIONS.x.result }}\`
133
+ - Use falsy: \`\${{ not ACTIONS.x.result }}\`
134
+ - Logical AND: \`\${{ A && B }}\` (NOT \`and\`)
135
+ - Logical OR: \`\${{ A || B }}\` (NOT \`or\`)
136
+ - Comparisons: \`\${{ ACTIONS.score.result > 70 }}\`
137
+
138
+ ## Loops (for_each)
139
+ Iterate over a list, executing the action once per item.
140
+
141
+ \`\`\`yaml
142
+ control_flow:
143
+ for_each: \${{ ACTIONS.list_ips.result }}
144
+ \`\`\`
145
+
146
+ The current item is available as \`\${{ ACTIONS.current_action.result }}\` within the loop body.
147
+
148
+ ## Join Strategy
149
+ When multiple parent actions converge, control when the child executes.
150
+
151
+ \`\`\`yaml
152
+ control_flow:
153
+ join_strategy: all # Wait for ALL parents (default)
154
+ # or
155
+ join_strategy: any # Execute as soon as ANY parent completes
156
+ \`\`\`
157
+
158
+ ## Retry Policy
159
+ Retry a failed action automatically.
160
+
161
+ \`\`\`yaml
162
+ control_flow:
163
+ retry_policy:
164
+ max_attempts: 3 # Total attempts (including first)
165
+ timeout: 30 # Timeout per attempt in seconds
166
+ retry_until: null # Optional expression that stops retries when truthy
167
+ \`\`\`
168
+
169
+ ## Start Delay
170
+ Delay execution by N seconds.
171
+
172
+ \`\`\`yaml
173
+ control_flow:
174
+ start_delay: 5 # Wait 5 seconds before executing
175
+ \`\`\`
176
+
177
+ ## Error Paths
178
+ Branch on action failure using source_handle in edges:
179
+
180
+ - \`success\` — execute when parent succeeds
181
+ - \`error\` — execute when parent fails
182
+
183
+ Access error data: \`\${{ ACTIONS.failed_action.error }}\`
184
+
185
+ ## Complete Example
186
+ \`\`\`yaml
187
+ # Action with all control flow options
188
+ control_flow:
189
+ run_if: \${{ ACTIONS.should_proceed.result }}
190
+ for_each: \${{ ACTIONS.get_ips.result }}
191
+ join_strategy: all
192
+ retry_policy:
193
+ max_attempts: 3
194
+ timeout: 60
195
+ start_delay: 2
196
+ \`\`\``,
197
+ common_mistakes: `# Tracecat Common Mistakes — Top 15
198
+
199
+ ## 1. Wrong HTTP request action type
200
+ - **Wrong:** \`core.http.request\` (dot)
201
+ - **Right:** \`core.http_request\` (underscore)
202
+
203
+ ## 2. Python script field name
204
+ - **Wrong:** \`code: "def main()..."\`
205
+ - **Right:** \`script: "def main()..."\`
206
+
207
+ ## 3. Boolean comparisons in run_if
208
+ - **Wrong:** \`run_if: \${{ ACTIONS.x.result == true }}\`
209
+ - **Right:** \`run_if: \${{ ACTIONS.x.result }}\`
210
+ - **Wrong:** \`run_if: \${{ ACTIONS.x.result == false }}\`
211
+ - **Right:** \`run_if: \${{ not ACTIONS.x.result }}\`
212
+
213
+ ## 4. Logical operators in expressions
214
+ - **Wrong:** \`\${{ A and B }}\` / \`\${{ A or B }}\`
215
+ - **Right:** \`\${{ A && B }}\` / \`\${{ A || B }}\`
216
+
217
+ ## 5. Action inputs format
218
+ - **Wrong:** Sending inputs as JSON object
219
+ - **Right:** Sending inputs as YAML string
220
+
221
+ ## 6. Action update HTTP method
222
+ - **Wrong:** \`PATCH /actions/{id}\`
223
+ - **Right:** \`POST /actions/{id}\` (with workflow_id query param)
224
+
225
+ ## 7. Listing actions endpoint
226
+ - **Wrong:** \`GET /workflows/{id}/actions\`
227
+ - **Right:** \`GET /actions?workflow_id={id}\`
228
+
229
+ ## 8. Missing workspace_id
230
+ - Must be a **query parameter** on every request
231
+ - NOT a header
232
+ - MCP tools handle this automatically
233
+
234
+ ## 9. Orphaned graph nodes
235
+ - Creating actions without adding edges = invisible in UI
236
+ - Always add edges AND position nodes after creating actions
237
+
238
+ ## 10. Unclosed expressions
239
+ - **Wrong:** \`\${{ ACTIONS.x.result\`
240
+ - **Right:** \`\${{ ACTIONS.x.result }}\`
241
+
242
+ ## 11. Using core.http_request for native operations
243
+ - **Wrong:** \`core.http_request\` to create a case
244
+ - **Right:** \`core.cases.create_case\`
245
+
246
+ ## 12. Secret/Schedule update method
247
+ - **Wrong:** \`PATCH /secrets/{id}\` or \`PATCH /schedules/{id}\`
248
+ - **Right:** \`POST /secrets/{id}\` / \`POST /schedules/{id}\`
249
+
250
+ ## 13. FN.serialize_json for Python inputs
251
+ - When passing objects to run_python, serialize them:
252
+ \`\${{ FN.serialize_json(ACTIONS.x.result) }}\`
253
+
254
+ ## 14. Trigger spacing in graph layout
255
+ - Trigger at y=0 takes ~200px height
256
+ - First action should be at y >= 300 (not y=200)
257
+
258
+ ## 15. Batch insert rows format
259
+ - **Wrong:** \`{ data: [{...}] }\`
260
+ - **Right:** \`{ rows: [{...}] }\` (flat objects, keys = column names)`,
261
+ };
262
+ const TOOLS_CATALOG = {
263
+ workflows: [
264
+ { name: "tracecat_list_workflows", description: "List all workflows in the workspace", params: "(none)", example: "tracecat_list_workflows()" },
265
+ { name: "tracecat_create_workflow", description: "Create a new workflow", params: "title (required), description", example: 'tracecat_create_workflow({ title: "Alert Triage" })' },
266
+ { name: "tracecat_get_workflow", description: "Get workflow details by ID", params: "workflow_id (required)", example: 'tracecat_get_workflow({ workflow_id: "wf_xxx" })' },
267
+ { name: "tracecat_update_workflow", description: "Update workflow title/description/status", params: "workflow_id (required), title, description, status", example: 'tracecat_update_workflow({ workflow_id: "wf_xxx", status: "online" })' },
268
+ { name: "tracecat_deploy_workflow", description: "Deploy (commit) a workflow", params: "workflow_id (required)", example: 'tracecat_deploy_workflow({ workflow_id: "wf_xxx" })' },
269
+ { name: "tracecat_export_workflow", description: "Export workflow as YAML", params: "workflow_id (required)", example: 'tracecat_export_workflow({ workflow_id: "wf_xxx" })' },
270
+ { name: "tracecat_delete_workflow", description: "Delete a workflow permanently", params: "workflow_id (required)", example: 'tracecat_delete_workflow({ workflow_id: "wf_xxx" })' },
271
+ { name: "tracecat_validate_workflow", description: "Validate workflow (actions, inputs, expressions, graph)", params: "workflow_id (required)", example: 'tracecat_validate_workflow({ workflow_id: "wf_xxx" })' },
272
+ { name: "tracecat_autofix_workflow", description: "Validate + auto-fix common issues (orphans, disconnected trigger, unclosed expressions)", params: "workflow_id (required), dry_run", example: 'tracecat_autofix_workflow({ workflow_id: "wf_xxx" })' },
273
+ ],
274
+ actions: [
275
+ { name: "tracecat_list_actions", description: "List all actions for a workflow", params: "workflow_id (required)", example: 'tracecat_list_actions({ workflow_id: "wf_xxx" })' },
276
+ { name: "tracecat_create_action", description: "Create a new action in a workflow", params: "workflow_id (required), type (required), title (required)", example: 'tracecat_create_action({ workflow_id: "wf_xxx", type: "core.http_request", title: "Check IP" })' },
277
+ { name: "tracecat_get_action", description: "Get action details", params: "action_id (required), workflow_id (required)", example: 'tracecat_get_action({ action_id: "act_xxx", workflow_id: "wf_xxx" })' },
278
+ { name: "tracecat_update_action", description: "Update action inputs/control_flow. Inputs must be YAML string!", params: "action_id (required), workflow_id (required), title, description, inputs (YAML string), control_flow", example: 'tracecat_update_action({ action_id: "act_xxx", workflow_id: "wf_xxx", inputs: "url: https://...\\nmethod: GET" })' },
279
+ { name: "tracecat_delete_action", description: "Delete an action", params: "action_id (required), workflow_id (required)", example: 'tracecat_delete_action({ action_id: "act_xxx", workflow_id: "wf_xxx" })' },
280
+ ],
281
+ executions: [
282
+ { name: "tracecat_run_workflow", description: "Execute a workflow with optional payload", params: "workflow_id (required), payload", example: 'tracecat_run_workflow({ workflow_id: "wf_xxx", payload: { ip: "1.2.3.4" } })' },
283
+ { name: "tracecat_list_executions", description: "List executions, optionally by workflow", params: "workflow_id, limit", example: 'tracecat_list_executions({ workflow_id: "wf_xxx", limit: 5 })' },
284
+ { name: "tracecat_get_execution", description: "Get full execution details", params: "execution_id (required)", example: 'tracecat_get_execution({ execution_id: "exec_xxx" })' },
285
+ { name: "tracecat_get_execution_compact", description: "Get compact execution (action status, inputs, results, errors)", params: "execution_id (required)", example: 'tracecat_get_execution_compact({ execution_id: "exec_xxx" })' },
286
+ { name: "tracecat_cancel_execution", description: "Cancel a running execution", params: "execution_id (required)", example: 'tracecat_cancel_execution({ execution_id: "exec_xxx" })' },
287
+ ],
288
+ cases: [
289
+ { name: "tracecat_list_cases", description: "List cases in workspace", params: "limit, status", example: 'tracecat_list_cases({ status: "new", limit: 10 })' },
290
+ { name: "tracecat_create_case", description: "Create a new case", params: "workflow_id (required), case_title (required), payload, malice, status, priority, action", example: 'tracecat_create_case({ workflow_id: "wf_xxx", case_title: "Suspicious IP" })' },
291
+ { name: "tracecat_get_case", description: "Get case details", params: "case_id (required)", example: 'tracecat_get_case({ case_id: "case_xxx" })' },
292
+ { name: "tracecat_update_case", description: "Update case status/priority/malice", params: "case_id (required), case_title, status, priority, malice, action", example: 'tracecat_update_case({ case_id: "case_xxx", status: "resolved" })' },
293
+ { name: "tracecat_delete_case", description: "Delete a case permanently", params: "case_id (required)", example: 'tracecat_delete_case({ case_id: "case_xxx" })' },
294
+ { name: "tracecat_add_comment", description: "Add comment to a case", params: "case_id (required), content (required)", example: 'tracecat_add_comment({ case_id: "case_xxx", content: "Investigation started" })' },
295
+ { name: "tracecat_list_comments", description: "List all comments on a case", params: "case_id (required)", example: 'tracecat_list_comments({ case_id: "case_xxx" })' },
296
+ ],
297
+ secrets: [
298
+ { name: "tracecat_search_secrets", description: "Search secrets by name", params: "name", example: 'tracecat_search_secrets({ name: "virustotal" })' },
299
+ { name: "tracecat_create_secret", description: "Create a new secret", params: "name (required), keys (required), type, description", example: 'tracecat_create_secret({ name: "virustotal", keys: [{ key: "API_KEY", value: "xxx" }] })' },
300
+ { name: "tracecat_get_secret", description: "Get secret metadata (not values)", params: "secret_name (required)", example: 'tracecat_get_secret({ secret_name: "virustotal" })' },
301
+ { name: "tracecat_update_secret", description: "Update secret keys/description", params: "secret_id (required), name, description, keys", example: 'tracecat_update_secret({ secret_id: "uuid", keys: [{ key: "API_KEY", value: "new" }] })' },
302
+ { name: "tracecat_delete_secret", description: "Delete a secret", params: "secret_id (required)", example: 'tracecat_delete_secret({ secret_id: "uuid" })' },
303
+ ],
304
+ tables: [
305
+ { name: "tracecat_list_tables", description: "List all tables", params: "(none)", example: "tracecat_list_tables()" },
306
+ { name: "tracecat_create_table", description: "Create a new table", params: "name (required), description", example: 'tracecat_create_table({ name: "ioc_enrichment" })' },
307
+ { name: "tracecat_get_table", description: "Get table details (includes columns)", params: "table_id (required)", example: 'tracecat_get_table({ table_id: "tbl_xxx" })' },
308
+ { name: "tracecat_update_table", description: "Update table name/description", params: "table_id (required), name, description", example: 'tracecat_update_table({ table_id: "tbl_xxx", name: "new_name" })' },
309
+ { name: "tracecat_delete_table", description: "Delete a table", params: "table_id (required)", example: 'tracecat_delete_table({ table_id: "tbl_xxx" })' },
310
+ { name: "tracecat_create_column", description: "Add column to table", params: "table_id (required), name (required), type (required: TEXT, INTEGER, NUMERIC, DATE, BOOLEAN, TIMESTAMP, TIMESTAMPTZ, JSONB, UUID, SELECT, MULTI_SELECT)", example: 'tracecat_create_column({ table_id: "tbl_xxx", name: "ip_address", type: "TEXT" })' },
311
+ { name: "tracecat_delete_column", description: "Delete column from table", params: "table_id (required), column_id (required)", example: 'tracecat_delete_column({ table_id: "tbl_xxx", column_id: "col_xxx" })' },
312
+ { name: "tracecat_list_rows", description: "List rows with pagination", params: "table_id (required), limit, offset", example: 'tracecat_list_rows({ table_id: "tbl_xxx", limit: 50 })' },
313
+ { name: "tracecat_get_row", description: "Get single row by ID", params: "table_id (required), row_id (required)", example: 'tracecat_get_row({ table_id: "tbl_xxx", row_id: "row_xxx" })' },
314
+ { name: "tracecat_insert_row", description: "Insert a row (keys = column names)", params: "table_id (required), data (required)", example: 'tracecat_insert_row({ table_id: "tbl_xxx", data: { ip: "1.2.3.4", score: 85 } })' },
315
+ { name: "tracecat_update_row", description: "Update a row", params: "table_id (required), row_id (required), data (required)", example: 'tracecat_update_row({ table_id: "tbl_xxx", row_id: "row_xxx", data: { score: 90 } })' },
316
+ { name: "tracecat_delete_row", description: "Delete a row", params: "table_id (required), row_id (required)", example: 'tracecat_delete_row({ table_id: "tbl_xxx", row_id: "row_xxx" })' },
317
+ { name: "tracecat_batch_insert_rows", description: "Insert multiple rows at once", params: "table_id (required), rows (required: array of flat objects)", example: 'tracecat_batch_insert_rows({ table_id: "tbl_xxx", rows: [{ ip: "1.2.3.4" }, { ip: "5.6.7.8" }] })' },
318
+ ],
319
+ graph: [
320
+ { name: "tracecat_get_graph", description: "Get workflow graph (nodes, edges, positions, version)", params: "workflow_id (required)", example: 'tracecat_get_graph({ workflow_id: "wf_xxx" })' },
321
+ { name: "tracecat_add_edges", description: "Connect actions in the graph", params: "workflow_id (required), edges (required: [{source_id, source_type, target_id, source_handle}])", example: 'tracecat_add_edges({ workflow_id: "wf_xxx", edges: [{ source_id: "trigger_id", source_type: "trigger", target_id: "act_xxx" }] })' },
322
+ { name: "tracecat_delete_edges", description: "Remove edges from graph", params: "workflow_id (required), edges (required)", example: 'tracecat_delete_edges({ workflow_id: "wf_xxx", edges: [{ source_id: "act_1", source_type: "udf", target_id: "act_2" }] })' },
323
+ { name: "tracecat_move_nodes", description: "Reposition nodes. Layout: trigger at (500,0), first action y>=300, 160px vertical spacing", params: "workflow_id (required), positions (required: [{action_id, x, y}])", example: 'tracecat_move_nodes({ workflow_id: "wf_xxx", positions: [{ action_id: "act_xxx", x: 500, y: 300 }] })' },
324
+ { name: "tracecat_update_trigger_position", description: "Reposition the trigger node", params: "workflow_id (required), x (required), y (required)", example: 'tracecat_update_trigger_position({ workflow_id: "wf_xxx", x: 500, y: 0 })' },
325
+ ],
326
+ schedules: [
327
+ { name: "tracecat_list_schedules", description: "List all schedules", params: "workflow_id", example: 'tracecat_list_schedules({ workflow_id: "wf_xxx" })' },
328
+ { name: "tracecat_create_schedule", description: "Create schedule (cron or interval)", params: "workflow_id (required), cron, every, inputs", example: 'tracecat_create_schedule({ workflow_id: "wf_xxx", every: "1h" })' },
329
+ { name: "tracecat_get_schedule", description: "Get schedule details", params: "schedule_id (required)", example: 'tracecat_get_schedule({ schedule_id: "sched_xxx" })' },
330
+ { name: "tracecat_update_schedule", description: "Update schedule", params: "schedule_id (required), cron, every, inputs, status", example: 'tracecat_update_schedule({ schedule_id: "sched_xxx", status: "offline" })' },
331
+ { name: "tracecat_delete_schedule", description: "Delete a schedule", params: "schedule_id (required)", example: 'tracecat_delete_schedule({ schedule_id: "sched_xxx" })' },
332
+ ],
333
+ webhooks: [
334
+ { name: "tracecat_create_webhook_key", description: "Generate/rotate webhook API key", params: "workflow_id (required)", example: 'tracecat_create_webhook_key({ workflow_id: "wf_xxx" })' },
335
+ ],
336
+ system: [
337
+ { name: "tracecat_health_check", description: "Check if Tracecat API is healthy", params: "(none)", example: "tracecat_health_check()" },
338
+ ],
339
+ docs: [
340
+ { name: "tracecat_docs", description: "Get inline documentation", params: "topic (required: action_types, expressions, functions, control_flow, common_mistakes)", example: 'tracecat_docs({ topic: "expressions" })' },
341
+ { name: "tracecat_tools_documentation", description: "Get documentation of all MCP tools grouped by category", params: "category (optional)", example: 'tracecat_tools_documentation({ category: "workflows" })' },
342
+ ],
343
+ templates: [
344
+ { name: "tracecat_list_templates", description: "List available SOAR workflow templates", params: "(none)", example: "tracecat_list_templates()" },
345
+ { name: "tracecat_get_template", description: "Get full YAML template for a workflow pattern", params: "template_id (required)", example: 'tracecat_get_template({ template_id: "alert_triage" })' },
346
+ ],
347
+ };
348
+ export function registerDocTools(server, _client) {
349
+ server.tool("tracecat_docs", "Get inline documentation about Tracecat concepts. Topics: action_types, expressions, functions, control_flow, common_mistakes.", {
350
+ topic: z.enum(["action_types", "expressions", "functions", "control_flow", "common_mistakes"])
351
+ .describe("Documentation topic to retrieve"),
352
+ }, async ({ topic }) => {
353
+ const doc = DOCS[topic];
354
+ if (!doc) {
355
+ return {
356
+ content: [{ type: "text", text: `Unknown topic: ${topic}. Available: ${Object.keys(DOCS).join(", ")}` }],
357
+ isError: true,
358
+ };
359
+ }
360
+ return { content: [{ type: "text", text: doc }] };
361
+ });
362
+ const categoryEnum = z.enum([
363
+ "workflows", "actions", "executions", "cases", "secrets",
364
+ "tables", "graph", "schedules", "webhooks", "system", "docs", "templates",
365
+ ]);
366
+ server.tool("tracecat_tools_documentation", "Get documentation of ALL Tracecat MCP tools grouped by category. Use this to discover available tools and learn how to use them. Optionally filter by category.", {
367
+ category: categoryEnum.optional().describe("Filter by category. Omit to get all tools."),
368
+ }, async ({ category }) => {
369
+ const categories = category ? [category] : Object.keys(TOOLS_CATALOG);
370
+ const lines = ["# Tracecat MCP Tools Reference\n"];
371
+ for (const cat of categories) {
372
+ const tools = TOOLS_CATALOG[cat];
373
+ if (!tools)
374
+ continue;
375
+ lines.push(`## ${cat.charAt(0).toUpperCase() + cat.slice(1)} (${tools.length} tools)\n`);
376
+ for (const t of tools) {
377
+ lines.push(`### ${t.name}`);
378
+ lines.push(`${t.description}`);
379
+ lines.push(`**Parameters:** ${t.params}`);
380
+ lines.push(`**Example:** \`${t.example}\`\n`);
381
+ }
382
+ }
383
+ const total = Object.values(TOOLS_CATALOG).reduce((sum, arr) => sum + arr.length, 0);
384
+ if (!category) {
385
+ lines.push(`---\n**Total: ${total} tools across ${Object.keys(TOOLS_CATALOG).length} categories**`);
386
+ }
387
+ return { content: [{ type: "text", text: lines.join("\n") }] };
388
+ });
389
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TracecatClient } from "../client.js";
3
+ export declare function registerExecutionTools(server: McpServer, client: TracecatClient): void;
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ export function registerExecutionTools(server, client) {
3
+ server.tool("tracecat_run_workflow", "Execute a workflow by ID with optional input payload", {
4
+ workflow_id: z.string().describe("Workflow ID"),
5
+ payload: z.record(z.unknown()).optional().describe("Input payload for the workflow execution"),
6
+ }, async ({ workflow_id, payload }) => {
7
+ const result = await client.post(`/workflows/${workflow_id}/execute`, payload ?? {});
8
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
9
+ });
10
+ server.tool("tracecat_list_executions", "List workflow executions, optionally filtered by workflow ID", {
11
+ workflow_id: z.string().optional().describe("Filter by workflow ID"),
12
+ limit: z.number().optional().describe("Maximum number of results"),
13
+ }, async ({ workflow_id, limit }) => {
14
+ const params = {};
15
+ if (workflow_id)
16
+ params.workflow_id = workflow_id;
17
+ if (limit)
18
+ params.limit = limit.toString();
19
+ const executions = await client.get("/workflow-executions", params);
20
+ return { content: [{ type: "text", text: JSON.stringify(executions, null, 2) }] };
21
+ });
22
+ server.tool("tracecat_get_execution", "Get details of a specific workflow execution", {
23
+ execution_id: z.string().describe("Execution ID"),
24
+ }, async ({ execution_id }) => {
25
+ const execution = await client.get(`/workflow-executions/${execution_id}`);
26
+ return { content: [{ type: "text", text: JSON.stringify(execution, null, 2) }] };
27
+ });
28
+ server.tool("tracecat_cancel_execution", "Cancel a running workflow execution", {
29
+ execution_id: z.string().describe("Execution ID"),
30
+ }, async ({ execution_id }) => {
31
+ const result = await client.post(`/workflow-executions/${execution_id}/cancel`);
32
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33
+ });
34
+ server.tool("tracecat_get_execution_compact", "Get compact execution details (lighter response with action-level status, inputs, results, errors)", {
35
+ execution_id: z.string().describe("Execution ID"),
36
+ }, async ({ execution_id }) => {
37
+ const execution = await client.get(`/workflow-executions/${execution_id}/compact`);
38
+ return { content: [{ type: "text", text: JSON.stringify(execution, null, 2) }] };
39
+ });
40
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TracecatClient } from "../client.js";
3
+ export declare function registerGraphTools(server: McpServer, client: TracecatClient): void;
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ export function registerGraphTools(server, client) {
3
+ server.tool("tracecat_get_graph", "Get the workflow graph (nodes, edges, positions, version). Use this to inspect the visual layout and connections between actions.", {
4
+ workflow_id: z.string().describe("Workflow ID"),
5
+ }, async ({ workflow_id }) => {
6
+ const graph = await client.get(`/workflows/${workflow_id}/graph`);
7
+ return { content: [{ type: "text", text: JSON.stringify(graph, null, 2) }] };
8
+ });
9
+ server.tool("tracecat_add_edges", "Add connections (edges) between actions in a workflow graph. source_type is 'trigger' for the trigger node or 'udf' for actions. source_handle can be 'success', 'error', or null (default).", {
10
+ workflow_id: z.string().describe("Workflow ID"),
11
+ edges: z.array(z.object({
12
+ source_id: z.string().describe("Source node ID (trigger ID or action ID)"),
13
+ source_type: z.enum(["trigger", "udf"]).describe("'trigger' for trigger node, 'udf' for action nodes"),
14
+ target_id: z.string().describe("Target action ID"),
15
+ source_handle: z.string().nullable().optional().describe("'success', 'error', or null (default)"),
16
+ })).describe("Array of edges to add"),
17
+ }, async ({ workflow_id, edges }) => {
18
+ const operations = edges.map((edge) => ({
19
+ type: "add_edge",
20
+ payload: {
21
+ source_id: edge.source_id,
22
+ source_type: edge.source_type,
23
+ target_id: edge.target_id,
24
+ source_handle: edge.source_handle ?? null,
25
+ },
26
+ }));
27
+ const result = await client.patchGraph(workflow_id, operations);
28
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
29
+ });
30
+ server.tool("tracecat_delete_edges", "Remove connections (edges) between actions in a workflow graph.", {
31
+ workflow_id: z.string().describe("Workflow ID"),
32
+ edges: z.array(z.object({
33
+ source_id: z.string().describe("Source node ID"),
34
+ source_type: z.enum(["trigger", "udf"]).describe("'trigger' or 'udf'"),
35
+ target_id: z.string().describe("Target action ID"),
36
+ })).describe("Array of edges to delete"),
37
+ }, async ({ workflow_id, edges }) => {
38
+ const operations = edges.map((edge) => ({
39
+ type: "delete_edge",
40
+ payload: {
41
+ source_id: edge.source_id,
42
+ source_type: edge.source_type,
43
+ target_id: edge.target_id,
44
+ },
45
+ }));
46
+ const result = await client.patchGraph(workflow_id, operations);
47
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
48
+ });
49
+ server.tool("tracecat_move_nodes", "Reposition action nodes in the workflow graph. Recommended layout: trigger at (500,0), first action at y>=300, 160px vertical spacing, 320px horizontal spacing for parallel nodes.", {
50
+ workflow_id: z.string().describe("Workflow ID"),
51
+ positions: z.array(z.object({
52
+ action_id: z.string().describe("Action ID to reposition"),
53
+ x: z.number().describe("X coordinate"),
54
+ y: z.number().describe("Y coordinate"),
55
+ })).describe("Array of node positions"),
56
+ }, async ({ workflow_id, positions }) => {
57
+ const operations = [{
58
+ type: "move_nodes",
59
+ payload: {
60
+ positions: positions.map((p) => ({
61
+ action_id: p.action_id,
62
+ x: p.x,
63
+ y: p.y,
64
+ })),
65
+ },
66
+ }];
67
+ const result = await client.patchGraph(workflow_id, operations);
68
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
69
+ });
70
+ server.tool("tracecat_update_trigger_position", "Reposition the trigger node in the workflow graph. Default recommended position is (500, 0).", {
71
+ workflow_id: z.string().describe("Workflow ID"),
72
+ x: z.number().describe("X coordinate"),
73
+ y: z.number().describe("Y coordinate"),
74
+ }, async ({ workflow_id, x, y }) => {
75
+ const operations = [{
76
+ type: "update_trigger_position",
77
+ payload: { x, y },
78
+ }];
79
+ const result = await client.patchGraph(workflow_id, operations);
80
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
81
+ });
82
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { TracecatClient } from "../client.js";
3
+ export declare function registerScheduleTools(server: McpServer, client: TracecatClient): void;