Clipboard History MCP

d-khomenko/clipboard-history-mcp
19 starsMITCommunity

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Your clipboard, but Claude can read it. Type-classified, secret-encrypted.

README.md

clipboard-history-mcp

Your clipboard, but Claude can read it. Type-classified, secret-encrypted, macOS-native.

!Power-draw comparison: a small green Daemon (idle ~1 mW solid, burst ~30 mW dashed) shares a bottom baseline and a vertical edge with a much larger red Chrome tab (Google Doc — idle ~100 mW solid, burst ~500 mW dashed). Square area is proportional to power.

flowchart LR
  U[You ⌘C something] --> D[Rust daemon<br/>launchd-managed]
  D -->|classify + encrypt| DB[(SQLite + FTS5)]
  D -. non-secret clips .-> O[Obsidian vault<br/>optional]
  C[Claude] -->|MCP stdio| M[15 tools]
  M --> DB
  M -->|Touch ID| V[Vault<br/>AES-256-GCM]

One Rust binary. No Node.js, no Swift toolchain, no other apps to install. Drag a .mcpb into Claude Desktop, done.

One process · ~1 mW idle · ~24 MB RAM. Replaces the clipboard-manager + secret-scanner + Obsidian-export stack a typical setup needs three or four background apps to assemble.

!Demo: copy a URL, a JSON snippet, code, an OpenAI key — daemon classifies each, then Claude lists them in chat and unlocks the secret behind Touch ID.

---

What this gets you

You're working in Claude. You've copied 47 things today — URLs, JSON snippets, an OpenAI key from a dashboard, a SQL query from your DB tool, half a Stripe webhook payload. Now Claude can use any of them:

You: "Find that API key I copied from the OpenAI dashboard yesterday"
Claude: Found 1 secret matching "OpenAI" — kind: openai_api_key,
source: Safari, copied 14h ago. Last 4 chars: ab12.
To reveal, call unlock_secret(id=23, reason="...").

You: "Give me all the GitHub URLs I copied"
Claude: Returns 12 unique GitHub URLs deduplicated by repo,
ranked by how often you pasted them back.

You: "There was a Stripe JSON config in the buffer — restore it to my clipboard"
Claude: Found, restoring. ✓ ready to ⌘V.

You: "What code did I copy from ChatGPT over the last 2 hours?"
Claude: 3 Python snippets, 1 SQL query, 1 shell command.

You: "Show me the screenshot I just copied"
Claude: Returns a 142×120 PNG, mime image/png, captured from Preview 2 minutes ago.

Search uses SQLite FTS5 + window-title indexing — you find clips by where you copied them, not just by content.

---

Why nothing else does this

