Communitygithub.com

anhhung04/tracecat-automation-best-practices

Use when building, editing, validating, or debugging generic Tracecat automations through Tracecat MCP, including workflow DSL/YAML authoring, table design, unique indexes, run-python Tracecat imports, agent presets, ai.agent or ai.preset_agent workflows, executions, validation, and workflow best practices.

What is tracecat-automation-best-practices?

tracecat-automation-best-practices is a Cursor agent skill that use when building, editing, validating, or debugging generic Tracecat automations through Tracecat MCP, including workflow DSL/YAML authoring, table design, unique indexes, run-python Tracecat imports, agent presets, ai.agent or ai.preset_agent workflows, executions, validation, and workflow best practices.

Works with~Claude Code~Codex CLI✓Cursor✓Antigravity
npx skills add https://github.com/anhhung04/omp-tracecat/tree/HEAD/skills/tracecat-automation-best-practices

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Tracecat Automation Best Practices

For Slack-facing automations, use /skill:tracecat-slackbot-best-practices. In Tracecat Workspace Chat, load /skill:tracecat-workspace-chat first; its host-specific tool mapping overrides the external MCP steps below. On-demand references: graph-shape, expressions, run-python, tables, trigger-inputs, case-triggers, secrets, agent-outputs, agent-presets, workflow-editing.

Workflow

For an external Tracecat MCP connection, start from live context rather than guessing:

  1. Discover the workspace with list_workspaces.
  2. Read tracecat://platform/dsl-reference when DSL syntax or examples are needed.
  3. Use get_workflow_authoring_context for action schemas, variables, and secrets.
  4. For existing workflows, use get_workflow, then targeted edit_workflow patches — see workflow-editing. Before adding or editing any run_if, depends_on, or join_strategy, call get_workflow_graph where the host exposes it and read every upstream action and its gated_by list first.
  5. Validate with validate_workflow, run a draft or published execution when appropriate, then inspect failures with list_workflow_executions and get_workflow_execution.

Other hosts can expose a different tool surface. Follow their adapter rather than mechanically prefixing these bare MCP names.

Clarify production choices that change the workflow contract: workspace, integration/provider, secret source, publish/run behavior, destructive side effects, approvals, or acceptance criteria. If the request is already specific enough, proceed.

Graph shape

Sketch the shape before authoring and keep it as close to one line as the work allows. Prefer linear over parallel, and readable over fully connected: fewer edges is the metric.

  • depends_on is execution order, not data wiring. An action can read ACTIONS.<ref>.result from any ancestor, not only a direct parent — validation checks that the ref exists in the workflow, not that it is a parent. Add an edge when the action must wait, never to make a value reachable.
  • Gate once. A run_if on an action also skips every action below it on the chain, so never repeat it downstream. A chain where every action carries the same TRIGGER.enabled && ... guard is wrong; only the first action needs it. A downstream action gets its own run_if only for a new condition, written alone. The exception is a join, and it is a trap: a task with several parents is not force-skipped while one survives, but the default join_strategy: all then requires every parent to have been visited, so one skipped branch makes the join unreachable and fails the workflow. Use join_strategy: any, or repeat the branch's condition on the join so it self-skips first.
  • Default to a single chain. Branch only for genuinely independent work worth running concurrently, and rejoin only when a later action needs results from more than one branch.
  • One parent is the norm. More than one means a deliberate join — choose join_strategy on purpose.
  • Keep each run_if short: one readable comparison for the one new condition. A long &&/|| chain usually restates upstream gates or belongs in an earlier transform.
  • Keep ordinary workflows around 20 nodes or fewer and agentic workflows around 6 nodes or fewer. Prefer readable top-to-bottom flows ordered the way the process reads, and human-readable refs. Tracecat MCP auto-formats the canvas when actions or edges change, so omit layout rather than hand-placing nodes.

See graph-shape for a worked over-connected rewrite, join strategies, error edges, and why scatter is not branching.

Action priority

