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

# Introduction

> Connect Claude, Cursor, and other MCP-compatible AI assistants directly to the Punch Rescue Public API — declare, read, re-categorize, and resolve emergencies in natural language.

## Overview

The **Punch Rescue MCP server** exposes the [Public API v1](/api-reference/introduction) over the [Model Context Protocol](https://modelcontextprotocol.io) (MCP). MCP is an open standard that lets AI assistants call external tools in a structured, typed way — so an assistant connected to the Rescue MCP server can discover an org's categories, declare and resolve emergencies, and read the audit log on your behalf.

Every tool maps onto the same REST endpoints, auth, and permission model as the Public API. There's nothing new to learn server-side: the key that works against `https://api.punchrescue.com/api/public/v1` is the same key you use here.

<Note>
  The MCP server is **read/write** but scoped exactly like your API key. A `read`-only key sees only the read tools; write tools appear (and succeed) only when the key was issued with `write` permission. Every tool is scoped to a single `orgId` your key's subject covers.
</Note>

## Endpoint

| Transport       | URL                               |
| --------------- | --------------------------------- |
| Streamable HTTP | `https://api.punchrescue.com/mcp` |

The server speaks the streamable-HTTP MCP transport (protocol version `2024-11-05`). Point any MCP-compatible client at the URL above and authenticate with a Bearer token (below).

## Authentication

Authentication is identical to the Public API: send your Rescue API key as a **Bearer token**. The server returns `401 Unauthorized` to any request without a valid key.

```bash theme={null}
Authorization: Bearer rk_live_<8hex><64hex>
```

API keys are minted by a Rescue admin and shown **exactly once** at creation. Each key carries:

* **A subject** — `partner`, `enterprise`, or `org` — determining which organizations the key (and therefore the MCP tools) can act on.
* **An optional `allowedOrgIds` whitelist** — further narrows the key to a subset of orgs under its subject.
* **Permissions** — `read` and/or `write`. Read tools need `read`; the declare/recategorize/resolve tools need `write`.
* **An optional `activeUntilDate`** — a hard expiration checked on every request.

<Note>
  Treat your API key like a password. Never commit it to source control or paste it into a chat. If a key is leaked, contact Rescue support to revoke or rotate it.
</Note>

### OAuth (Coming Soon)

<Note>
  OAuth authentication is on the roadmap. It will let you connect clients via a standard OAuth 2.0 flow instead of a static API key. Until then, use a Bearer API key as shown above.
</Note>

## Quick sanity check

Before wiring up a client, confirm the server is reachable and your key is valid with a raw `initialize` call. A valid key returns the server's capabilities; an invalid or missing key returns `401 Unauthorized`.

```bash theme={null}
curl https://api.punchrescue.com/mcp \
  -H "Authorization: Bearer $RESCUE_API_KEY" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
```

## Claude Code

Add the server with a single command. The `--header` flag carries your Bearer token on every request.

```bash theme={null}
claude mcp add --transport http rescue https://api.punchrescue.com/mcp \
  --header "Authorization: Bearer $RESCUE_API_KEY"
```

Then start Claude Code and run `/mcp` to confirm the `rescue` server connected and its tools are listed.

## Claude Desktop

Claude Desktop connects to remote servers through the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge. Edit your `claude_desktop_config.json` (on macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`) and add a `rescue` entry:

```json theme={null}
{
  "mcpServers": {
    "rescue": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.punchrescue.com/mcp",
        "--header",
        "Authorization: Bearer rk_live_..."
      ]
    }
  }
}
```

Restart Claude Desktop after saving. Replace `rk_live_...` with your real key.

## Cursor

Cursor uses the same `mcp-remote` bridge. Add a `rescue` entry to your MCP settings (`~/.cursor/mcp.json`, or **Settings → MCP → Add new MCP server**):

```json theme={null}
{
  "mcpServers": {
    "rescue": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.punchrescue.com/mcp",
        "--header",
        "Authorization: Bearer rk_live_..."
      ]
    }
  }
}
```

## Available tools

The server exposes seven tools, split into read and write. Every tool takes an `orgId`. Write tools are present only when the key carries `write` permission.

### Read tools

Available to any key with `read` permission.

| Tool               | Arguments                                                                                    | Description                                                                                                                     |
| ------------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `list_categories`  | `orgId`                                                                                      | List the emergency categories available for an org. Use the returned `categoryId` (or `name`) when declaring or recategorizing. |
| `list_emergencies` | `orgId`, *(opt)*`status`, `categoryId`, `externalId`, `since`, `until`, `cursor`, `limit`    | List emergencies for an org. `status` is `declared`, `categorized`, or `resolved`; `since`/`until` bound `occurredAt`.          |
| `get_emergency`    | `orgId`, `emergencyId`                                                                       | Fetch a single emergency by id.                                                                                                 |
| `list_audit_logs`  | `orgId`, *(opt)*`eventCategory`, `eventType`, `actorId`, `since`, `until`, `cursor`, `limit` | List audit-log entries for an org. `eventType` requires `eventCategory`; `since`/`until` bound the window (ISO-8601).           |

### Write tools

Present and usable only when the key carries `write` permission.

| Tool                | Arguments                                                                                                                                 | Description                                                                                                                                                                                                           |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create_emergency`  | `orgId`, *(opt)*`categoryId` \| `categoryName`, `externalId`, `source`, `description`, `occurredAt`, `location`, `metadata`, `declaredBy` | Declare a new emergency. With neither `categoryId` nor `categoryName`, it lands under the org's **Uncategorized** category. On production this fans out to base-station devices and webhook subscribers in real time. |
| `update_emergency`  | `orgId`, `emergencyId`, *(opt)*`categoryId` \| `categoryName`                                                                             | Recategorize an existing emergency. Only the category is persisted today; resolved emergencies are immutable. Fires an `emergency.categorized` webhook.                                                               |
| `resolve_emergency` | `orgId`, `emergencyId`, *(opt)*`resolutionNote`, `resolvedAt`, `resolvedBy`, `metadata`                                                   | Resolve an emergency. Resolving an already-resolved emergency returns an error. Fires an `emergency.resolved` webhook.                                                                                                |

