Let your AI send secrets without giving your AI the secrets.
How PrivateNote MCP hands off files over email, Slack, Discord, and WhatsApp — without the model reading them
Your assistant can send a contract, a password, or a CSV today. It should not have to read those files to do it. Pass local paths to PrivateNote MCP and put only a self-destructing link in the channel you already use.
Key takeaways
- Pass file paths, not file bytes—the local MCP encrypts so the model never needs the payload.
- You approve a preview that binds exact bytes, options, and destination. Creating a note is not permission to send it.
- Email, Slack, Discord, and WhatsApp should carry only the PrivateNote URL after sendAuthorized — never the file. Leave one-shot off if the agent can also send messages.
- The decryptable URL is a bearer capability the agent can see after confirm. Do not log it. After delivery, poll open status or revoke an unopened note.
The useful part of an AI assistant is not that it can read a contract, a password file, or a customer export. The useful part is that it can get that payload to the right person, on the channel they already live in, without turning the assistant into a copy of the secret.
That is the daily job: send a PDF to a client by email, drop a staging password to a teammate in Slack, hand a bot token to a moderator in Discord, or put a one-time login in a WhatsApp thread. People already do this by pasting. Agents make the paste faster — and they make the leak larger, because the secret now also sits in the model context, the chat transcript, and every log the host keeps.
PrivateNote’s MCP server is built for the opposite split. The agent orchestrates. A local process handles the secret. You approve the exact file and destination. Email, Slack, Discord, and WhatsApp only carry a link. The model is told to pass file paths, not file bytes. Encryption happens on your machine after you confirm. What comes back is a URL whose fragment holds the key — the same one-time secret link model as the web app. Creating a note is not permission to send it.
The job is sending, not knowing
Most “AI + secrets” advice still assumes the model must see the credential in order to help. That is true if you want the model to explain the file, rewrite the contract, or debug the key. It is not true if you want the model to deliver it.
Delivery is a logistics problem: which file, which person, which channel, how long the access should last, whether it should burn after the first view. None of those decisions require the plaintext. They require a tool that can read a local path, encrypt, and return a handle the rest of the stack is allowed to see.
That is what privatenote-mcp is for. Hosts that honor MCP instructions are told, in order: if the user named a local file, pass the absolute path; do not open, cat, or summarize it; call create_private_note (a preview); wait for you to approve; then call confirm_private_note. Only after that may another tool send the URL — and only to the destination you approved.
The rule
If the assistant can complete the task with a path, it must not complete it with the contents. Paste into the prompt is a last resort — after the secret is already in the chat.
Why the channels you already use keep the wrong copy
Email, Slack, Discord, and WhatsApp are good at conversation. They are poor vaults. Encryption in transit, or even end-to-end encryption, does not decide how long a searchable copy remains on devices, in workspace exports, in cloud backups, or in notification previews. That is the same distinction as why email is the worst place for secrets and why secrets should never be pasted into chat.
- Email stores attachments in both mailboxes, on mail servers, in legal holds, and in “search all mail.” Forwarding multiplies copies you cannot later revoke.
- Slack is indexed by the workspace. Admins, compliance exports, and AI features that summarize channels can all rediscover a password months later.
- Discord looks ephemeral in a busy server. It is not. Pins, search, and DMs keep bot tokens and invite-admin secrets next to memes.
- WhatsApp is often the only channel a contractor or client will actually open. It is also a family inbox: screenshots travel, linked computers sync, and backups outlive the job.
The point is not to abandon those tools. The point is to stop putting the payload in them. They remain the envelope. The note is the letter. Recipients still need no PrivateNote account: they open the link in a browser, the same way they would open any other URL you already send them.
The three-part split
| Role | What it may see | What it must not see |
|---|---|---|
| AI agent (Cursor, Claude, Codex) | Who, which path, which channel, expiry, burn-after-read. After you confirm: the decryptable URL | File bytes, passwords, note body. It must not send the URL until you approved that destination |
| PrivateNote MCP (local Node process) | The files on disk, for encryption only | Nothing it then returns as plaintext |
| Email / Slack / Discord / WhatsApp | The PrivateNote URL (and a human-readable subject) | The attachment, the credential, the passphrase |
MCP does not send the email or the Slack message for you. Creating a note is not authorization to send it. After you approve a preview that names an exact channel and recipient, the result may include sendAuthorized: true — then a mail or chat tool may send only the URL to that destination. If no destination was approved, sendAuthorized is false and the agent should give you the URL to paste, not fire another tool.
The URL is a bearer capability: anyone who has it can open the note. The current MCP host returns secureUrl in the tool result, so the agent can observe the link even though it cannot observe the file bytes. Do not log it, commit it, or copy it into unrelated tools. Treat it like the secret.
The rest of the returned payload is small: noteId, expiresAt, burnAfterReading, and when relevant passwordProtected and attachedFileNames. File names are metadata, not contents — still avoid putting a secret in the filename.
What the MCP actually does on your machine
When the agent calls create_private_note, a local Node.js process — not the model — canonicalizes the path, checks it against the workspace allowlist, hashes the file, and shows you a preview. It does not return a decryptable URL yet. After you approve, confirm_private_note re-opens that same file, verifies device, inode, size, and SHA-256, and encrypts those bytes. If the file changed, confirmation fails; the agent must start over. AES-256-GCM ciphertext is POSTed to PrivateNote. The decryption key is placed in the URL fragment (#…). Browsers do not send fragments to servers. The API stores an encrypted blob. Without the fragment, that blob is noise.
Prefer path parameters so the host never has to put the secret in the tool-call arguments the model constructed:
contentFilePath— UTF-8 file that becomes the note body (a password, a recovery code, a short instruction).filePath/filePaths— local attachments (PDF, CSV, images). Requiressign_in. Combined maximum 20 files.passwordFilePath— first line of a local file becomes the reveal passphrase (Argon2id wrap). Send that passphrase out of band, never in the same message as the link.contentandpassword— last resorts, only if the user already pasted the secret into chat and no file exists.
Default policy matches a handoff, not an archive: burnAfterReading is true, expiresIn defaults to 24 hours. Shorter is better when the recipient is waiting on the other side of Slack. Multi-view (burnAfterReading: false) needs a Premium account. File attach, recipient verification, open notifications, and list/revoke need a signed-in session; gates and alerts also need a paid plan.
Paths are constrained. By default only files under the workspace (the MCP process working directory) can be read. Set PRIVATENOTE_MCP_ALLOWED_ROOTS if you must add folders. SSH keys, cloud credential directories, and the MCP session file are always blocked, even if you allow $HOME or /.
You approve the exact handoff — not a pathname
An agent that can create a decryptable link and send email or Slack is a confused deputy: it might encrypt the wrong file, or send the right link to the wrong person, in one turn. PrivateNote’s default is that you authorize three things together: the exact file bytes, the exact note options (expiry, burn-after-read), and the exact destination.
The preview must show the destination when sending is requested, for example “Send via: Email / Recipient: alice@example.com”. Approving that must never authorize a different address or Slack channel. confirm_private_note takes only confirmationId. The agent cannot override path, recipient, channel, expiry, or destruction policy on confirm.
Creating a note without a destination returns sendAuthorized: false. That is intentional. A link in the chat is still sensitive — but it is not permission to mail it.
Do not enable one-shot if the agent can send
PRIVATENOTE_MCP_ALLOW_ONE_SHOT skips the human preview. The agent cannot turn this on as a tool argument, and changing the env later has no effect until you restart the MCP server. Leave it off. Turning it on while the same host can send email, Slack, Discord, or files lets the model create a decryptable link and dispatch it in one step — including the wrong file or the wrong recipient. Path allowlisting still applies; that is not a substitute for your approval.
Connect it once
Requires Node.js 18+. After saving, restart the MCP client so it loads the current tool schemas and instructions. Full install notes live on the MCP integration page.
{
"mcpServers": {
"privatenote": {
"command": "npx",
"args": ["-y", "privatenote-mcp"]
}
}
}Codex CLI: codex mcp add privatenote -- npx -y privatenote-mcp. That writes ~/.codex/config.toml. Codex-specific notes are in Using PrivateNote with OpenAI Codex.
Self-hosted: set PRIVATENOTE_API_BASE_URL and PRIVATENOTE_WEB_ORIGIN in the MCP env block so ciphertext never leaves your infrastructure. Optionally set PRIVATENOTE_MCP_ALLOWED_ROOTS. Do not add PRIVATENOTE_MCP_ALLOW_ONE_SHOT unless you have a tightly scoped automation host with no send tools.
Daily use: email
Email is still how companies send contracts, NDAs, invoices with bank details, and customer exports. The failure mode is attaching the file. The mailbox then keeps a decryptable copy for years, including on the recipient’s side, BCC trails, and e-discovery.
The MCP pattern: the agent already knows Alice’s address and the two local PDFs. It must not open those PDFs. It signs in if needed, calls create_private_note with filePaths, sendChannel, and sendRecipient so the preview names Alice, waits for you to approve, then confirm_private_note. Only then may the mail tool put only secureUrl in the email body. If you use recipient verification, the reader must prove they own the address before the note unlocks — that gate does not send the mail; your mail tool still does.
Send these two files to Alice by email. Protect them with the password in /absolute/path/client-password.txt. Don't expose the files or password to the model: /absolute/path/contract.pdf /absolute/path/nda.pdf
- The agent calls
sign_inif there is no plugin session, thencreate_private_notewithfilePaths,passwordFilePath,sendChannel: "email", andsendRecipient. You approve the preview. Thenconfirm_private_note. - Email subject can say “contract pack” — it should not quote clauses from the PDF.
- Send the passphrase in a second message, a call, or a password manager share — never in the same mail as the link. See how to share a password securely.
- For a named recipient on a paid plan:
requireRecipientVerification: trueandrecipientEmail: "alice@example.com".
This is the same delivery model as sending sensitive documents securely, except the assistant is allowed to run it without becoming a reader of the pack.
Daily use: Slack
Slack is where on-call happens. Staging database passwords, deploy tokens, and “the CSV from finance” get dropped into a thread because everyone is already there. Workspace search then makes that drop a permanent internal wiki of secrets.
Ask for a path-based handoff and a short fuse. Fifteen minutes is enough if the teammate is online. Burn after reading so a later search hit is a dead link, not a live credential. Treat the URL like the secret: do not post it in a public channel; use a DM or a dedicated private channel.
Send Mark the database password from /absolute/path/secrets/db-password.txt in Slack. Make it read-once and expire in 15 minutes. Don't read the file into the chat.
create_private_notewithcontentFilePath,expiresIn: "15m",burnAfterReading: true, and the Slack destination bound at preview. Approve, thenconfirm_private_note.- If Slack is connected as another MCP tool, the agent may post only
secureUrlto the approved destination aftersendAuthorizedis true. Otherwise it gives you the URL to paste. - After Mark opens it, rotate the password if it was a shared staging secret — the note reduced leftover copies; it did not make a shared credential unique.
Daily use: Discord
Discord is the default ops channel for many product communities and game-adjacent teams. Bot tokens, Cloudflare keys, and “here’s the spreadsheet of reported users” get pasted into mod DMs because Discord is where the mods are.
A Discord message is still a stored message. Server members with the right role, device backups, and Discord’s own search will keep it. The MCP pattern is identical to Slack: encrypt the file or token locally, put the link in the DM, set a short expiry. If the token was already pasted earlier in the same server, rotate it — wrapping a leaked secret does not un-leak it.
DM the community lead the bot token in /absolute/path/secrets/discord-bot.txt and the moderation export at /absolute/path/reports/export.csv. Read-once, 1 hour, don't open those files.
- Attachments need
sign_in. A token-only note can usecontentFilePathwithout a session. - Bind the Discord destination at preview, approve it, then
confirm_private_note. Only then may a Discord tool send the URL. - Prefer a user DM over a mod-channel paste. “Private channel” is still a transcript.
- If the export includes personal data, add a passphrase file and tell the lead the passphrase in a voice call.
Daily use: WhatsApp
WhatsApp is the channel you use when the other person will not join Slack and will not check email until Monday. A contractor, a family-office assistant, a client on a phone. End-to-end encryption is real. Persistence is also real: the chat is a record, often on several devices, often backed up.
You do not need them to install PrivateNote. You need them to tap a link. The assistant’s job is to prepare that link from a local file you already have — invoice PDF, Wi-Fi password, one-time portal code — without reading it into the Cursor or Claude thread first. You still approve the preview (exact file and WhatsApp destination) before confirm_private_note creates the URL.
Send the onboarding pack at /absolute/path/client/onboarding.pdf to Ana on WhatsApp. Burn after reading, expire tomorrow. Don't read the PDF.
WhatsApp will preview URLs. That preview is the PrivateNote landing page, not the decrypted file. Still: do not put the passphrase in the same chat. If the number might be wrong, create the note, send the link, and revoke if she never opened it. Counsel and other professional senders who need a branded reveal page rather than a green bubble should read secure client delivery for lawyers.
Sign in when the payload is a file
Text-only notes from contentFilePath can be created without an account, subject to the same public rate limits as the website. File attach is different. The MCP must call sign_in first. That opens an approve page in the browser (the grant is already in the URL — there is no code to type). After you click Approve, the agent calls sign_in again with pollDeviceCode. The session is stored at ~/.config/privatenote/mcp-session.json and sent as Authorization: Bearer. It does not replace your website cookie session.
- Agent runs sign_in() — browser opens
- You click Approve on the PrivateNote page
- Agent polls with pollDeviceCode, then whoami()
- Agent calls create_private_note with file paths (preview — no URL yet)
- You approve the exact files, expiry, and destination
- Agent calls confirm_private_note, then you (or another tool) send only secureUrl on the human channel
whoami shows the signed-in account. sign_out deletes the plugin session file. Treat that file like a credential for this machine.
Install privatenote-mcp in Cursor, Claude Desktop, or Codex. The agent keeps the workflow; the local process keeps the secret.
Open the MCP setup guideAfter the link is sent
A good handoff does not end when the message leaves Slack. Paid, signed-in sessions can treat delivery as events, not contents: created, opened, expired, revoked. list_sent_notes returns ids, status, and openedAt — never the plaintext. revoke_sent_note invalidates an unopened note. notifyOnOpen emails your PrivateNote account when the note is first opened; the agent learns about the open by polling list_sent_notes. There is no push webhook into Cursor.
sign_in()
create_private_note({
filePaths: [
"/absolute/path/signed-contract.pdf",
"/absolute/path/customer-export.csv"
],
expiresIn: "24h",
burnAfterReading: true,
requireRecipientVerification: true,
recipientEmail: "john@example.com",
notifyOnOpen: true,
label: "contract-for-john",
sendChannel: "email",
sendRecipient: "john@example.com"
})
# wait for user approval of that exact destination
confirm_private_note({ confirmationId: "<from preview>" })
# email or Slack only secureUrl to John
# later: list_sent_notes() and look for openedAt on that noteIdRecipient verification is an unlock gate on PrivateNote, not a substitute for sending the message. Open alerts require a verified sender email on the account. Status-only receipts are explained in know when a secret link was opened. Agents with broad tool access still need the permission model in securing AI agents like employees.
The honest privacy caveat
The strong claim — “the AI never saw the file bytes” — holds when the host honors path-first instructions and you never pasted the payload into the prompt. It does not mean the agent cannot see secureUrl after confirm. A normal MCP tool call can still expose arguments to the host. That is why the tools prefer paths: the argument is /Users/you/secrets/db-password.txt, not the password.
If you type create a PrivateNote for sk_live_abc123, a cloud model provider can process that string before the MCP runs. PrivateNote will still store only ciphertext, and Slack will still get only a link. You have improved the downstream leak. You have not kept the secret out of the model. For maximum privacy, keep secrets in files (or use the VS Code / Cursor extension so the editor encrypts without the agent), and tell the assistant the path.
- Do not ask the model to “check the PDF first” or to summarize it. That forces a read with another tool.
- Do not put the passphrase in the same Slack/WhatsApp/email as the URL.
- Treat
secureUrlas the secret. The agent can see it in the tool result. Do not commit it, log it, or paste it in a public Discord channel. - Reload the MCP client after upgrading
privatenote-mcpso instructions and schemas stay current. Leave one-shot off.
Frequently asked questions
Does PrivateNote MCP send Slack, Discord, WhatsApp, or email itself?
No. It encrypts locally and returns a URL plus metadata. Delivery uses whatever you already use — another MCP server (Gmail, Slack, …), or you pasting the link. Creating a note is not permission to send it. sendAuthorized is true only if you approved that exact channel and recipient in the preview.
Why do I have to confirm before the link is created?
Confirmation binds the exact file bytes, note options, and destination. Without it, an agent that also has mail or chat tools could encrypt the wrong file or send the right link to the wrong person in one turn. One-shot mode skips this preview. Leave it off, especially if the same host can send messages or files.
Can the agent see the decryptable URL?
Yes, in the current MCP host. secureUrl is returned in the tool result so a delivery tool can send it. The agent still must not see the file bytes. Possession of the URL may unlock the note — treat it as a bearer capability, not as “the model cannot access the secret.”
Must the recipient install anything?
No. They open the complete link in an ordinary browser. No PrivateNote account is required to reveal a note. They may still need a passphrase or email verification if you enabled those gates.
Can I attach files without signing in?
No. File attach, list/revoke, recipient verification, and open notifications require sign_in. A text-only note from contentFilePath can be created without a session, within public limits.
What if the assistant reads the file anyway?
Then the privacy boundary is already broken for that turn. MCP instructions tell hosts not to. If your client ignores them, keep using the editor extension or the web app, and do not point the agent at the file. You can still send the URL the extension produced.
Does burn-after-read stop screenshots?
No. Once a lawful recipient decrypts the note, they can copy, screenshot, or re-upload. Burn-after-read and expiry reduce leftover copies on the service and in the chat history. They cannot remotely delete what someone already captured. For documents, view-only file access on Business reduces casual download; it is not a prohibition on photography.
Let the agent send the link. Keep the file off the model.
Install privatenote-mcp, point it at local paths, and keep using email, Slack, Discord, and WhatsApp as envelopes — not as vaults. Leave one-shot off if the same host can send messages or files.