| | Maccy | maccy-clipboard-mcp | clipboard-history-mcp | |---|---|---|---| | Captures clipboard | ✓ | (reads Maccy's DB) | ✓ | | Classifies clip type (URL/JSON/code/SQL/secret) | ✗ | ✗ | ✓ | | Indexes window titles for context-search | ✗ | ✗ | ✓ | | Detects secrets at capture (252 gitleaks rules) | ✗ | ✗ | ✓ | | Encrypts secrets at rest with Touch ID gate | ✗ | ✗ | ✓ | | Standalone binary (no runtime needed) | n/a | ✗ | ✓ macOS + Linux | | .mcpb one-click install | n/a | ✗ | ✓ |

The other ~20 clipboard-mcp repos on GitHub only read the current clipboard. None capture history.

---

Install

One-click — Claude Desktop

  1. Download clipboard-history-mcp.mcpb from the latest release.
  2. Claude Desktop → Settings → Extensions → "Install from file" → pick the .mcpb.
  3. Restart Claude Desktop. Ask: "List my recent clipboard URLs."

Manual — Claude Code / CLI

curl -L https://github.com/d-khomenko/clipboard-history-mcp/releases/latest/download/clipboard-history-mcp.mcpb \
  -o clipboard-history-mcp.mcpb
unzip clipboard-history-mcp.mcpb -d ext

# register MCP
claude mcp add -s user clipboard-history -- ./ext/server/clipboard-history-mcp serve

# install background daemon (captures even when Claude is closed)
./ext/server/clipboard-history-mcp install

Quick install — cargo install

If you have a Rust toolchain (Rust 1.95+) and use Claude Code:

cargo install clipboard-history-mcp
clipboard-history-mcp install                      # start daemon
claude mcp add -s user clipboard-history -- clipboard-history-mcp serve

This pulls a release crate from crates.io and compiles locally. Slightly slower than the .mcpb drag-and-drop above (compile takes ~2 min on Apple Silicon), but it's the cleanest path for Claude Code users — three commands and you're capturing.

From source (macOS)

Requirements: Rust 1.95+, macOS 13+.

git clone https://github.com/d-khomenko/clipboard-history-mcp
cd clipboard-history-mcp
cargo build --release
./target/release/clipboard-history-mcp install                                 # start daemon
claude mcp add -s user clipboard-history -- "$(pwd)/target/release/clipboard-history-mcp" serve

Linux (X11; Wayland partial)

Requirements: a working Secret Service implementation (GNOME Keyring or KWallet — comes with most desktop installs), xvfb not required for normal use (only for headless CI).

git clone https://github.com/d-khomenko/clipboard-history-mcp
cd clipboard-history-mcp
cargo build --release

# Set a master password (used to wrap your AES master key)
./target/release/clipboard-history-mcp migrate-v2

# Install systemd user service
./target/release/clipboard-history-mcp install
# Optional: keep daemon running after logout
./target/release/clipboard-history-mcp install --linger

# Register with Claude Code
claude mcp add -s user clipboard-history -- "$(pwd)/target/release/clipboard-history-mcp" serve

Wayland note: clipboard read/write works on Wayland via arboard's portal handling. Window-title capture (opt-in via CLIPBOARD_CAPTURE_WINDOW_TITLE=1) currently only works on X11; Wayland support is deferred to v0.4.x once xdg-desktop-portal window-title APIs are widely shipped.

1Password / Bitwarden: add app names to CLIPBOARD_IGNORE_APPS so password-manager paste events are skipped:

CLIPBOARD_IGNORE_APPS="1Password,Bitwarden,KeePassXC" \
  ./target/release/clipboard-history-mcp install

Auto-mirror to Obsidian vault (optional)

If you also use Obsidian, the daemon can write a sidecar .md for every captured clip plus a daily-note timeline entry. Configure at install time:

clipboard-history-mcp install --vault ~/Documents/Obsidian/MyVault

Result: every non-secret clip lands as <vault>/clipboard/YYYY-MM/<id>-<kind>-<slug>.md with frontmatter (kind, source, captured-at), and a bullet appended to <vault>/daily/YYYY-MM-DD.md linking back to the sidecar. Secrets are never mirrored — they stay encrypted in the SQLite vault.

The daemon owns the ## Clipboard captures H2 section in your daily notes — append-only, idempotent. You can write anything else above or below it.

---

Try asking Claude

Copy any of these prompts into Claude after install:

List my last 20 clipboard entries.

Show me only the URLs I've copied today.

Search my clipboard history for "anthropic".

Find any code snippets in Python from the last 3 hours.

How many secrets are in my clipboard vault, by kind?

Pin the JSON I just copied — I'll need it again.

Show me only my pinned clips.

What apps did I copy from most this week?

Restore that GitHub PR link to my clipboard.

Claude picks the right tool from 15 available and answers directly.

---

Configuration

Set env vars in the launchd plist (install writes them) or via claude mcp add --env KEY=val:

| Variable | Default | What it does | |---|---|---| | CLIPBOARD_POLL_MS | 1500 | Watcher poll interval (ms) | | CLIPBOARD_HISTORY_MAX | 1000 | Ring-buffer size (unpinned clips only — pinned items sit outside the ring buffer) | | CLIPBOARD_CAPTURE_WINDOW_TITLE | 0 | Capture window titles (needs Accessibility permission) | | CLIPBOARD_IGNORE_APPS | (empty) | Comma-separated app display names to skip | | CLIPBOARD_NEVER_STORE_SECRETS | 0 | Paranoid mode — metadata only, no ciphertext | | CLIPBOARD_DATA_DIR | ~/Library/Application Support/clipboard-history-mcp | Override data dir | | CLIPBOARD_VAULT_PATH | (empty) | Auto-mirror non-secret clips to this Obsidian vault directory (sidecar .md per clip + bullet in daily/YYYY-MM-DD.md). Set via --vault PATH at install time. | | CLIPBOARD_MAX_BLOB_BYTES | 26214400 (25 MB) | Skip image / file pasteboards larger than this. Logs a WARN line with the source app + size. |

---

Tools (15)

Read

| | | |---|---| | list_history(limit?, kind?, source_app?, since?, pinned_only?) | Paginated history, newest first. pinned_only=true returns only pinned clips. | | get_item(id) | Single clip by id | | search_history(query, limit?) | FTS5 BM25 across preview + window title | | get_urls(limit?) | URL clips, deduped by hostname | | get_code(language?, limit?) | Code clips, optional language filter | | get_json(limit?) | JSON clips with parsed structure | | get_secrets_index(kind?) | Secret metadata — no values | | unlock_secret(id, reason) | Decrypt one secret — gated by Touch ID | | get_stats | Counts by kind, oldest/newest, db size | | daemon_status | PID, running state |

Write

| | | |---|---| | copy_item(id) | Restore a clip back to system clipboard | | pin_item(id, pinned) | Pin so it survives clear_history | | tag_item(id, tag, remove?) | Free-form tagging | | delete_item(id) | Hard delete | | clear_history(scope) | 'all' \| 'older_than_days:N' \| 'kind:K' |

---

CLI

clipboard-history-mcp daemon         # run watcher (managed by launchd)
clipboard-history-mcp serve          # MCP stdio server (Claude spawns this)
clipboard-history-mcp install [--window-titles]
clipboard-history-mcp uninstall [--keep-data]
clipboard-history-mcp status         # JSON: daemon pid, db size, etc.
clipboard-history-mcp vault list     # secret metadata
clipboard-history-mcp vault unlock N # decrypt secret #N (Touch ID)
clipboard-history-mcp doctor         # diagnose perms/Keychain/pasteboard
clipboard-history-mcp migrate-v2     # validate v0.2.x SQLite (no-op import)
clipboard-history-mcp clean-legacy [-y]  # remove the v0.3.x macOS data dir (~/Library/Application Support/clipboard-history-mcp/)

---

Security model

  • Secrets detected at capture — 252+ gitleaks regex patterns + RFC 7519 JWT + Luhn-validated cards.
  • Encryption at rest — AES-256-GCM with a 32-byte master key in macOS Keychain.
  • Touch ID gateunlock_secret calls LAContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics). 5-minute auth cache per session.
  • LLM-blind by defaultlist_history and search_history return secret rows with text: null and preview: "[REDACTED:kind]". The only path that returns plaintext is unlock_secret(id, reason), and the reason argument is mandatory and audit-logged.
  • Paranoid modeCLIPBOARD_NEVER_STORE_SECRETS=1 keeps metadata only; ciphertext never written.
  • Transient-type respect — clips marked by password managers (1Password, Bitwarden) with org.nspasteboard.ConcealedType are never captured.

