# Claude Code setup

Source: https://pushary.com/docs/agents/guides/claude-code
Last updated: 2026-11-08

> Set up Claude Code notifications and approvals in one command. Approve risky commands, file edits and Claude's own questions from your phone or Mac.

Pushary sends Claude Code notifications, permission prompts and questions to your phone, so you can approve `rm -rf ./dist` or pick an auth strategy while you are away from your desk. One command installs Claude Code hooks (small programs Claude Code runs around each tool call) and the Pushary MCP server. A gated action waits for your answer before it runs.

## Set it up [#set-it-up]

```bash
npx pushary@latest setup
```

On Windows PowerShell, run `npx.cmd pushary@latest setup`. The CLI needs Node.js 20.17+, 22.13+ or 23.5+.

<Steps>
  <Step>
    ### Scan the QR code with your phone [#scan-the-qr-code-with-your-phone]

    Setup shows a QR code first. Scan it with your phone's camera or the Pushary app. Sign in or create an account, check that the code on your phone matches the one in your terminal, then tap **Approve** on &#x2A;*Connect this computer?** and allow notifications.

    New accounts start a 3-day free trial, then pay $9.99 a month. A card is needed to start the trial. If this computer already has a Pushary key, setup reuses it and skips this step.
  </Step>

  <Step>
    ### Pick Claude Code [#pick-claude-code]

    Setup asks &#x2A;*Which agents do you use?** with the agents it found already selected. Keep Claude Code selected. To skip the question, run `npx pushary@latest setup --agents claude_code`.
  </Step>

  <Step>
    ### Answer the test question [#answer-the-test-question]

    Setup sends a test push, then a test question. Answer it on your phone. The terminal says **You're set** once your answer arrives.
  </Step>

  <Step>
    ### Restart Claude Code [#restart-claude-code]

    Claude Code reads hooks when it starts. Quit every open session and start a new one.
  </Step>
</Steps>

On a Mac you can use [Pushary Isle](/docs/agents/mac-app) instead. It connects Claude Code for you and needs no Node.

## What setup changes on your computer [#what-setup-changes-on-your-computer]

| File                                            | What changes                                                                                                               |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `~/.claude.json`                                | Adds the `pushary` MCP server (`https://pushary.com/api/mcp/mcp`) with your key. The file is written readable by you only. |
| `~/.claude/settings.json`                       | Adds the Pushary hooks and allows `mcp__pushary__*`, so Pushary's own tools run without a prompt.                          |
| `~/.claude/skills/pushary/SKILL.md`             | The Pushary skill, which tells Claude Code when to notify you and when to ask.                                             |
| `~/.pushary/config.json` and your shell profile | Your API key, as `PUSHARY_API_KEY`.                                                                                        |
| `CLAUDE.md` in the current folder               | Only if you say yes when asked. A section that tells agents in this repo to ask through Pushary.                           |

Setup also installs the `pushary` package globally, so each hook starts fast.

### The hooks that decide [#the-hooks-that-decide]