Pick the most specific action that does the job:

  1. Integration actions (tools.<integration_slug>.*) for the product being called. Search list_actions with a short product or action-name substring (for example query="okta"), or browse a known tools.<integration_slug> namespace, before writing an API call. Search matches literal substrings, not separate keywords. Follow next_cursor while has_more is true when the first page lacks a suitable action. Inspect candidates with get_action_context and compare their required credentials and arguments.
  2. Core actions: core.http_request / core.http_paginate for products or endpoints with no integration action, core.transform.* for shaping, and core.table.* / core.cases.* for platform data.
  3. core.script.run_python only for a self-contained step that no action covers. Never call an API from Python when an integration or HTTP action can make the call.

Never invent tools.* names. If a generic request fits several integrations, show the candidates first.

Choosing between an agent and Python

Decide by the kind of work, and optimize the whole workflow for maintainability and readability — the fewest actions and the least code that does the job.

  • Agentic work → use an agent (ai.agent / ai.preset_agent). Anything needing judgment, investigation, routing, enrichment choices, summarization, composing a message, or posting to Slack. Grant the tools on the preset's actions allowlist or on a skill's metadata.tools, then trust the agent with them. Prefer agents, prompts, and skills — they carry the creative thinking with far fewer moving parts than a graph of deterministic nodes.
  • Deterministic data plumbing → use Python (core.script.run_python). Transforming, normalizing, redacting, joining, and batching rows into tables. This is exactly what Python is for — it is not a smell. One clear run_python action beats scattering the same work across many nodes. API calls are not data plumbing: they go in integration or core.http_* actions (see Action priority).

The smell is using Python for the agentic part — for example composing or sending an agent's Slack message from a script instead of giving the agent the Slack tool.

Prompt-size guardrail: push behavior into the prompt up to a reasonable size. When a single prompt grows too large to stay readable, decompose into subagents or a skill rather than inflating one mega-prompt.

Agent-First Automation

When the user asks for an agentic workflow, an agent preset, or says to use agents, make the agent the primary owner of the work. Give it the tools it needs and trust it to investigate, decide, route, compose messages, and act. Default to a thin shape: trigger -> reshape/redact -> upsert event record -> ai.agent (or ai.preset_agent).

Persist the normalized event before the agent runs. When events originate in a SIEM, log platform, SaaS webhook stream, audit trail, or cloud service, store the normalized payload and review state in a Tracecat table first, with a deterministic upsert. That gives retries, duplicate webhooks, and replay a durable source of truth. Point the agent at the saved row, so the workflow — not the agent — owns the first durable record of the event.

A deterministic node earns its place by being thin, direct, and easy to audit, and by doing work the agent should not own: redact secrets before the agent sees data, normalize schema, upsert the event row, guard expensive agent runs with a dedupe check, prepare bounded batches, or checkpoint durable state. Keep judgment, routing, enrichment, message composition, soft approval decisions, and final notification behavior in the agent. When an approval question itself needs judgment, let the agent decide or draft the request.

Two different mechanisms cover risk, and they do not compete with preset-owned tools. Use a deterministic gate for an irreversible external effect the workflow itself performs — the graph decides whether that node runs at all. Use per-tool tool_approvals on the preset for a risky tool the agent holds: the agent keeps the tool, and the run pauses for a human before that specific call goes through.

Favor one well-instructed agent or reusable preset over many small deterministic nodes. An agent's tools normally live on the preset's actions allowlist, or on an attached skill's metadata.tools when several agents share the group. That way the agent has the same tools in Workspace Chat and in every workflow that calls it. Instructions carry behavior rather than permissions: output contracts, Slack style, dedupe rules, tenant boundaries, and permitted side effects go in the preset instructions, which the agent reads instead of repo files. When the agent needs a tool, granting the narrow tool there usually beats wrapping the same call in a workflow action.

An ai.preset_agent node usually carries just preset and user_prompt, plus an optional instructions append for run-specific context. Its actions argument replaces the preset's and its skills' registry tools for that run rather than adding to them — MCP tools stay attached, and an empty list is ignored — so it suits trying a preset with a different tool set in a test or an eval. A list left there in a shipping workflow also drops the registry tools the preset's skills contribute, which is rarely what the author wanted.

