Skip to main content
Glama

Delete Trace

delete_trace
Destructive

Removes a trace and its spans by trace ID while preserving evaluation results for audit. Use to delete erroneous or sensitive traces for compliance or data cleanup.

Instructions

Remove a single trace by id. Cascades to spans; eval_results keep the score history with trace_id NULLed.

Sibling tools — log_trace creates traces, get_traces queries them, evaluate_output / evaluate_with_llm_judge / verify_citations score them. delete_rule handles custom-rule deletion (separate concern); list_rules / deploy_rule manage the custom-rule lifecycle. delete_trace is the DESTRUCTIVE single-row remove for traces; it does NOT touch eval_results (preserved for audit + drift analytics), spans cascade automatically.

Behavior. DESTRUCTIVE — SQL DELETE scoped to the caller's tenant_id. Cascades: spans belonging to this trace are deleted (FK ON DELETE CASCADE); eval_results that referenced this trace have their trace_id set to NULL (FK ON DELETE SET NULL) so aggregate dashboards + historical scores remain valid even after the trace is gone. Not idempotent: deleting an already-deleted trace returns deleted: false. Does not emit an audit log entry in v0.4 — traces are user-scope data, not policy changes. Rate-limited to 20 req/min on HTTP MCP.

Output shape. Returns JSON: { "deleted": boolean, "trace_id": string }. deleted=true if a row was removed; deleted=false if no trace with that id existed (or it belonged to a different tenant — cross-tenant deletes silently fail).

Use when a trace was captured in error, contains sensitive data that must be removed for compliance (e.g., a customer exercises GDPR right-to-erasure), or when cleaning up test data. Combine with get_traces to find candidates: query with filters → review → delete_trace(id) per target. For bulk time-window deletion, use deleteTracesOlderThan via the CLI / retention config — delete_trace is the single-row surgical path.

Don't use to clean up OLD data in bulk (use retention config with --retention-days). Don't use to PAUSE a trace — traces are immutable once stored; there's nothing to pause. Don't use to delete eval_results — eval_results survive their trace's deletion intentionally (for audit + drift analysis); they're pruned only by retention.

