# Pickclip MCP server — documentation for AI agents

This page is the complete technical reference for Pickclip's Model Context
Protocol (MCP) server. It is written for AI agents and their developers:
everything you need to evaluate what Pickclip can do for you, set up a
connection, and call the tools correctly is on this one page. The
human-oriented guide lives at <https://www.pickclip.pro/mcp/>.

Last updated: 2026-08-12 (tool surface of Pickclip 0.5.6).

## What Pickclip is (evaluation summary)

Pickclip is a native macOS menu-bar clipboard manager with on-device AI
actions, made by Slideform (<https://www.pickclip.pro/>).

- **Clipboard history**: every copy (text and images) is captured into a
  searchable local history with source app, timestamps, previews, and pins.
  Concealed and transient copies (password managers, "cloaked" fields) are
  never recorded.
- **Actions**: when the user copies text, a small popup offers one-tap
  transforms — Fix (grammar/typos), Organize, Translate, Clean Link
  (strip tracking parameters), Plain Text — plus the user's own **custom
  actions**, described in plain language and executed either as an AI prompt
  or as a generated deterministic script.
- **AI engines**: Apple Intelligence (on-device, default), a built-in local
  MLX model, or a local Ollama server. All local by default; no Pickclip
  cloud service touches clipboard data.
- **Also**: screenshot OCR, an ⌥V history picker, action sharing via links,
  and opt-in iPhone sync through the user's own iCloud Drive.
- **Requirements**: macOS 26 or later, Apple Silicon.
- **Licensing**: 14-day free trial (no account), then a one-time license
  ($10 early-user pricing) from <https://www.pickclip.pro/#license>.
  The MCP server works during the trial and with a license; after an expired
  trial every tool call returns an error asking the user to buy a license.

**What the MCP integration gives an agent**: read the user's clipboard and
clipboard history (text and images), run any eligible Pickclip action on a
clip and get the transformed result, put text on the user's clipboard, and
list/create/update/delete the user's custom actions.

**What it does not give**: no file access, no browsing, no keystroke or paste
injection, no reading of anything except what the user copied. Writing is
limited to the clipboard itself. There is no streaming, no resources, no
prompts — tools only.

## Architecture and security model

- Pickclip runs a **local MCP server over HTTP** inside the app. It binds to
  `127.0.0.1` only, on an **ephemeral port** chosen at each launch.
- The endpoint is `http://127.0.0.1:<port>/mcp` — stateless streamable-HTTP
  transport, **JSON responses only** (no SSE). `initialize` is idempotent, so
  reconnecting clients can re-initialize at any time.
- Every request must carry a bearer token:
  `Authorization: Bearer <token>`. Requests without the right token get
  **401**. Requests with a non-localhost `Origin` header get **403**
  (DNS-rebinding protection).
- The port and token are published in a **discovery file** readable only by
  the user (permissions `0600`):

  `~/Library/Application Support/Pickclip/mcp.json`

  ```json
  { "port": 54321, "token": "<base64url token>", "url": "http://127.0.0.1:54321/mcp" }
  ```

  The file is rewritten on every server start and removed when the server
  stops. Because the port changes across launches, always re-read this file —
  never cache the URL.
- The token is per-install and can be regenerated by the user (Settings →
  AI Tools → Regenerate token), which disconnects existing clients.
- **Off by default.** The server only runs while the user has enabled
  Settings → AI Tools → "Let AI tools use your clipboard and actions"
  (and Pickclip is running and entitled — trial or licensed).

### The stdio bridge

Most MCP clients launch local servers as a command speaking stdio. Pickclip
ships a small Foundation-only binary for that:

```
/Applications/Pickclip.app/Contents/Helpers/pickclip-mcp-bridge
```

It relays stdio JSON-RPC to the local HTTP endpoint, re-reading `mcp.json`
as needed so it survives port changes and token rotation, and it **launches
Pickclip automatically** if the app isn't running when a client connects.
The bridge holds no secrets, so client configs never go stale. No Node, no
npx, no network beyond loopback.

## Setup

Prerequisite for every path: Pickclip is installed (drag to `/Applications`),
and the user has switched on **Settings → AI Tools → "Let AI tools use your
clipboard and actions."**

### Claude Desktop (one-click extension)

In Pickclip: Settings → AI Tools → **Install Extension**. Pickclip generates
`Pickclip.mcpb` (a Claude Desktop extension bundling the bridge) and opens it
in Claude Desktop, which shows its install dialog. No config file edits. The
extension is version-independent; Pickclip's settings pane shows when an
update is available.

### Redeep (one-click)

In Pickclip: Settings → AI Tools → **Install in Redeep**. Same `.mcpb`
bundle, handed to Redeep's native install sheet. Requires Redeep at
`/Applications/Redeep.app`.

### Claude Code

```sh
claude mcp add Pickclip -- /Applications/Pickclip.app/Contents/Helpers/pickclip-mcp-bridge
```

### Cursor

Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` in a project:

```json
{
  "mcpServers": {
    "Pickclip": {
      "command": "/Applications/Pickclip.app/Contents/Helpers/pickclip-mcp-bridge"
    }
  }
}
```

### Claude Desktop, configured by hand

Merge the same `mcpServers` snippet as Cursor's into
`~/Library/Application Support/Claude/claude_desktop_config.json` (create the
file if needed, keep any existing servers), then restart Claude Desktop.
Pickclip's Settings → AI Tools → Manual setup → **Copy config** puts exactly
this snippet on the clipboard with the correct path for that install.

### Any other MCP client

- **stdio client**: launch the bridge binary above as the server command.
  No arguments, no environment variables needed.
- **HTTP client running as the same user on the same Mac**: read
  `~/Library/Application Support/Pickclip/mcp.json`, POST MCP JSON-RPC to
  `url` with `Authorization: Bearer <token>`. Example smoke test:

  ```sh
  PORT=$(python3 -c "import json;print(json.load(open('$HOME/Library/Application Support/Pickclip/mcp.json'))['port'])")
  TOKEN=$(python3 -c "import json;print(json.load(open('$HOME/Library/Application Support/Pickclip/mcp.json'))['token'])")
  curl -s http://127.0.0.1:$PORT/mcp \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" -H "Accept: application/json" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
  ```

  Requests must send `Accept: application/json` and
  `Content-Type: application/json` — the server answers **406** otherwise
  (curl's default `Accept: */*` is not enough).

## Tool reference

Ten tools. All results are returned as MCP tool results whose content is a
single JSON text block, except where an image content block (base64 PNG) is
noted. Errors come back as tool results with `isError: true` and a
plain-language message — surface these to the user; they usually say exactly
what to do (e.g. the trial has ended, or a required argument is missing).

History item **summaries** (returned by the list/search tools) look like:

```json
{
  "id": "9C2A9F3E-…",           // UUID — pass to get_clipboard_item / run_action
  "type": "text",                // "text" | "image"
  "copiedAt": "2026-08-12T14:03:22Z",
  "pinned": false,
  "sourceApp": "Safari",         // omitted when unknown; for results generated
                                 // via MCP it holds the action title or "AI Tools"
  "label": "receipt for expenses", // user-assigned label, when set
  "charCount": 1204,             // text clips only
  "preview": "first 280 chars…", // text clips; images: "[image 1200×800]"
  "hasRichText": true            // text clips only
}
```

### Read tools (annotated read-only)

**`list_clipboard_history`** — recent clips, most recent first.
- Arguments: `limit` (integer, default 20), `query` (string, optional
  case-insensitive substring filter), `type` (`"text"` | `"image"`,
  optional).
- Returns `{"count": n, "items": [summary, …]}`.

**`get_clipboard_item`** — full content of one clip.
- Arguments: `id` (string UUID, required).
- Text clip: the summary plus `"content"` holding the complete text.
- Image clip: a JSON metadata block plus an image content block
  (`image/png`, base64).

**`search_clipboard_history`** — clips whose text contains the query.
- Arguments: `query` (string, required), `limit` (integer, default 20).
- Returns `{"count": n, "items": [summary, …]}`. Matches clip text only, so
  image clips never match a query (here or via `list_clipboard_history`'s
  `query` filter).

**`get_current_clipboard`** — what is on the clipboard right now.
- No arguments.
- Text: `{"type": "text", "content": "…", "sourceApp": "…"}`.
- Image: `{"type": "image", "dimensions": "1200x800"}` plus an image content
  block. Empty: `{"type": "empty", "content": null}`.

### Operate tools

**`run_action`** — run a Pickclip action on a clip, get the transformed text.
- Arguments:
  - `action_id` (string, required) — a built-in id: `fix`, `organize`,
    `translate`, `clean-link`, `plain-text` — or a custom action's UUID or
    display name (case-insensitive) from `list_custom_actions`.
  - `clip_id` (string UUID, optional) — a text clip from history. Omit to run
    on the current clipboard text.
  - `language` (string) — required for `translate` (e.g. `"French"`); ignored
    by every other action.
  - `push_to_clipboard` (boolean, default false) — also place the result on
    the user's clipboard.
- Returns
  `{"action": "Translate → French", "result": "…", "pushedToClipboard": false, "clipId": "…"}`
  (`"currentClipboard": true` instead of `clipId` when run on the live
  clipboard).
- Rules and refusals (each returns a clear error):
  - Actions run on **text**. An image clip, or an image on the current
    clipboard, is refused.
  - Custom actions that **deliver to a destination** (Notes, browser,
    Shortcuts…), **smart/command actions** (open, play, search…), and
    **image-chained actions** only run from the Pickclip popup, not
    headlessly. Plain prompt actions and script actions run fine.
  - `translate` without `language` returns an error listing valid options.
- **The action sees only the text you named.** A custom action can be given
  the user's *working context* in Pickclip's settings — the app they're in,
  their last few clips, the last thing Pickclip pasted — so that "continue
  this" continues the previous result. That envelope is deliberately dropped
  for `run_action`: the result you get back is derived from your clip and
  nothing else, so it can never carry clips you didn't ask for. Read history
  with the read tools when you need more than one clip.
- Race safety: if the user copies something new while the action is running,
  the result is still returned but **not** pushed; the response carries a
  `pushNote` explaining that. Use the `push_to_clipboard` tool to overwrite
  deliberately.

**`push_to_clipboard`** — put text on the user's clipboard.
- Arguments: `text` (string, required).
- Returns `{"pushed": true, "charCount": n}`.
- Replaces the current copy. The previous copy remains in clipboard history,
  and the pushed text is recorded in history too (attributed to "AI Tools" —
  a `run_action` push is attributed to the action's title), so nothing is
  lost.

### Custom-action management

**`list_custom_actions`** — the user's custom actions.
- No arguments. Returns
  `{"count": n, "actions": [{"id": "…", "name": "…", "instructions": "…", "kind": "prompt" | "script", "destination": "…", "engine": "…"}, …]}`
  (`destination`/`engine` only when set).

**`create_custom_action`** — make a new custom action from plain language.
- Arguments: `name` (string, required), `instructions` (string, required —
  what the action should do to copied text), `destination` (string, optional,
  e.g. `"notes"`; defaults to the clipboard), `engine` (`"apple"` | `"mlx"` |
  `"ollama"`, optional; defaults to the user's global setting).
- Pickclip classifies the instructions the same way the in-app builder does:
  deterministic transforms become a small script (`"kind": "script"`),
  everything else runs as an AI prompt (`"kind": "prompt"`).
- Returns `{"created": true, "id": "…", "name": "…", "kind": "…"}`.
- Tip: verify a new action by running it on a real clip with `run_action`.

**`update_custom_action`** — edit an existing custom action.
- Arguments: `id` (string UUID, required); `name`, `instructions`,
  `destination`, `engine` all optional. Changing `instructions` rebuilds and
  re-classifies the action; other fields are edited in place.
- Returns `{"updated": true, "id": "…", "name": "…", "kind": "…"}`.

**`delete_custom_action`** — remove a custom action. **Cannot be undone** —
confirm with the user before calling.
- Arguments: `id` (string UUID, required).
- Returns `{"deleted": true, "id": "…", "name": "…"}`.

## Privacy notes an agent should respect

- Clipboard contents are personal data. Only read what the task needs, and
  don't retain or forward clips beyond the user's request.
- Password-manager and concealed copies never reach Pickclip's history, so
  you will not see them — don't ask users to re-copy secrets to work around
  that.
- Everything you see, you asked for. No tool returns clip content you didn't
  request: `run_action` runs without the working-context envelope (see above),
  and no tool reports which app the user is in right now or what they last
  pasted. (A clip's own `sourceApp` — the app it was copied *from* — is part
  of the item you requested, and is reported.)
- If you run in the cloud, clip text you read leaves the user's Mac as part
  of your context. Pickclip warns users about this; behave accordingly.

## Troubleshooting

| Symptom | Cause and fix |
|---|---|
| Client can't connect / bridge exits | AI Tools is switched off, or Pickclip isn't installed. Enable Settings → AI Tools. The bridge auto-launches Pickclip when the toggle is on. |
| `mcp.json` missing | The server isn't running (toggle off, or the app quit). The file is only present while the server is up. |
| HTTP 401 | Wrong or rotated token. Re-read `mcp.json`; if using a copied config, re-copy it. Bridge users are immune — the bridge re-reads the file itself. |
| HTTP 403 | The request carried a non-localhost `Origin`. The server refuses cross-site/browser calls by design. |
| HTTP 406 | Missing `Accept: application/json` (or wrong `Content-Type`). Send both headers exactly. |
| Every tool returns a "trial has ended" error | The 14-day trial expired. The user needs a license: <https://www.pickclip.pro/#license>. |
| Tool result `isError` with "can't run actions right now" | Pickclip is still starting up. Retry in a moment. |
| `run_action` refuses the action | The action needs the popup (destination/smart/image actions), the clip is an image, or `translate` is missing `language`. The error message states which. |
| Extension installed but tools look outdated | The `.mcpb` bundles a manifest snapshot. Reinstall from Settings → AI Tools (the pane shows "Update Extension" when the installed version is older). |

## Version history of the MCP surface

- **0.5.3** — first release: history/clipboard read tools and custom-action
  management (`list_clipboard_history`, `get_clipboard_item`,
  `search_clipboard_history`, `get_current_clipboard`,
  `list_custom_actions`, `create_custom_action`, `update_custom_action`,
  `delete_custom_action`), the stdio bridge, and the one-click Claude
  Desktop extension.
- **0.5.6** — operate tools: `run_action` (run built-in or custom actions on
  clips, optional push) and `push_to_clipboard`; one-click Redeep install.

## Contact

Questions, bug reports, integration ideas: <pickclip@slideform.co>.
