Docs

JAMF Deployment Guide

Deploy Agent Keeper Claude Code security hooks to your entire Mac fleet using JAMF Pro. Every developer gets Runtime Shield monitoring automatically, no manual setup on their end.

Overview

Agent Keeper hooks are configured via a JSON file at ~/.claude/settings.json. JAMF pushes this file to all Macs that have Claude Code installed. Once deployed, every Claude Code session is monitored through the Agent Keeper dashboard.

What gets deployed:

  • PreToolUse hook, evaluates every tool call (Bash, Write, Read, etc.) before execution
  • PostToolUse hook, logs tool executions to the audit trail
  • UserPromptSubmit hook, scans user prompts for injection attempts
  • SessionStart hook, registers the workstation on checkin

Requirements:

  • JAMF Pro (or JAMF Now Business)
  • Macs with Claude Code installed
  • A Agent Keeper account (Free tier works for 1 workstation, Team for unlimited)
  • An API key from the Agent Keeper dashboard

Step 1: Get your API key

  1. Log in to YOUR_AGENTKEEPER_URL
  2. Go to Settings → API Keys
  3. Click Create Key and name it "JAMF Fleet" or similar
  4. Copy the key (starts with ak_live_)

Keep this key secure. It authenticates all your fleet's hook calls.

Step 2: Create the configuration payload

Create the settings.json content. Replace YOUR_API_KEY with the key from Step 1:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/evaluate",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|Edit|Write|Read|Glob|Grep|WebFetch|WebSearch",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/evaluate",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash|Edit|Write|Read|Glob|Grep|WebFetch|WebSearch",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/audit",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/checkin",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Step 3: Create the JAMF script

In JAMF Pro, go to Settings → Computer Management → Scripts → New.

Name: Deploy Agent Keeper Claude Code Hooks

Script:

#!/bin/bash
# Deploy Agent Keeper Claude Code security hooks
# This script writes the hooks configuration for the logged-in user.
# Run as: current user (not root), Claude Code reads from ~/.claude/

LOGGED_IN_USER=$(stat -f "%Su" /dev/console)
CLAUDE_DIR="/Users/${LOGGED_IN_USER}/.claude"
SETTINGS_FILE="${CLAUDE_DIR}/settings.json"

# Create .claude directory if it doesn't exist
mkdir -p "${CLAUDE_DIR}"
chown "${LOGGED_IN_USER}:staff" "${CLAUDE_DIR}"

# Check if settings.json already exists
if [ -f "${SETTINGS_FILE}" ]; then
    # Backup existing settings
    cp "${SETTINGS_FILE}" "${SETTINGS_FILE}.backup.$(date +%Y%m%d%H%M%S)"
    echo "Backed up existing settings.json"

    # Merge: if existing file has hooks, we need to be careful not to overwrite
    # other settings. For simplicity, we overwrite the hooks section only.
    # If you need to preserve other settings, use jq to merge.
fi

# Write the hooks configuration
cat > "${SETTINGS_FILE}" << 'HOOKS_EOF'
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/evaluate",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|Edit|Write|Read|Glob|Grep|WebFetch|WebSearch",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/evaluate",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash|Edit|Write|Read|Glob|Grep|WebFetch|WebSearch",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/audit",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "http",
            "url": "https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/checkin",
            "headers": {
              "Authorization": "Bearer YOUR_API_KEY"
            },
            "timeout": 10
          }
        ]
      }
    ]
  }
}
HOOKS_EOF

# Set correct ownership and permissions
chown "${LOGGED_IN_USER}:staff" "${SETTINGS_FILE}"
chmod 644 "${SETTINGS_FILE}"

echo "Agent Keeper hooks deployed for ${LOGGED_IN_USER}"
exit 0

Important: Replace YOUR_API_KEY in the script with your actual Agent Keeper API key before saving.