---

Architecture

src/
├── main.rs               # binary entry; clap subcommand dispatch
├── core/
│   ├── db.rs             # SQLite WAL + migrations + FTS5
│   ├── store.rs          # add/list/search/delete clips
│   ├── crypto.rs         # AES-256-GCM + Keychain
│   ├── types.rs          # classifier (url/json/sql/shell/code:lang)
│   ├── secrets.rs        # gitleaks rules + JWT + Luhn
│   ├── pasteboard.rs     # NSPasteboard via objc2
│   └── biometry.rs       # LAContext Touch ID gate
├── daemon/{watcher,context}.rs    # poll loop on pinned thread
├── mcp/tools.rs                   # 15 MCP tools via rmcp
└── cli/{install,uninstall,status,vault,doctor,migrate_v2}.rs

Tech stack: Rust 1.95 · rmcp 1.6 · tokio · objc2 · rusqlite (bundled FTS5) · aes-gcm · security-framework · objc2-local-authentication · clap 4

---

Performance

Daemon idle footprint, measured on Apple Silicon with default CLIPBOARD_POLL_MS=1500 and vault-mirror enabled:

| Metric | Value | How measured | |---|---|---| | CPU steady-state | 0.05 % | ps cumulative CPU-time delta over 60 s (0.03 s / 60 s) | | Memory (RSS) | ~24 MB | ps -o rss | | Power draw (avg) | ~1 mW | 0.05 % of an E-core (~1 W full tilt) running continuously | | Battery impact | ~1 % per 3 weeks | 1 mW × 504 h = 0.5 Wh on a 52 Wh MacBook Air battery, assuming 24/7 continuous run |

