Join the Monad team at RSAC in San Francisco
Learn More
Read the blog
Resources / Blog / Detection Engineering for Claude Code, Part 1

March 23, 2026

Detection Engineering for Claude Code, Part 1

Matt Jane

Chief Architect & CTO

Curtis Redgate

Software Engineer

TL;DR

  • Claude Code emits OpenTelemetry natively across metrics and structured log events. The data is already there.
  • That telemetry unlocks four detection categories most teams currently have zero coverage on: unauthorized tool use, data exfiltration via AI, MCP server abuse, and prompt injection.
  • Raw OTLP is deeply nested and not analyst-friendly. Part 2 covers how to normalize it and get it into your SIEM.

Most engineering teams have been rolling out Claude Code for weeks or months by the time security asks what it’s doing. Adoption happens fast, visibility comes later. Claude Code already emits telemetry natively over OpenTelemetry. The data exists. The question is whether you have a pipeline to receive it, normalize it, and get it somewhere your security team can actually use.

This post covers what that telemetry contains and what it lets you detect. Part 2 covers how to get it flowing.

What Claude Code actually emits

Claude Code exports metrics and structured log events.

Metrics:

  • Session counts
  • Token usage by model
  • Cost in USD
  • Lines of code changed
  • Active time

Structured log events:

  • user_prompt
    • Full prompt text when OTEL_LOG_USER_PROMPTS=1 is enabled. Off by default.
  • api_request
    • Calls to the Anthropic API. Includes model, token counts, cost, and duration.
  • tool_result
    • Tool invocations with full detail: tool type in tool_parameters.bash_command, full command in tool_parameters.full_command, outcome in success, and decision in decision_type and decision_source.
  • tool_decision
    • Fires at the moment a permission decision is made, before execution completes. Carries decision_type (accept/reject) and decision_source. More useful than tool_result for catching rejected or policy-violating commands since it fires at decision time, not after the fact.
  • mcp_tool_use
    • MCP server and tool invocations. Add OTEL_LOG_TOOL_DETAILS=1 for server names and tool names.

What this telemetry lets you detect

Getting Claude Code telemetry into your SIEM opens up four detection categories that most teams currently have zero coverage on.

Unauthorized tool use

Same mental model as “process executed suspicious command,” just through an AI agent. Intent and outcome both live in claude_code.tool_result. Use tool_parameters.bash_command to identify that a Bash tool fired, then read tool_parameters.full_command for the actual command. success (true/false) tells you the outcome, decision_type (accept/reject) tells you whether it was allowed, and decision_source tells you why: config, user_permanent, user_temporary, user_abort, or user_reject. Alert on commands that were attempted but rejected, or auto-accepted when your policy says they shouldn’t have been. The more sophisticated version is behavioral baselining: a frontend engineer whose sessions suddenly start querying databases or accessing infrastructure configs is worth investigating.

Day-one detections:

  • curl or wget to an external IP in full_command
  • Reads of .aws/credentials or .env files
    • Packages installed from untrusted registries
  • chmod on sensitive files or SSH key generation
  • Commands attempted but rejected (decision_type: reject)
    • Commands auto-accepted via config that fall outside policy

Data exfiltration via AI

The threat model with the least existing coverage. Claude Code can read files and make outbound requests in the same session, and without this telemetry none of that is visible. The core detection: correlate tool_result events where tool_parameters.bash_command is Bash to identify shell activity, then key off tool_parameters.full_command for the actual command. bash_command only contains a short label like echo — any rule written against it alone will miss most of the signal. mcp_tool_use extends this to connected services: read internal files, send via a Slack or email MCP server, both actions appear in the telemetry.

What to look for:

  • Reads of sensitive file paths followed by curl or wget in full_command within the same session
  • session.id correlation between file reads and outbound network calls
  • mcp_tool_use events that pass file content to a Slack, email, or external API connector

MCP server abuse

MCP servers give Claude Code authenticated access to external systems: Slack, Jira, databases, cloud APIs, internal tools. That’s a lateral movement path through AI tooling with no existing detection coverage for most teams. There is no connection lifecycle event, so you won’t see a session connect to a new MCP server. Visibility is limited to claude_code.tool_result events fired when a tool is actually invoked. Server identity only surfaces when OTEL_LOG_TOOL_DETAILS=1 is set, exposing mcp_server_name and mcp_tool_name inside tool_parameters. If a session is compromised via prompt injection, the attacker inherits access to every connected MCP server. tool_result is where you catch that.

What to flag:

Unknown or unapproved server names in mcp_server_name

  • MCP tools accessing resources outside the user’s normal scope
  • Bulk data retrieval through internal API connectors
  • MCP tool invocations that follow anomalous bash activity in the same session

Prompt injection

The entry point for the other three. Direct injection is detectable when OTEL_LOG_USER_PROMPTS=1 is enabled: write detections against user_prompt for known patterns. Indirect injection is harder. Raw file contents are not included in telemetry, so a malicious README with embedded instructions won’t appear in user_prompt. What it produces is anomalous downstream behavior: unexpected tool_decision events, unusual tool_result commands, or MCP tool usage that doesn’t fit the session. That’s where the other three detections layer in.

Direct injection patterns to detect:

  • “Ignore previous instructions” or “output your system prompt”
  • Base64-encoded payloads in prompt text
  • Attempts to override safety constraints
  • References to internal systems the user shouldn’t be aware of

Why raw OTLP isn’t enough

Claude Code ships the data. The problem is the format. Raw OTLP comes out deeply nested, one large blob per session, with each metric and event buried inside arrays of key-value pairs. It is not something an analyst wants to write detections against. Here is a representative slice:

{"resourceMetrics":[{"resource":{"attributes":[
  {"key":"department","value":{"stringValue":"engineering"}},
  {"key":"service.name","value":{"stringValue":"claude-code"}},
  {"key":"service.version","value":{"stringValue":"2.1.76"}}
  ]},"scopeMetrics":[{"metrics":[{
    "name":"claude_code.session.count",
    "sum":{"dataPoints":[{"asDouble":1,"attributes":[
      {"key":"user.email","value":{"stringValue":"user@domain.com"}},
      {"key":"session.id","value":{"stringValue":"..."}}
    ]}]}
  }]}]}]}

Before this data is useful, it needs to be split into one record per event and normalized to a flat schema. Part 2 covers how to do that and how to route it to your SIEM without building a custom pipeline.

Up next

Part 2 picks up here. It covers how to ingest raw Claude Code OTel data, normalize it into flat analyst-friendly records, route it to your SIEM or data lake, and run sample detections against it on day one.

Related content

Detection Engineering for Claude Code, Part 2

Matt Jane

|

March 23, 2026

Detection Engineering for Claude Code, Part 2

Detection Engineering for Claude Code, Part 1

Matt Jane

|

March 23, 2026

Detection Engineering for Claude Code, Part 1

Customer Story: Upstart

Valerie Zargarpur

|

March 16, 2026

Customer Story: Upstart

The backbone for
security telemetry.

Effortlessly transform, filter, and route your security data. Tune out the noise and surface the signal with Monad.