> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getunbound.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Pi

<Note>
  **Pi support is in Beta.**
</Note>

Pi is a terminal coding agent. Unbound enforces your tool policies on it through an extension that pi loads at the start of each session.

## Prerequisites

Before setting up the integration, ensure you have:

* **Unbound CLI**: version **1.16.19 or later**, installed and logged in — see the [CLI guide](/cli/overview)
* **Pi**: version **0.87.1 or later**
* **Node.js**: version **22 or later**
* **pi-mcp-adapter**: version **2.21.0 or later**, needed only for MCP rules — see [MCP](#mcp)

## Setup with Unbound CLI

```bash theme={null}
unbound setup pi
```

This drops the Unbound extension at `~/.pi/agent/extensions/unbound/index.js`, stores your API key in `~/.unbound/config.json`, and reports the install to Unbound. If you set `PI_CODING_AGENT_DIR`, the extension goes into that folder instead. **Start a new pi session after setup** — a session already running does not pick it up.

Pi is not part of `unbound setup --all`. From CLI 1.16.20, [`unbound onboard`](/cli/tool-setup#one-step-onboarding) sets it up when it detects pi on the device and skips it otherwise; `unbound setup pi` sets it up either way.

To remove the Unbound configuration:

```bash theme={null}
unbound setup pi --clear
```

## What is enforced

Pi asks Unbound before each call, so your rule decides whether it runs.

| Area | Covered |
| - | - |
| **Terminal** | Its `bash` and `powershell` tools, and commands you type yourself with `!cmd` |
| **Read**, **Write**, **Edit** | Pi's own file tools |
| **Search** | `grep`, `find` and `ls` |
| **Delete** | Pi has no delete tool. A delete through the shell is caught by a terminal rule |
| **MCP** | Calls made through `pi-mcp-adapter` 2.21.0 or later — see [MCP](#mcp) |
| **Data** | The prompt is checked as it is submitted |

Every call is recorded, and the prompt text is captured with it.

### What each action does

| Action | On pi |
| - | - |
| **Block** | The call is stopped, and your custom block message is shown in pi |
| **Warn** | The developer is asked to confirm. On a headless run — `pi -p`, or the JSON mode — there is nobody to ask, so the call is blocked |
| **Audit** | The call runs and is recorded |
| **Require Slack Approval** | Pi cannot hold a call open for approval, so the developer is asked to confirm instead. Use **Block** where someone else must decide |

<Note>
  **If a device cannot reach Unbound, the call runs** — the extension waits up to 20 seconds, then lets it through. Your organization can require the opposite, so an unreachable check blocks instead. See [Settings](/dashboard/settings).
</Note>

## MCP

**MCP rules apply on pi when its MCP calls go through `pi-mcp-adapter`, version 2.21.0 or later.** The adapter asks Unbound before each call, so your rule decides whether it runs.

| | |
| - | - |
| **Covered** | Every kind of call the adapter makes: its `mcp` tool, per-server and direct tools, scripts, resource reads and MCP UI |
| **Not covered** | Calls made through an adapter older than 2.21.0, through any other MCP extension, or on a pi build that predates extension events. They run unchecked, and nothing in pi says so |

**Check the adapter version on each device.** An older adapter is the first thing to rule out when an MCP rule does not fire on pi.

What the developer sees:

| Action | On an MCP call in pi |
| - | - |
| **Block** | The call is stopped and the developer is shown the policy's reason |
| **Warn** | The developer is asked to confirm. On a headless run there is nobody to ask, so the call is blocked |

When a rule allows a call, the adapter's own approval prompt still applies if the developer has it turned on.

### How a server is identified

Unbound identifies an MCP server from the adapter's configuration files — the server's `url`, or its `command` and arguments. It reads them only when they leave no doubt about what the adapter loaded:

* every file is plain JSON, with no comments and no trailing commas
* the files are in the adapter's standard locations, and pi was not started with `--mcp-config`
* no file pulls servers in from elsewhere (imports, plugins, host discovery) or defines a socket server
* none of them has changed since the pi session started

Otherwise the server is left unidentified rather than guessed. **If your organization restricts MCP servers to a sanctioned list, calls to an unidentified server are denied.** Without that restriction, rules still match on the server and tool name.

Credentials in `env`, `headers` and OAuth settings are never read. A credential written into a server's `url` or arguments is sent as part of that server's identity, so keep secrets out of both.

### Limits

* **Another extension can answer first.** If a different pi extension handles MCP approvals before Unbound does, that call is not checked.
* **Very large arguments are checked in part.** Arguments up to 512 KB are checked whole. Beyond that, the largest values are checked at their start and end only; every other argument is still checked whole.

## Account identity

When pi is signed in to Anthropic, Unbound records the signed-in account — its email and plan. When pi uses an API key from any provider, Unbound records `api_key` only.

## Limitations

Pi has no managed configuration, so a developer can step around the extension. Running `pi --no-extensions`, or pointing `PI_CODING_AGENT_DIR` at a different folder, starts pi without it.

[Discovery](/cli/discovery) still reports every pi install, so you can see where it is in use.

## Deploying through MDM

**MDM onboarding sets Pi up only on devices that have pi.** The [Python onboarding command](/mdm-integrations/jamf/python) and `sudo unbound onboard` look for pi across every user on the device: the `pi` command, or pi's own files in a home (`.pi/agent/auth.json`, `.pi/agent/sessions`). Where pi is found, the extension is installed for every user on that device. Where it is not, the Pi step is skipped and reported as skipped, not as a failure.

A device picks this up the next time its policy runs the onboarding command. Clearing (`--clear`) always removes the Pi extension, whether or not pi is still installed.

The [signed macOS package](/mdm-integrations/jamf/binary) does not set up Pi. There, or to install on every device regardless of detection, run the `pi/mdm/setup.py` script from your device management platform. See [MDM Integrations](/mdm-integrations/overview) for deployment guides.

<CardGroup cols={2}>
  <Card title="Agent Coverage" icon="table-list" href="/policies/agent-coverage">
    What each agent supports, side by side
  </Card>

  <Card title="Tool Policies" icon="shield" href="/policies/tool-policies">
    Configure security guardrails for AI tools
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.