Write agent instructions as goals, context, and reasons rather than prohibitions. A blanket rule can cancel a tool the agent was given: one caller appended "Use only supplied evidence" and the agent skipped the single policy lookup it had been granted a tool for. Reserve hard rules for irreversible or outward-facing actions, and put tool_approvals or a deterministic gate behind those instead of relying on the wording of a prompt.

A preset's runtime has more capability than list_actions shows: a real shell and file tools, Python, curl, jq, and DuckDB. Inspect the preset/runtime context before adding a workflow helper to compensate for a presumed limitation — see agent-presets.

Authoring Defaults

  • For agentic workflows, push most behavior into the agent prompt/preset. For event-driven workflows, a single reshape/redact/upsert action before the agent is usually the right deterministic boundary.
  • Use ai.agent / ai.preset_agent for the agentic work; the product's tools.* action (or core.http_request when none exists) for deterministic API calls; core.script.run_python for deterministic data work, once an inline expression, run_if, or a core.transform.* action has been ruled out — see expressions and run-python.
  • For bulk table writes, prefer one native core.table.insert_rows action when rows are already shaped (up to 1000 rows per batch). If shaping, chunking, or mixed table/case writes are needed, use core.script.run_python with imported helpers and bounded batches instead of scattering many DB-backed actions.
  • Do not give core.http_request to an agent unless the user explicitly accepts the broad network capability. Put deterministic HTTP in the workflow graph or a tightly scoped subflow.
  • Split into subflows only when there is a real orchestration boundary, reusable child workflow, separate execution history, approvals, long-running actions, or independent checkpointing. Use core.workflow.execute and prefer workflow_alias over hard-coded IDs. Subflow bulk defaults: loop_strategy: batch, batch_size: 32, fail_strategy: isolated, wait_strategy: wait (use detach only when the parent does not need child results).
  • Keep run-python and agent outputs small: downstream rows, summary counts, bounded error samples.
  • Prefer a reusable agent preset over inline ai.agent, and the model object over the deprecated top-level model_name/model_provider — see agent-presets.

Common Mistakes

  • Using Python for the agentic part — composing or posting an agent's Slack message, or making routing/judgment calls in a script. The agent owns message composition, posting (via a Slack tool), and judgment; Python owns deterministic data plumbing.
  • Over-connecting the graph: an edge from every producer to every consumer. Read from ancestors and keep one chain (see graph-shape).
  • Repeating a run_if down a chain, restating a condition an ancestor already enforced. The skip propagates on its own — gate once at the top, and give a downstream action a run_if only for a new condition it alone introduces (see graph-shape).
  • Workflow action names are not MCP tool names. Use MCP tools such as create_workflow to manage workflows, and action names such as core.http_request only inside workflow YAML.
  • Inventing tools.* action names. Discover actions with list_actions, then inspect exact schemas with get_action_context.
  • Calling a product's API from core.script.run_python (requests, httpx) or a bare core.http_request when a suitable tools.* action exists. Search and page through list_actions for the product first.
  • Using for_each for ordinary loop management, or using scatter/gather for data-heavy batching, filtering, joins, or table writes. Default to scatter for workflow-level loops; add gather only when downstream steps need combined results, and omit it otherwise. Be especially careful with >10 scattered DB-backed table/case actions, which can exhaust Postgres connection slots. Scatter's optional interval can stagger work, but it does not batch DB writes; use one core.table.insert_rows action or core.script.run_python batching for data-heavy processing (see run-python).
  • Using insert_rows(..., upsert=True) without a unique index on the key column (see tables).
  • Granting core.http_request to an agent without explicit user approval of broad network access.
  • Shipping a workflow with actions: on an ai.preset_agent node. It replaces the preset's and its skills' registry tools for that run instead of adding to them, so the skills' tools quietly disappear. Outside a test or an eval, put the tools on the preset or a skill and keep the node to preset + user_prompt.
  • Defining an agent output_type when the user did not ask for structured output. Default to none, give the agent the tool, and let the side effect be the output; add one only on an explicit request or when a downstream step branches on the value (see agent-outputs).

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Related Skills