Elastic Security Labs built a 280-line dependency-free bash script triggered by Cursor's lifecycle hooks to log every AI coding agent tool call (shell commands, file reads/edits, MCP calls) as JSONL, shipped via the already-deployed Elastic Agent into Elasticsearch. Since a May rollout, the pipeline has captured over 13 million tool-call events from more than 1,100 machines and nearly 900 users, enabling ES|QL hunting queries for things like credential file access or curl-pipe-to-shell patterns. The post covers hook configuration gotchas, a self-installing distribution trick for unmanaged Linux machines via Cursor's enterprise Cloud Distribution, privacy/hardening decisions (field-level security, metadata-only logging), and the limitations of hook-based auditing (tamper-evident not tamper-proof, no visibility into prompts or model reasoning).

18m read timeFrom elastic.co
Post cover image
Table of contents
Why AI coding agent activity is a blind spotWhat are agent hooks?How does the hook collector script work?Configuring Cursor hooksDeploying Cursor hooks without MDMSending hook logs to Elasticsearch with Elastic AgentHunting across agent activity with ES|QLHardening and privacy for agent audit logsWhat 13 million tool calls reveal about AI coding agent behaviorWhat are the limitations of hook-based AI agent auditing?Getting started: try it on one laptop

Questions this post answers

How do I log every tool call Cursor's AI agent makes, including shell commands and file reads?

Register a script against Cursor's lifecycle hooks (beforeShellExecution, afterFileEdit, beforeReadFile, beforeMCPExecution, etc.) defined in hooks.json, which Cursor invokes with a JSON payload on stdin for each event. A sensor-only script can immediately respond {"permission":"allow"} to blocking hooks, then append the parsed event as one JSONL line, promoting fields like tool_name, command, file_path, and mcp_server to the top level for easy querying. Developers wiring up agent observability pipelines can compare hook-based approaches like this on daily.dev.

Why does Cursor's hooks.json config not work after I deploy it via MDM to /Library/Application Support/?

Cursor splits hook command paths on spaces, so a script located under a path containing a space (like /Library/Application Support/) will silently never execute. The fix is keeping the executable script itself at a space-free path such as /usr/local/share/ai-hooks/, while only the hooks.json config file lives inside the Cursor application support directory. Additionally, Cursor only reads hooks.json at startup, so deployed machines stay silent until Cursor restarts. Anyone debugging silent AI agent hook deployments can track gotchas like this on daily.dev.

What do coding agents like Cursor actually spend most of their tool calls doing, shell commands or file reads?

File reads dominate over shell command execution by roughly four to one across a fleet of over 1,100 machines generating 13 million tool-call events. This contradicts the intuitive assumption that agents mostly run commands; in practice most agent activity is reconnaissance of the existing codebase, reading files before taking any action. Teams evaluating how AI coding agents actually behave in practice can dig into data like this on daily.dev.

73 Impressions