### Common arguments

| Argument                      | Applies to                              | Notes                                                                                                                          |
| ----------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `orgId`                       | every tool                              | The Rescue organization UUID. Your key's subject (and any `allowedOrgIds` whitelist) must cover it, or the call returns `403`. |
| `categoryId` / `categoryName` | `create_emergency`, `update_emergency`  | Either one resolves the target category. Discover valid values with `list_categories` first.                                   |
| `externalId`                  | `create_emergency`, `list_emergencies`  | Your own incident id, unique per org. Set it on declares for deduplication and to look an emergency back up later.             |
| `cursor` / `limit`            | `list_*` tools                          | Cursor pagination. `limit` defaults to `50`; pass the previous response's `nextCursor` as `cursor` to page.                    |
| `since` / `until`             | `list_emergencies`, `list_audit_logs`   | ISO-8601 timestamps bounding the result window.                                                                                |
| `status`                      | `list_emergencies`                      | One of `declared`, `categorized`, `resolved`.                                                                                  |
| `declaredBy` / `resolvedBy`   | `create_emergency`, `resolve_emergency` | Actor reference `{ "id": "...", "name": "..." }`. The server assigns the actor `type`.                                         |

## Typical conversation flow

<Steps>
  <Step title="Pick the org">
    Tell the assistant which `orgId` to work in. Every tool needs it, and your key must cover it.
  </Step>

  <Step title="Discover categories">
    Ask the assistant to run `list_categories` so it has valid `categoryId` values before declaring anything.
  </Step>

  <Step title="Declare an emergency">
    `create_emergency` with a `categoryId` (and an `externalId` for your own correlation). Returns the new emergency record.
  </Step>

  <Step title="Inspect or re-categorize">
    `get_emergency` to read committed state, or `update_emergency` to move it to a different category.
  </Step>

  <Step title="Resolve">
    `resolve_emergency` with an optional `resolutionNote`. The assistant can then `get_emergency` once more to show the final resolved record.
  </Step>

  <Step title="Audit">
    `list_audit_logs` to review who did what, filtered by `eventCategory`, `eventType`, `actorId`, or a time window.
  </Step>
</Steps>

## Things to know

* **One org per call.** Tools never act across orgs implicitly — pass the `orgId` explicitly each time.
* **Discover before you declare.** Resolve a real `categoryId` with `list_categories`; an unknown category id is rejected.
* **Writes are real on production.** Declares, recategorizations, and resolutions fan out to base-station devices and webhook subscribers in real time. There's no "test mode" flag — use a sandbox-scoped key for experimentation.
* **No idempotency key yet.** Don't blindly retry a failed write. Set `externalId` on declares and, after a `5xx`, check state with `get_emergency` (or `list_emergencies` filtered by `externalId`) before retrying.
* **Recategorize is category-only.** `update_emergency` persists only the category today; other attribute changes aren't yet supported on the public surface. Resolved emergencies are immutable.
* **Same model as REST.** Permissions, org scoping, error semantics (`401`/`403`/`409`/`422`), and webhooks all behave exactly as documented for the [Public API](/api-reference/introduction).

<Tip>
  Hitting a bug or missing a tool you'd like to see? Reach out to your Rescue contact with the request ID returned by the server — it's the fastest way for us to trace what happened.
</Tip>