See the two-squares comparison at the top of this README for a visual on how this compares to a single Chrome tab.

Burst cost on each ⌘C: ~30 ms tick at 1–3 % CPU (secret-classifier regexes + FTS5 index + AES-GCM + optional vault file write), then back to idle. Activity Monitor's _Energy Impact_ column reports ~0 — below its detection threshold.

Linux numbers untested but expected similar order of magnitude (arboard polling + SQLite is portable).

---

FAQ

Does this run on Linux/Windows? Linux: yes since v0.4 (X11 + Wayland clipboard via arboard; window-title capture is X11-only for now). See the Linux install section above. Windows: not yet — arboard works there, but the launchd/systemd-style installer hasn't been ported. PRs welcome.

What if I copy a 50MB blob? Currently captured. A CLIPBOARD_MAX_BYTES guard is on the roadmap.

Can Claude leak my secrets? Not without you (a) installing this tool, (b) approving Touch ID, (c) the LLM choosing to call unlock_secret with a reason that gets logged. The text field for secret rows is always null in list_history/search_history.

Does it sync across Macs? No native cross-device sync — the SQLite vault stays local. If you set --vault PATH to point inside a synced Obsidian vault (iCloud, git, Syncthing, etc.), the per-clip sidecar markdown files follow your sync — but secrets are never mirrored to the vault, so password-protected items remain on the originating machine.

Can I use this with Maccy already running? Yes. They don't conflict — Maccy provides UI, this provides the MCP layer. Both poll NSPasteboard.changeCount independently.

What about [other clipboard manager]? Same answer — there's no exclusive lock. Worst case, both store the same clip; storage cost is trivial.

---

Changelog

See CHANGELOG.md. Latest: v0.3.0-alpha.0.

Contributing

See CONTRIBUTING.md. PRs welcome — issues with macOS 13/14 reproductions especially.

Sponsor

Pre-1.0 alpha. Built solo, in bursts. If clipboard-history-mcp finds you a leaked key, saves you from typing the same JSON twice, or just makes Claude slightly more useful at your terminal — consider becoming a backer or Founding Sponsor on Patreon.

  • $3 / month — backer ☕ — name in SUPPORTERS.md, Discord role
  • $10 / month — founding sponsor 🥇 — name + avatar in this README, permanently listed if you join before v1.0

Sponsorship is gratitude, not a support contract. It does not buy priority issue triage, custom features, or response-time SLA — solo OSS doesn't scale that way. What it buys: visibility, the occasional roadmap vote, and proof that this kind of tool is worth maintaining past 0.x.

GitHub Sponsors works too — see the Sponsor button at the top of the repo.

License

MIT. vendor/gitleaks.toml is the gitleaks rule catalog (also MIT) — see vendor/README.md.

Related MCP servers

Browse all →