feat(agents): tracecat mcp server#2138
Conversation
|
@cubic review |
@jordan-umusu I have started the AI code review. It will take a few minutes to complete. |
|
@codex review |
There was a problem hiding this comment.
7 issues found across 42 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/agent/preset/prompts.py">
<violation number="1" location="tracecat/agent/preset/prompts.py:52">
P3: Add a leading space (or newline) so this new sentence doesn’t run into the previous one in the composed prompt.</violation>
</file>
<file name="scripts/cluster">
<violation number="1" location="scripts/cluster:282">
P2: NEXT_PUBLIC_API_URL ignores a PUBLIC_API_URL override, so the frontend can keep calling the default app host even when PUBLIC_API_URL points elsewhere. Default NEXT_PUBLIC_API_URL to PUBLIC_API_URL to keep the frontend aligned with the API override.</violation>
</file>
<file name="tracecat/mcp/middleware.py">
<violation number="1" location="tracecat/mcp/middleware.py:43">
P2: `sys.getsizeof` measures Python object memory, not payload bytes; this can mis-enforce the input size limit (e.g., multi-byte strings). Use encoded byte length instead to enforce the configured byte limit accurately.</violation>
</file>
<file name="tracecat/agent/mcp/internal_tools.py">
<violation number="1" location="tracecat/agent/mcp/internal_tools.py:118">
P2: Bug: `or` fallback is incorrect for empty sets. If a workspace secret exists but has no decrypted keys, `set()` is falsy in Python, so `workspace_inventory.get(secret_name) or org_inventory.get(secret_name)` silently falls through to the org inventory. Use an explicit `is not None` check to correctly distinguish "secret absent" from "secret present with no keys".</violation>
</file>
<file name="tracecat/organization/router.py">
<violation number="1" location="tracecat/organization/router.py:174">
P2: Avoid returning raw ValueError messages from MCP config/auth helpers. These messages include internal configuration details (e.g., missing env vars) and should be sanitized before sending to clients.</violation>
</file>
<file name="tests/unit/test_mcp_server.py">
<violation number="1" location="tests/unit/test_mcp_server.py:46">
P2: Tests under tests/unit are expected to be integration-style with no mocks. These tests rely heavily on monkeypatch, which conflicts with the project’s testing guidelines for this directory.</violation>
</file>
<file name="tracecat/mcp/server.py">
<violation number="1" location="tracecat/mcp/server.py:1">
P1: Infinite loop when the dependency graph has cycles. The BFS re-enqueues nodes whenever it finds a longer path (`new_depth > depth[child]`), which never terminates if a cycle is reachable from a root. Since this is called automatically when users provide workflow YAML without a layout, a cyclic `depends_on` will hang the MCP server. Add a visited-count guard or cap the maximum depth to the number of actions.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1d2020c159
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
d5b4187 to
1c4cfdf
Compare
…n_python validation
1c4cfdf to
39af36c
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
4 issues found across 31 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docker-compose.yml">
<violation number="1" location="docker-compose.yml:312">
P1: The MCP service is pinned to an older image tag (`1.0.0-beta.5`) than the rest of the stack. This is likely missing the new `tracecat.mcp` module and will cause the container to crash on startup. Use the same tag as the other services so the MCP code is present.</violation>
</file>
<file name="tracecat/agent/mcp/internal_tools.py">
<violation number="1" location="tracecat/agent/mcp/internal_tools.py:116">
P2: Optional secrets with required keys are incorrectly treated as mandatory. The guard `if not required_keys and requirement.get("optional", False)` only skips optional secrets that have no keys. An optional secret that declares `keys` (e.g., for an optional integration) but isn't provisioned will cause the tool to be reported as unconfigured and blocked from being added to a preset via `update_preset`. The condition should skip all optional secrets, regardless of whether they declare keys.</violation>
</file>
<file name="tracecat/mcp/auth.py">
<violation number="1" location="tracecat/mcp/auth.py:624">
P1: Service principal role is created without computing effective scopes, leaving `scopes=None`. All other role-creation paths in this file call `compute_effective_scopes`. This means service principals authenticated via client-credentials will fail any downstream scope-based authorization checks. Either compute scopes for the service role or explicitly assign appropriate scopes (e.g., a wildcard like `system_role()` uses).</violation>
<violation number="2" location="tracecat/mcp/auth.py:762">
P2: N+1 query: each workspace in the loop triggers a separate `resolve_workspace_org` DB call. Consider fetching all workspace→org mappings in a single query (e.g., `select(Workspace.id, Workspace.organization_id).where(Workspace.id.in_([...]))`) and filtering in memory.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
de89703 to
6b260f0
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
2 issues found across 2 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/mcp/server.py">
<violation number="1">
P2: Avoid returning raw exception details to MCP clients. Log the error and return a generic failure message instead.
(Based on your team's feedback about avoiding platform-level exception details in client responses.) [FEEDBACK_USED]</violation>
<violation number="2">
P2: Avoid returning raw exception details to MCP clients. Log the error internally, but return a sanitized message so platform/internal details aren’t leaked.
(Based on your team's feedback about avoiding platform-level exception details in client responses.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
This comment has been minimized.
This comment has been minimized.
| @field_validator("allowlisted_cidrs", "methods", mode="before") | ||
| @classmethod | ||
| def _coerce_none_to_empty_list(cls, v: Any) -> Any: | ||
| """DB columns may store NULL; coerce to empty list for validation.""" | ||
| if v is None: | ||
| return [] | ||
| return v | ||
|
|
||
| @field_validator("filters", mode="before") | ||
| @classmethod | ||
| def _coerce_none_to_empty_dict(cls, v: Any) -> Any: | ||
| """DB columns may store NULL; coerce to empty dict for validation.""" | ||
| if v is None: | ||
| return {} | ||
| return v |
There was a problem hiding this comment.
do the pydantic defaults not work?
|
|
||
|
|
||
| @mcp.tool() | ||
| async def run_draft_workflow( |
There was a problem hiding this comment.
change so we only have polling
8f1e2e2 to
a2c9e9b
Compare
|
@cubic re-review |
@jordan-umusu I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
6 issues found across 33 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tracecat/agent/preset/prompts.py">
<violation number="1" location="tracecat/agent/preset/prompts.py:52">
P3: Add a space or newline at the end of this sentence so the next literal doesn't concatenate into "do not add it.Important" in the rendered prompt.</violation>
</file>
<file name="tracecat/workflow/executions/service.py">
<violation number="1" location="tracecat/workflow/executions/service.py:887">
P2: Calling `_start_workflow` directly bypasses the `@audit_log`-decorated workflow creation path, so nowait executions no longer generate audit entries. Use the audited `create_workflow_execution_wait_for_start` in the background task to preserve audit logs.</violation>
</file>
<file name="docker-compose.yml">
<violation number="1" location="docker-compose.yml:318">
P2: Port mapping hardcodes container port 8099 while `TRACECAT_MCP__PORT` allows overriding the MCP listen port; if the env var is changed, Docker maps to the wrong internal port and the service becomes unreachable. Tie the container port to the same env var.</violation>
</file>
<file name="tracecat/mcp/server.py">
<violation number="1" location="tracecat/mcp/server.py:1">
P2: Broad `except Exception` handlers forward the raw exception message to MCP clients via `ToolError(f"Failed to ...: {e}")`. Internal details (DB errors, file paths, etc.) could leak to external callers. Use a generic message without `{e}` in the client-facing `ToolError`, and keep the detailed error only in the server-side log.
(Based on your team's feedback about not surfacing platform-level exceptions to clients.) [FEEDBACK_USED]</violation>
<violation number="2" location="tracecat/mcp/server.py:1">
P2: The `offset` parameter is accepted but silently discarded — users attempting to paginate will always receive the first page. Either remove the parameter until cursor support is implemented (to avoid misleading callers), or pass it through to `search_rows`.</violation>
<violation number="3" location="tracecat/mcp/server.py:1">
P1: Post-fetch filtering on `status` and `search` is applied to an already-limited page of DB results. If most of the first `limit` workflows don't match the filter, the tool may return zero or fewer results than actually exist. Either push the `status`/`search` filters down into the database query, or fetch enough rows to fill the requested limit after filtering.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Problem
Tracecat had no way for external AI coding tools (Claude Desktop, Cursor, etc.) to interact with the platform programmatically. Users couldn't manage workflows,
cases, tables, or executions from their IDE or AI assistant.
What This PR Addresses
Adds a standalone MCP (Model Context Protocol) server for Tracecat, exposing ~48 tools and 3 resources that let external MCP clients manage:
Key components:
tracecat/mcp/server.py— FastMCP server with all tool/resource definitionstracecat/mcp/auth.py— 3 auth modes: OIDC interactive (browser login), JWT verification, token introspectiontracecat/mcp/config.py— env-var-driven config (host, port, auth mode, rate limits)tracecat/mcp/middleware.py— rate limiting, input size limits, timeoutsmcpservice on port 8099 across all compose filesHow to Test / QA
uv run pytest tests/unit/test_mcp_server.py tests/unit/test_mcp_auth.py tests/unit/test_mcp_config.py -xjust cluster up -d— verify themcpcontainer starts on port 8099http://localhost:8099/mcp, authenticate via OIDC, and verifylist_workspaces→list_workflows→run_draft_workflowflowTRACECAT_MCP__AUTH_MODEset tooidc_interactive,oauth_client_credentials_jwt, andoauth_client_credentials_introspectionSummary by cubic
Adds a first‑party MCP server for IDE integrations with org‑scoped auth and connect links over streamable HTTP. Auth is OIDC‑only with startup retries and raises fastmcp AuthorizationError on failures; includes Helm/Docker deploys, rate limits, sanitized errors, tests (including webhook path), and table export/import (CSV export patched). Also fixes spacing in the agent preset prompt.
New Features
Migration
Written for commit c357b45. Summary will update on new commits.