Step 4: Create the JAMF policy

  1. Go to Computers → Policies → New
  2. General:
    • Display Name: "Deploy Agent Keeper Claude Code Hooks"
    • Trigger: Recurring Check-in (or Login)
    • Execution Frequency: Once per computer
  3. Scripts:
    • Add the script from Step 3
  4. Scope:
    • Target: Smart Group of Macs with Claude Code installed (see below)
  5. Save

Smart Group: Macs with Claude Code

Create a Smart Group to target only Macs that have Claude Code:

  • Criteria: Application Title, is, Claude Code
  • Or: Files, /usr/local/bin/claude, exists

This ensures you only deploy hooks to machines where Claude Code is actually installed.

Step 5: Verify deployment

After the policy runs:

  1. Open the Agent Keeper dashboard
  2. Workstations should appear as developers start Claude Code sessions
  3. Each workstation shows up with its hostname, developer name, and shield status

You can also verify locally on any Mac:

cat ~/.claude/settings.json | python3 -m json.tool

Alternative: Intune (Windows/Mac)

For Microsoft Intune, use a Custom Configuration Profile (macOS) or PowerShell script (Windows):

macOS (Intune)

Create a custom profile with a shell script (same script as JAMF above). Deploy via Devices → macOS → Shell scripts.

Windows

# Deploy Agent Keeper hooks for Windows Claude Code users
$claudeDir = "$env:USERPROFILE\.claude"
$settingsFile = "$claudeDir\settings.json"

if (-not (Test-Path $claudeDir)) {
    New-Item -ItemType Directory -Path $claudeDir -Force
}

# Write hooks config (same JSON as above)
@"
{
  "hooks": {
    ...same JSON content...
  }
}
"@ | Set-Content -Path $settingsFile -Encoding UTF8

Write-Host "Agent Keeper hooks deployed for $env:USERNAME"

Alternative: Kandji

In Kandji, use a Custom Script library item with the same bash script from Step 3. Set the execution context to "Current User" and assign to the appropriate Blueprint.

Updating hooks

To update the hooks configuration (e.g., rotate API key), update the script in JAMF and set the policy execution frequency to "Ongoing" temporarily. After all machines check in, set it back to "Once per computer."

Troubleshooting

Hooks not working after deployment

  1. Check the file exists: ls -la ~/.claude/settings.json
  2. Check the file is valid JSON: python3 -m json.tool ~/.claude/settings.json
  3. Check file permissions: Should be readable by the user (644)
  4. Check Claude Code sees it: In Claude Code, run /hooks to list active hooks
  5. Check API key is valid: The key should start with ak_live_

Workstation not appearing in dashboard

The workstation registers on the first SessionStart hook call. Make sure:

  • The API key is valid and active
  • The Agent Keeper endpoint is reachable: curl -s https://YOUR_AGENTKEEPER_URL/api/v1/claude-code/health
  • Claude Code was started AFTER the settings.json was deployed

Conflict with existing settings.json

If a developer already has a ~/.claude/settings.json with their own settings, the JAMF script will overwrite it. To merge instead:

# Install jq if not present
brew install jq 2>/dev/null || true

# Merge hooks into existing settings
if [ -f "${SETTINGS_FILE}" ]; then
    jq -s '.[0] * .[1]' "${SETTINGS_FILE}" /tmp/agentkeeper-hooks.json > "${SETTINGS_FILE}.merged"
    mv "${SETTINGS_FILE}.merged" "${SETTINGS_FILE}"
fi

Security considerations

  • The API key in settings.json is write-only. It can only send security events and check policies. It cannot read data, list hosts, or modify settings.
  • The hooks make outbound HTTPS calls to YOUR_AGENTKEEPER_URL on port 443.
  • Hook calls are non-blocking with a 10-second timeout. If the endpoint is unreachable, Claude Code continues normally.
  • No data leaves the developer's machine except: tool name, tool input summary, hostname, and session metadata. File contents are NOT sent (only file paths for Read/Write operations).

Need help?