Parameters. trace_id is the only parameter; must match 32-char lowercase hex (Zod regex). The trace_id you pass is exactly what log_trace returned in its response, or what get_traces returned per row. Format mismatch fails Zod with 400 BEFORE the storage layer is touched. Cross-tenant trace_ids return deleted: false silently — they're invisible to the caller's tenant (prevents enumeration attacks; matches delete_rule's tenant-isolation contract).

Error modes. Throws 400 on malformed trace_id (wrong format: not 32-char lowercase hex). Returns {deleted: false} when the id doesn't exist in the caller's tenant (not an error — the trace may simply have been deleted already). Returns 429 on HTTP rate limit. Storage failures propagate as 500.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
trace_idYesTrace id to delete (32-hex lowercase; obtained from log_trace response or get_traces)

Implementation Reference

  • The handler function that executes the delete_trace logic: calls storage.deleteTrace and returns the result as JSON.
    async (args) => {
      const deleted = await storage.deleteTrace(LOCAL_TENANT, args.trace_id);
      return {
        content: [
          {
            type: 'text' as const,
            text: JSON.stringify({ deleted, trace_id: args.trace_id }),
          },
        ],
      };
    },
  • Input schema definition using Zod: trace_id must be a 32-character lowercase hex string.
    const inputSchema = {
      trace_id: z
        .string()
        .regex(/^[a-f0-9]{32}$/)
        .describe('Trace id to delete (32-hex lowercase; obtained from log_trace response or get_traces)'),
    };
  • Registration function that calls server.registerTool with name 'delete_trace', full description, input schema, annotations, and handler.
    export function registerDeleteTraceTool(
      server: McpServer,
      storage: IStorageAdapter,
    ): void {
      server.registerTool(
        'delete_trace',
        {
          title: 'Delete Trace',
          description: [
            'Remove a single trace by id. Cascades to spans; eval_results keep the score history with trace_id NULLed.',
            '',
            'Sibling tools — log_trace creates traces, get_traces queries them, evaluate_output / evaluate_with_llm_judge / verify_citations score them. delete_rule handles custom-rule deletion (separate concern); list_rules / deploy_rule manage the custom-rule lifecycle. delete_trace is the DESTRUCTIVE single-row remove for traces; it does NOT touch eval_results (preserved for audit + drift analytics), spans cascade automatically.',
            '',
            'Behavior. DESTRUCTIVE — SQL DELETE scoped to the caller\'s tenant_id. Cascades: spans belonging to this trace are deleted (FK ON DELETE CASCADE); eval_results that referenced this trace have their trace_id set to NULL (FK ON DELETE SET NULL) so aggregate dashboards + historical scores remain valid even after the trace is gone. Not idempotent: deleting an already-deleted trace returns `deleted: false`. Does not emit an audit log entry in v0.4 — traces are user-scope data, not policy changes. Rate-limited to 20 req/min on HTTP MCP.',
            '',
            'Output shape. Returns JSON: `{ "deleted": boolean, "trace_id": string }`. `deleted=true` if a row was removed; `deleted=false` if no trace with that id existed (or it belonged to a different tenant — cross-tenant deletes silently fail).',
            '',
            "Use when a trace was captured in error, contains sensitive data that must be removed for compliance (e.g., a customer exercises GDPR right-to-erasure), or when cleaning up test data. Combine with get_traces to find candidates: query with filters → review → delete_trace(id) per target. For bulk time-window deletion, use `deleteTracesOlderThan` via the CLI / retention config — delete_trace is the single-row surgical path.",
            '',
            "Don't use to clean up OLD data in bulk (use retention config with --retention-days). Don't use to PAUSE a trace — traces are immutable once stored; there's nothing to pause. Don't use to delete eval_results — eval_results survive their trace's deletion intentionally (for audit + drift analysis); they're pruned only by retention.",
            '',
            'Parameters. trace_id is the only parameter; must match 32-char lowercase hex (Zod regex). The trace_id you pass is exactly what log_trace returned in its response, or what get_traces returned per row. Format mismatch fails Zod with 400 BEFORE the storage layer is touched. Cross-tenant trace_ids return `deleted: false` silently — they\'re invisible to the caller\'s tenant (prevents enumeration attacks; matches delete_rule\'s tenant-isolation contract).',
            '',
            "Error modes. Throws 400 on malformed trace_id (wrong format: not 32-char lowercase hex). Returns `{deleted: false}` when the id doesn't exist in the caller's tenant (not an error — the trace may simply have been deleted already). Returns 429 on HTTP rate limit. Storage failures propagate as 500.",
          ].join('\n'),
          inputSchema,
          annotations: {
            readOnlyHint: false,
            destructiveHint: true,
            idempotentHint: false,
            openWorldHint: false,
          },
        },
        async (args) => {
          const deleted = await storage.deleteTrace(LOCAL_TENANT, args.trace_id);
          return {
            content: [
              {
                type: 'text' as const,
                text: JSON.stringify({ deleted, trace_id: args.trace_id }),
              },
            ],
          };
        },
      );
    }
  • Import and registration call within registerAllTools(), wiring the tool into the MCP server.
    import { registerDeleteTraceTool } from './delete-trace.js';
    import { registerEvaluateWithLLMJudgeTool } from './evaluate-with-llm-judge.js';
    import { registerVerifyCitationsTool } from './verify-citations.js';
    
    export function registerAllTools(
      server: McpServer,
      storage: IStorageAdapter,
      evalEngine: EvalEngine,
      customRuleStore: CustomRuleStore,
    ): void {
      registerLogTraceTool(server, storage);
      registerEvaluateOutputTool(server, storage, evalEngine);
      registerGetTracesTool(server, storage);
      registerListRulesTool(server, customRuleStore);
      registerDeployRuleTool(server, customRuleStore);
      registerDeleteRuleTool(server, customRuleStore);
      registerDeleteTraceTool(server, storage);
      registerEvaluateWithLLMJudgeTool(server, storage);
      registerVerifyCitationsTool(server, storage);
    }
  • Storage-layer implementation: tenant-scoped SQL DELETE from traces table, returns boolean indicating if a row was deleted.
    async deleteTrace(tenantId: TenantId, traceId: string): Promise<boolean> {
      assertTenant(tenantId);
      // Tenant-scoped: a trace id owned by a different tenant is
      // untouchable from this call. Cross-tenant deletions are not just
      // denied — they're invisible (no indication the id even exists).
      const result = this.db
        .prepare('DELETE FROM traces WHERE tenant_id = ? AND trace_id = ?')
        .run(tenantId, traceId);
      return result.changes > 0;
    }
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (destructiveHint, idempotentHint), the description details SQL DELETE scoped to tenant, cascade behaviors, idempotency behavior, rate limits, and silent cross-tenant failure. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with sections (siblings, behavior, output, use cases, errors). Every sentence adds value; front-loaded with key purpose and differentiation. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, description fully covers output shape, error modes, rate limits, tenant isolation, and cascade effects. Complete for decision-making and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Adds meaning beyond schema: format requirement (32-char hex), source (log_trace/get_traces), validation failure, and cross-tenant behavior. Schema coverage is 100%, so baseline is 3, but description significantly enriches.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool removes a single trace by ID, and distinguishes itself from siblings like delete_rule (deletes rules) and bulk deletion via CLI. The cascade behavior and effect on eval_results further clarify scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (error, compliance, test cleanup) and when not to use (bulk, pause, eval_result deletion). Provides alternatives like retention config and suggests combining with get_traces.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iris-eval/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server