apps/opik-documentation/documentation/fern/docs-v2/cost_intelligence/install/mdm.mdx
This is the path we recommend for most organizations. You deliver the configuration as a file on each device using the device management you already run: Jamf, Kandji, Intune, JumpCloud, or Linux provisioning tooling.
Two reasons it's usually the better choice:
The configuration itself is identical to the managed settings path; only the delivery differs. Claude Code reads it locally instead of fetching it from Anthropic.
A single JSON drop-in, written as root:
| OS | Path |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/managed-settings.d/50-opik-cipx.json |
| Linux | /etc/claude-code/managed-settings.d/50-opik-cipx.json |
| Windows | C:\Program Files\ClaudeCode\managed-settings.d\50-opik-cipx.json |
Claude Code deep-merges everything in managed-settings.d/ with any existing managed-settings.json, so this is additive: it won't clobber other policies you already push.
Settings delivered this way are org-enforced: users cannot disable the plugin or override the OPIK_CIPX_* environment, and they apply to every user on the device.
Generate it from Cost Intelligence → Organization Policies → Copy settings file, or start from this template:
{
"extraKnownMarketplaces": {
"opik-enterprise": {
"source": { "source": "github", "repo": "comet-ml/cost-intelligence-proxy" },
"autoUpdate": true
}
},
"enabledPlugins": {
"opik-cipx@opik-enterprise": true
},
"env": {
"OPIK_CIPX_BASE_URL": "https://www.comet.com/opik/api",
"OPIK_CIPX_WORKSPACE": "your-org-cc-workspace",
"OPIK_CIPX_API_KEY": "<workspace-scoped API key>",
"OPIK_CIPX_PROJECT": "claude-code"
}
}
Validate before shipping. A malformed payload is silently ignored by some MDMs, which gives you a rollout that reports success and enforces nothing:
plutil -convert json -o /dev/null 50-opik-cipx.json # macOS
python3 -m json.tool 50-opik-cipx.json > /dev/null # any platform
Ship the JSON to the device (a pkg payload, or a "files and script" policy), then place it as root:
#!/bin/bash
set -euo pipefail
DIR="/Library/Application Support/ClaudeCode/managed-settings.d"
/bin/mkdir -p "$DIR"
/bin/cp 50-opik-cipx.json "$DIR/50-opik-cipx.json"
/usr/sbin/chown root:wheel "$DIR/50-opik-cipx.json"
/bin/chmod 644 "$DIR/50-opik-cipx.json"
MDM script runners already execute as root. Use absolute tool paths; those runners typically have a minimal PATH.
The whole install fits in one JumpCloud Command, run as root. Inline the JSON with a heredoc so no separate file transfer is needed:
#!/bin/bash
set -euo pipefail
DIR="/Library/Application Support/ClaudeCode/managed-settings.d"
/bin/mkdir -p "$DIR"
TMP="$(/usr/bin/mktemp)"
/bin/cat > "$TMP" <<'JSON'
{ ...your configuration... }
JSON
/usr/bin/plutil -convert json -o /dev/null "$TMP" # validate; abort if bad
/bin/mv "$TMP" "$DIR/50-opik-cipx.json"
/usr/sbin/chown root:wheel "$DIR/50-opik-cipx.json"
/bin/chmod 644 "$DIR/50-opik-cipx.json"
Two operational notes specific to JumpCloud:
Same JSON, different path. Any provisioning tool that can write a root-owned file works: Ansible, Puppet, Chef, or a package postinstall:
install -d -m 755 /etc/claude-code/managed-settings.d
install -m 644 -o root -g root \
50-opik-cipx.json /etc/claude-code/managed-settings.d/50-opik-cipx.json
As the logged-in user, in a fresh Claude Code session (managed settings load at startup):
claude plugin list
cat "/Library/Application Support/ClaudeCode/managed-settings.d/50-opik-cipx.json"
opik-cipx status
Then confirm traces are arriving in the target workspace.
Two things worth separating, because it determines how often you touch your MDM:
So you push this file once, and manage cost policy from the Opik dashboard afterwards.
sudo rm "/Library/Application Support/ClaudeCode/managed-settings.d/50-opik-cipx.json"
Then, on each device, opik-cipx uninstall removes the hook, stops the daemon, and deletes local state.