Mesmer's Cursor integration exports what your agents actually did — prompts, file edits, shell commands, tool calls — over OpenTelemetry, and lands it next to delivered effort in the same AI Usage tab that already reports Claude Code spend. It installs once per machine with a single shell command.
That shapes how three different people roll it out — an individual engineer running one command, an IT team pushing it to a fleet through Rippling or Iru, and a platform engineer wiring it into a Cursor Cloud Agent Build. Each gets its own section below, followed by the part worth reading before any of them: exactly what this captures, and what it deliberately doesn't.
An individual engineer: one command, once#
Put the organisation token in the environment that runs the installer, then run the one-liner:
export MESMER_OTLP_TOKEN='<org token>'
curl -fsSL https://mesmer.co/cursor-otel/install.sh | shRestart Cursor — it reads hooks.json at startup — and check that it
worked:
~/.local/share/mesmer-cursor-otel/bin/mesmer-cursor-doctordoctor checks the runtime, the launcher, the hook registration, the
stored credential and the endpoint, and then does the check that actually
matters: whether spans have been delivered to the collector recently. It
prints remediation for whatever's wrong, and it's safe to run as often as
you like.
Requirements, current as of 2026, are deliberately thin: macOS or Linux, x64 or arm64,
/bin/sh, curl and tar. Nothing else — the release ships a vendored,
relocatable CPython, so there's nothing to install from a package manager
on the target machine. The whole runtime occupies about 51 MB on disk
after a roughly 17 MB download.
Re-running the install command later is safe: it's idempotent, upgrades in place, and keeps whatever token is already stored if you don't supply a new one.
From then on it keeps itself current: on every session start it spawns a detached background check that upgrades in place.
A fleet, through Rippling#
We support Rippling MDM. This section describes what to deploy rather than where to click — check Rippling's current documentation for the exact screen; the shape of the deployment itself doesn't change.
It's two pieces, deployed together.
A recurring script policy runs the same installer, with one flag added, and downloads it before running it:
#!/bin/sh
set -e
installer=$(mktemp)
trap 'rm -f "$installer"' EXIT
curl -fsSL https://mesmer.co/cursor-otel/install.sh -o "$installer"
sh "$installer" --scope userDownload first rather than piping curl into sh. In a pipe, a failed
download hands sh an empty script, which exits 0, so the MDM records
a success on a Mac where nothing was installed. This version exits with
curl's error instead.
That's the whole policy, and it works unchanged whether Rippling happens to run it as root or as the signed-in user. If nobody is signed in, which is normal right after a machine boots, it exits cleanly without installing anything. That's exactly why the policy has to be recurring rather than one-time: a recurring policy installs the first time it runs after someone actually logs in, and on a shared or multi-account Mac, whoever is signed in when it fires gets their own install.
A configuration profile carries the organisation token, so it never
has to appear in the script, in argv, or in an MDM run-script log.
Download mesmer-cursor-otel.mobileconfig
and edit it — fill in the token and two distinct UUIDs (one for the
profile, one for the settings payload inside it; macOS rejects a profile
that reuses one) and assign it at the device level, not the user level,
since a user-scoped assignment lands somewhere the hook never reads.
| Key | Required | Meaning |
|---|---|---|
MESMER_OTLP_TOKEN | yes | The organisation token — same name as the environment variable a single-machine install uses. Ask the Mesmer team for it. |
Order doesn't matter between the two pieces: if the script runs before the profile lands, the hook is registered and simply exports nothing until it does — nothing breaks and no one is blocked in the meantime.
A fleet, through Iru#
We support Iru (formerly Kandji) the same way. It's the same two pieces as Rippling, each one a Library Item assigned to the Blueprints your engineers' Macs are on. As above, this describes what to deploy rather than every click; Iru's own documentation has the current screens.
A Custom Script runs the same script as the Rippling policy. In the Library, add a Custom Script, assign it to your Blueprints, and paste this into Audit Script:
#!/bin/sh
set -e
installer=$(mktemp)
trap 'rm -f "$installer"' EXIT
curl -fsSL https://mesmer.co/cursor-otel/install.sh -o "$installer"
sh "$installer" --scope userDownloading first matters most here, because Iru turns the exit code into Pass or Error; a piped one-liner would show a pass on a Mac where the download failed. Leave Remediation Script empty. Iru runs scripts as root, which the installer handles: it installs for whoever is signed in.
Set Execution Frequency to Run daily, not Install once per
device. This is Iru's version of Rippling's "recurring" rule, with one
extra trap. Iru counts a script as done the first time it exits 0,
and the installer exits 0 when nobody is signed in. A new Mac usually
runs its Library Items during Liftoff, before anyone has logged in, so a
once-per-device script would pass and never run again, and nothing would
be installed. Run daily installs the next time it runs while someone
is signed in. On a shared Mac, each account gets its own install the
next time the script runs while that person is signed in, so someone who
signs in and out between two runs isn't covered until a later run
catches them. Run every 15 min narrows that gap, though no schedule
guarantees it. It also shortens the wait for the first install on any
Mac: if the first run lands before anyone has signed in, Run daily
waits up to a day for the next one. Once the hook is installed, it keeps
itself up to date, so the faster schedule adds nothing after that.
A Custom Profile carries the organisation token. Edit
mesmer-cursor-otel.mobileconfig
the same way as for Rippling: fill in the token and two distinct UUIDs.
Add it to the Library as a Custom Profile, assign it to the same
Blueprints, and limit it to Mac. Iru doesn't need the profile signed,
and it installs custom profiles on the device (System) channel, which is
where the hook reads the token. There's no device-versus-user choice to
get wrong here.
Iru shows the installer's output on each Mac's status page. That tells
you the script ran, not that telemetry is flowing. To check that, run
mesmer-cursor-doctor on the Mac, as described for an individual
engineer above. As with Rippling, the order the two
items land in doesn't matter.
Cursor Cloud Agents: install during the Build#
The hook installs from the repository's own .cursor/environment.json,
during the environment's Build, with a scope flag that changes how the
file is written:
{
"install": "curl -fsSL https://mesmer.co/cursor-otel/install.sh | sh -s -- --scope cloud-agent"
}Add that after your own install steps if you have any.
The token has to be a team secret or an environment secret, never
a user secret. User secrets are only added when an agent starts, and
aren't available during Builds — which is exactly when the installer
looks for one. Get the scope wrong and the install still "succeeds": it
fails open, logs a warning, and quietly exports nothing from every agent
that boots off that snapshot. The Build log is honest about it either
way — a healthy install prints the version, the hooks path and the
endpoint; a missing token prints warning: no MESMER_OTLP_TOKEN in the environment instead.
Because the Build sequence is strictly ordered — the environment clones
the repository and runs install to completion before it ever snapshots
the disk — there's no window where an agent could load a hook
configuration out from under it.
| Individual / MDM fleet | Cursor Cloud Agent | |
|---|---|---|
| Hooks file | ~/.cursor/hooks.json (user level) | <repo>/.cursor/hooks.json (project level) |
| Written by | install.sh (--scope user, the default) | install.sh --scope cloud-agent, during the Build |
| Written how | Merged into whatever is already there | Merged into whatever is already there |
| Lifetime | Until you uninstall | Until the next Build |
These two paths are kept deliberately disjoint. Don't commit the
.cursor/hooks.json a Build writes — it's regenerated every time, its
paths are only valid inside that VM, and committing it would make every
teammate's laptop fire the hook a second time.
What actually reaches Mesmer#
Once installed, every repository you open in Cursor is instrumented — work repositories, personal projects, a throwaway clone, all of them — because the hooks are registered once at the user level and fire for every session on the machine regardless of path. The unit of consent here is the machine, not the project.
Every content-capture control upstream exposes is on. Prompts, model
responses, model thoughts, shell commands, tool
calls, tool definitions, file reads and file edits are all exported as
text — as are MCP calls, including the server and tool name, the request
and the result. An MCP call is the one place an agent reaches outside the
repository, so whatever it sends and receives goes too. That's across
every hook event this integration registers — from sessionStart through
shell execution, file edits, tool calls and MCP calls, to stop.
Troubleshooting#
Telemetry stopped arriving. Open Cursor's Hooks output channel and
look for Invalid user config or Failed to parse user hooks configuration. Cursor silently discards an entire hooks.json if it
contains even one unrecognised event key — from any tool, not just this
one — and reports the failure nowhere else. A single bad entry added by
something unrelated disables every hook in the file, ours included.
Remove the offending entry and restart Cursor.
Nothing arrives right after installing. hooks.json is only read at
Cursor startup, so restart it, then run mesmer-cursor-doctor.
A token in your shell can win over what's stored. Credentials resolve
in this order: a managed configuration profile, then MESMER_OTLP_TOKEN
in the environment that launched Cursor, then the stored mesmer.json.
On an unmanaged machine, a MESMER_OTLP_TOKEN left exported in a shell
profile for any other reason will keep authenticating every span with
that value instead of the one you installed with. doctor reports which
source is actually in use.
doctor is the right tool for checking whether telemetry is actually
being delivered — trust its output over anything you inspect by hand.
If it doesn't resolve the issue, reach out to the Mesmer team.
Uninstalling.
~/.local/share/mesmer-cursor-otel/bin/mesmer-cursor-uninstallIt strips Mesmer's entries from hooks.json first, then removes the
install — that order matters, since the hook entries are absolute paths
into the install directory. Unrelated Cursor hooks are preserved.