| Hook                | What it does                                                                                                                                                                                                                                                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PreToolUse`        | The approval gate. Runs before `Bash`, `Write`, `Edit`, `MultiEdit`, `NotebookEdit`, `PowerShell` and `Monitor`, and before Claude's own questions (`AskUserQuestion`) and plan approval (`ExitPlanMode`). A second entry covers MCP tools (`mcp__.*`). It does nothing unless a Team approval route sends the call to someone else in auto mode. |
| `PermissionRequest` | Runs when Claude Code is about to show its own permission prompt, for any tool. Your approval mode decides whether it goes to your phone.                                                                                                                                                                                                         |
| `PermissionDenied`  | Runs when auto mode blocks an action. Pushary asks you, and a yes lets Claude Code retry it once.                                                                                                                                                                                                                                                 |
| `Elicitation`       | Sends an MCP server's request for input to your phone.                                                                                                                                                                                                                                                                                            |

These four have a 600-second hook budget. That is a maximum runtime, not a promise to hide Claude Code's terminal prompt while the phone waits. The approval path and your rules decide how the wait works.

Twelve more hooks only record activity for your audit log and session list: `PostToolUse`, `Stop`, `UserPromptSubmit`, `Notification`, `SessionStart`, `SessionEnd`, `StopFailure`, `SubagentStart`, `SubagentStop`, `PreCompact`, `PostCompact` and `TeammateIdle`. Each times out after 10 seconds. Claude Code builds older than 2.0, and versions setup cannot detect, get fewer of them.

`UserPromptSubmit` saves the start of your prompt as the session's task title. Set `PUSHARY_TASK_TITLES=off` to turn that off.

Each notification names its session as `Claude Code - <project folder>`, so you can tell parallel sessions apart.

## Check that it works [#check-that-it-works]

```bash
npx pushary@latest doctor
```

Doctor checks your key and plan, then Claude Code's MCP server, hooks, auto-allowed tools and skill. It sends a real test push. Add `--roundtrip` to send a test question too.

Then try a real approval. A new workspace starts on **Always**, so it reaches your phone while you sit at your computer. If yours is on When away, decisions stay in Claude Code's own prompt while you are there. To test from your desk, open the Pushary app, tap **Questions & approvals** at the top of your inbox, and pick **Always**.

Ask Claude Code: `Create a file called pushary-test.txt that says hello.` Your phone buzzes with the Write request. Tap **Approve** and the file appears. Switch back to **When away** when you are done.

## Answer from your terminal or phone [#answer-from-your-terminal-or-phone]

With **Pushary CLI 1.9.66**, supported Claude Code approvals are answerable in both places at once. Answer whichever is in front of you. The first answer wins, and answering in the terminal closes the phone question.

This applies to CLI-managed interactive sessions in `default` or `acceptEdits` mode when only Pushary's built-in defaults ask about the call. It does not add a prompt to actions already permitted by the rules below. Rules you wrote, presets you chose, temporary modes and Team approval routes keep their configured behavior. Bypass and headless sessions keep the existing phone wait. Pushary for Mac is unchanged in this release.

Update the CLI and its hook registration:

```bash
npx pushary@latest upgrade
pushary doctor
```

Restart Claude Code after upgrading. The refreshed permission hook lets the phone question stay open for up to 10 minutes. An older registration keeps its previous window, with the terminal still answerable from the start. If the phone cannot be reached, answer at the terminal. If nobody answers the supported default prompt, the action stays pending.

Read the [1.9.66 release story](/blog/claude-code-approve-terminal-or-phone) for the before-and-after behavior.

## What Pushary can and cannot enforce on Claude Code [#what-pushary-can-and-cannot-enforce-on-claude-code]

* **Gated before they run:** shell commands (`Bash`, `PowerShell`, `Monitor`) and file changes (`Write`, `Edit`, `MultiEdit`, `NotebookEdit`).
* **Asked in every permission mode:** Claude's questions (`AskUserQuestion`) and plan approval (`ExitPlanMode`), because they are questions, not permissions.
* **Only when Claude Code would ask:** any other tool, including reads, web fetches and other MCP servers' tools, reaches your phone only through Claude Code's own permission prompt (`PermissionRequest`).
* **Your own allow rules:** a command your Claude Code allow rules already permit runs without a Pushary question, unless a rule you wrote in Pushary covers it. Pushary reads allow rules from your user, local and managed settings. It ignores allow rules in a repo's committed `.claude/settings.json`, so a cloned repo cannot switch Pushary off.

### Claude Code's permission mode decides first [#claude-codes-permission-mode-decides-first]

| Claude Code mode          | What Pushary does                                                                                                                                                                                               |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `default`                 | Gates as described above.                                                                                                                                                                                       |
| `acceptEdits`             | Steps aside for edits, which Claude Code already accepts. Shell commands still gate.                                                                                                                            |
| `plan`, `auto`, `dontAsk` | Steps aside, because Claude Code already owns these decisions. In `auto`, a call the classifier sends to a prompt still reaches you through `PermissionRequest`, and a blocked call through `PermissionDenied`. |
| `bypassPermissions`       | Keeps gating, but only calls with a consequence. Claude Code asks nothing, so Pushary is the only check.                                                                                                        |

The kill switch, your deny rules and a scope you approved apply in every mode. Two environment variables change the table:

* `PUSHARY_RESPECT_PERMISSION_MODE=0` gates in every mode, whatever Claude Code's mode is.
* `PUSHARY_DEFER_BYPASS=1` steps aside in `bypassPermissions` too.

### Run --dangerously-skip-permissions and still approve from your phone [#run---dangerously-skip-permissions-and-still-approve-from-your-phone]

`claude --dangerously-skip-permissions` starts Claude Code in `bypassPermissions`. Claude Code then runs every tool without a prompt, but Pushary's hooks still run. Routine commands and edits run without a question. A step that is hard to take back waits for your phone, such as `gh pr merge`, `git push --force`, pushing a tag, `npm publish`, `vercel --prod`, `eas submit`, `kubectl apply`, `gh workflow run`, `rm -rf`, an `UPDATE` to a production database, a `curl` or `Invoke-RestMethod` that sends data off your machine, or an upload with `scp` or `rsync`. So does a secret written to a host, such as `gh secret set`, `vercel env add`, `doppler secrets set` or `wrangler secret put`, and a `git config` setting that runs a program, such as `core.fsmonitor` or an alias that starts with `!`. So does an edit to `.env`, a CI workflow file, Claude Code's own settings or hooks, `.mcp.json`, a git hook or `.git/config`, a file in `~/.ssh`, a shell profile such as `~/.zshrc`, a launch agent or a file in `/etc`, whether Claude makes it with Edit or with a shell command such as `cp`, `sed -i` or `>`. Pushary does not read a script Claude writes and then runs, so this is a check on what the command shows, not a sandbox. A `git push` to a remote the repository already has runs, and so do reads over `ssh` such as `uptime` or `docker ps`. A push to a URL or host address, such as `git push git@example.com:acme/app.git HEAD`, waits for your phone, and so do `git remote add` and `git remote set-url`. Some pushes are not checked yet: `git subtree push`, `git lfs push`, `git.exe` in Git Bash, a destination given by a command substitution, and a setting such as `remote.pushDefault` or `branch.<name>.pushRemote`. These checks apply only to Claude Code in this mode: other agents, and every other Claude Code mode, ask about what they asked about before. To be asked about pushes too, add a rule for `Bash(git push:*)`. A rule you wrote, a preset you picked, an approval route, a mode override, the kill switch and a scope you approved all still apply, and decision activity follows your audit-history settings. The Mac app does this now. With the `pushary` CLI it needs 1.9.97 or later, and 1.9.100 or later for a push to an address and `git remote add`. A card that asks only because the step is risky says so. If you would rather not be stopped at all, open [Settings, Connections](https://pushary.com/dashboard/agent/settings) and set "When your agent skips permissions" to "Never stop me". Every step then runs, a merge included. The kill switch and approval routes still apply, and so does a rule you wrote, for the commands it names. See [Claude Code dangerously skip permissions](/claude-code-dangerously-skip-permissions) for what the flag turns off.

### Choose what happens when you do not answer [#choose-what-happens-when-you-do-not-answer]

For the simultaneous terminal and phone path above, an unanswered default prompt stays open. The timeout table below describes the existing wait used outside that path. A phone-only rule with an explicit auto-approve or auto-deny timeout still follows that rule.

Each rule has a timeout and an **If you don't respond** action. A new workspace starts with one rule for every tool (`*`): wait 60 seconds, then **Ask in terminal**. That hands the call back to Claude Code's own prompt, so nothing runs without an answer.

Pushary also ships built-in rules, but your own `*` rule outranks them. They only apply to a workspace that has no `*` rule, and every workspace created by setup or sign-up has one. They are:

| Tool            | Timeout | If you don't respond         |
| --------------- | ------- | ---------------------------- |
| `Bash`          | 60s     | Auto deny                    |
| `Write`         | 60s     | Ask in terminal              |
| `Edit`          | 45s     | Ask in terminal              |
| `Read`          | 0s      | Auto approve, without asking |
| Everything else | 60s     | Ask in terminal              |

A rule you write for one tool, such as `Bash`, beats your `*` rule. The other choices are **Auto approve**, **Auto deny** and **Wait for phone**. Change them at [pushary.com/dashboard/agent/policies](https://pushary.com/dashboard/agent/policies).

## Set it up by hand [#set-it-up-by-hand]

Use setup if you can. It writes up to 16 hook events (fewer on Claude Code builds older than 2.0) and pins each command to its full path. The minimum for approvals and activity looks like this.

Install the hook commands globally, unless `which pushary-hook` already prints a path. An existing install under `@pushary/agent-hooks` provides the same commands, and npm refuses to install over them.

```bash
npm install -g pushary
```

Add the MCP server to `~/.claude.json`:

```json
{
  "mcpServers": {
    "pushary": {
      "type": "http",
      "url": "https://pushary.com/api/mcp/mcp",
      "headers": {
        "Authorization": "Bearer pk_xxx.xxx"
      }
    }
  }
}
```

Add the hooks and the tool permission to `~/.claude/settings.json`:

```json
{
  "permissions": {
    "allow": ["mcp__pushary__*"]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit|MultiEdit|NotebookEdit|PowerShell|Monitor|AskUserQuestion|ExitPlanMode",
        "hooks": [{ "type": "command", "command": "pushary-hook", "timeout": 600 }]
      },
      {
        "matcher": "mcp__.*",
        "hooks": [{ "type": "command", "command": "pushary-hook", "timeout": 600 }]
      }
    ],
    "PermissionRequest": [
      {
        "hooks": [{ "type": "command", "command": "pushary-permission-hook", "timeout": 600 }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash|Write|Edit|MultiEdit|NotebookEdit|PowerShell|Monitor|TodoWrite|Task|WebFetch|WebSearch",
        "hooks": [{ "type": "command", "command": "pushary-post-hook", "timeout": 10 }]
      }
    ],
    "Stop": [
      {
        "hooks": [{ "type": "command", "command": "pushary-stop-hook", "timeout": 10 }]
      }
    ]
  }
}
```

Save your key in your shell profile:

```bash
export PUSHARY_API_KEY="pk_xxx.xxx"
```

Restart Claude Code, then run `npx pushary@latest doctor`. It reports any hook event that is missing.

The Pushary skill is optional. Setup installs it for you. By hand:

```bash
npx skills add Pushary/pushary-skill
```

## Verify a real approval [#verify-a-real-approval]

A test push, or `npx pushary@latest doctor --roundtrip`, proves that delivery works and that your answer comes back. It does not run Claude Code's hooks. The Mac app's phone test does not either. This check proves a real approval reached Claude Code.

<Steps>
  <Step>
    ### Restart Claude Code with your normal permissions [#restart-claude-code-with-your-normal-permissions]

    Finish sign-in and checkout, connect Claude Code, then restart it so it reads the installed hooks. If your current mode auto-allows the action, or keeps approval in the terminal, the check cannot prove a phone answer was used.
  </Step>

  <Step>
    ### Make a new target path and approve one Write [#make-a-new-target-path-and-approve-one-write]

    In an empty scratch folder, in the terminal where you will run the check:

    ```bash
    TARGET="$PWD/pushary-activation-$(node -p 'require("node:crypto").randomUUID()').txt"
    printf '%s\n' "$TARGET"
    ```

    Ask Claude Code: &#x2A;*"Use the Write tool to create this exact absolute path, containing `Pushary activation check`. Do not run other commands or answer any approval yourself."** Paste the printed path. Approve the Write yourself on your chosen surface. Check that the file appears with that text.
  </Step>

  <Step>
    ### Read the evidence [#read-the-evidence]

    ```bash
    npx pushary@latest doctor --activation claude_code --activation-target "$TARGET" --no-push --json
    ```

    It reads the Write decision for that exact path from the last 10 minutes, on this machine and workspace. Save the decision, session and tool-call IDs, the answer source and transport, with `claude --version`, `pushary --version`, and the Mac app version if you use it. The evidence excludes credentials and file contents.
  </Step>

  <Step>
    ### Repeat with a denial [#repeat-with-a-denial]

    Make another new target and repeat, denying the request this time. Check that the file was not created.
  </Step>

  <Step>
    ### Restart and run a negative control [#restart-and-run-a-negative-control]

    Restart the agent and repeat with a new target. For the negative control, remove only the Pushary hooks in an isolated test profile and use another new target. A test push may still pass, but that attempt must not produce matching hook evidence. Restore the test profile afterwards.
  </Step>
</Steps>

Never reuse a target. Its old approval does not test your current configuration.

| Result                 | What it means                                                                                                                                                                             |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `approved_executed`    | You approved, and Claude Code reported the Write completed after your answer.                                                                                                             |
| `denied_observed`      | You denied, and Claude Code reported the denial. Some host paths send no denial event, and those stay `unverified`. A missing completion event alone never proves a denial.               |
| `unexpected_execution` | Claude Code reported the Write completed after a denial. Investigate it.                                                                                                                  |
| `tool_failed`          | Claude Code reported a tool error.                                                                                                                                                        |
| `unverified`           | No proof either way: an unknown answer source, a synthetic question, a duplicate decision, a reused target, missing hooks, stale evidence, or client clocks that are missing or disagree. |

The check never creates a question or changes permissions. Queued events keep their original runtime timestamp. Results are what the agent reported at runtime, not independently attested execution. A pass covers that one invocation, not every agent or future request.

If no approval appeared, check Claude Code's permission mode and your Pushary approval mode before you repair anything. If the CLI owns your hooks, run `npx pushary@latest doctor`. If the Mac app owns them, use its agent setup and recovery controls. Keep the existing owner.

To cancel a test, cancel it in the agent or on its approval surface. The hook owns its own deadline. Stopping the read-only check does not cancel a pending agent request.

## Troubleshooting [#troubleshooting]

### Why did my phone not buzz? [#why-did-my-phone-not-buzz]

* You did not restart Claude Code after setup. Hooks load at start.
* You are at your computer in When away mode, so the decision stays in Claude Code's prompt. To test, set **Questions & approvals** to **Always** in the app.
* Claude Code is in `plan`, `auto`, `dontAsk` or `acceptEdits` mode, which Pushary respects. See the table above.
* Your Claude Code allow rules already permit the command.
* Run `npx pushary@latest doctor` to check the key, the plan and each hook.

### `which pushary-hook` prints nothing [#which-pushary-hook-prints-nothing]

Run `npx pushary@latest setup` again. It installs the hook commands globally.

### Setup says Pushary for Mac handles Claude Code's approvals [#setup-says-pushary-for-mac-handles-claude-codes-approvals]

The Mac app already owns Claude Code's hooks. Setup reuses the app's key and keeps the app's hooks. To have the CLI own them instead, run `npx pushary@latest setup --take-over-hooks`.

### More help [#more-help]

* [Claude Code notifications not working](/claude-code-notifications-not-working): fixes for hooks and alerts that stay silent.
* [Claude Code hooks explained](/blog/claude-code-hooks-explained): every hook event and what it can return.
* [Control Claude Code from your phone](/control-claude-code-from-phone): approve, answer or stop a session away from the terminal.

## Next steps [#next-steps]

<Cards>
  <Card title="Set your approval rules" description="Choose which actions run on their own and which wait for you." href="/docs/agents/policies" />

  <Card title="Claude Code notifications" description="Every way to get Claude Code on your phone, including Remote Control." href="/claude-code-notifications" />

  <Card title="Stop a session with the kill switch" description="Stop one agent session, or all of them, from your phone." href="/docs/agents/kill-switch" />
</Cards>