paperplane-mcp
by ParikshithV
README.md
# paperplane-mcp
An MCP connector for [Paperplane](https://github.com/ParikshithV/Paperplane) — the inbox for the
events your machines send you.
It lets an AI tool page you on your phone when a long job finishes or needs a decision, read back
what fired, acknowledge or resolve an alert, and file a follow-up ticket. One long-lived credential,
no per-channel keys.
## Setup
### 1. Create a token
In the Paperplane app: **Settings → Developer tokens → Create token**. The secret is shown once —
copy it then. It looks like `pplt_` followed by 64 hex characters.
### 2. Point a client at it
Claude Code — `.mcp.json` in your project, or `~/.claude.json` for every project:
```json
{
"mcpServers": {
"paperplane": {
"command": "npx",
"args": ["-y", "paperplane-mcp"],
"env": {
"PAPERPLANE_TOKEN": "pplt_...",
"PAPERPLANE_DEFAULT_CHANNEL": "Builds"
}
}
}
}
```
Claude Desktop uses the same block inside `claude_desktop_config.json`.
| Variable | Required | Default |
| --- | --- | --- |
| `PAPERPLANE_TOKEN` | yes | — |
| `PAPERPLANE_API_BASE_URL` | no | `https://paperplane-backend.parikshithv01.workers.dev` |
| `PAPERPLANE_DEFAULT_CHANNEL` | no | none — tools then need an explicit `channel` |
The server checks the token at startup, so a revoked one fails fast with a message on stderr rather
than surfacing mid-conversation.
## Tools
| Tool | What it does |
| --- | --- |
| `paperplane_send_event` | Push a notification to the phone. Supports priority, nature, and action buttons. |
| `paperplane_ask` | Send a question to the phone and **block** until it's answered (or times out). |
| `paperplane_check_ask` | Non-blocking read of an ask's current status — recover an answer after a client-side timeout. |
| `paperplane_list_channels` | List the channels you belong to. |
| `paperplane_list_messages` | Read recent events and replies, newest first, with alert status. |
| `paperplane_set_alert_state` | Mark a message `open`, `acked`, or `resolved`. |
| `paperplane_reply` | Post a reply into a channel thread. |
| `paperplane_list_tickets` | Read the personal ticket board. |
| `paperplane_create_ticket` | File a follow-up, optionally linked back to an alert. |
### Making something an alert
An event becomes an alert — badged, counted under "Needs attention", requiring acknowledgement —
when `nature` is one of `error`, `failure`, `failed`, `critical`, `pending`, or `priority` is one of
`high`, `crit`, `critical`, `urgent`. Anything else is an ordinary message.
### Action buttons
`callback` attaches buttons to the notification:
```json
{
"title": "Deploy failed",
"message": "api-service · main @ abc123",
"nature": "error",
"callback": [{ "label": "Re-run pipeline", "webhook": "https://ci.example.com/hooks/rerun" }]
}
```
The phone fires the webhook when the button is pressed — the connector never calls it.
## Scopes
A token carries scopes; the connector uses `events:write`, `channels:read`, `messages:read`,
`messages:write`, `tickets:read`, and `tickets:write`. Tokens minted from the app carry all of them.
Mint a narrower one via the API if you want a send-only credential:
```bash
curl -X POST "$PAPERPLANE_API_BASE_URL/tokens" \
-H "authorization: Bearer $SESSION_JWT" -H 'content-type: application/json' \
-d '{"name":"CI notifier","scopes":["events:write"]}'
```
Channel administration, membership, retention, and token management are session-only and stay out
of reach of any token.
## Development
```bash
npm install
npm run build
npm run inspect # builds, then opens the MCP Inspector
```
Point it at a local backend with `PAPERPLANE_API_BASE_URL=http://localhost:8787`.
Tool handlers in `src/tools/` take parsed arguments and return plain values — they know nothing
about MCP framing, so mounting them on a remote HTTP transport later is additive rather than a
rewrite.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues