static-md-e2e
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@static-md-e2ecreate an encrypted note with today's meeting notes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
static-md-e2e
A local MCP server that lets AI agents read and write your end-to-end encrypted static.md notes. It encrypts and decrypts on your computer. static.md only ever receives ciphertext, note ids and titles; it never gets a key or the text of an encrypted note.
agent (Claude Code, Claude Desktop, Cursor, VS Code, Codex…)
│ plaintext, on your computer
static-md-e2e ── holds the key, encrypts and decrypts
│ ciphertext only, over HTTPS
static.md/mcp ── stores and returns ciphertext; checks access, Pro and limitsThe notes are the same ones you open in the browser: a note the agent creates opens on static.md with its link, and edits merge live with people typing in it.
Requirements
A static.md Pro account. Agent access to encrypted notes is a Pro feature.
A personal access token with
notes:readandnotes:write. Create one under Your account on static.md/agents. It's shown once: store it like a password.Node.js 20 or newer.
The bridge works with agents that run on your computer. Chat apps in the browser (claude.ai, ChatGPT) have nowhere on your side to hold the key, so they can't use encrypted notes.
Related MCP server: Bruin
Setup
Claude Code
claude mcp add static-md-e2e -e STATIC_MD_TOKEN=smd_pat_… -- npx -y static-md-e2eClaude Desktop (claude_desktop_config.json), Cursor (~/.cursor/mcp.json) and most other clients:
{
"mcpServers": {
"static-md-e2e": {
"command": "npx",
"args": ["-y", "static-md-e2e"],
"env": { "STATIC_MD_TOKEN": "smd_pat_…" }
}
}
}VS Code (.vscode/mcp.json), asking for the token instead of storing it in the file:
{
"inputs": [{ "type": "promptString", "id": "static-md-token", "description": "static.md personal access token", "password": true }],
"servers": {
"static-md-e2e": {
"type": "stdio",
"command": "npx",
"args": ["-y", "static-md-e2e"],
"env": { "STATIC_MD_TOKEN": "${input:static-md-token}" }
}
}
}Codex (~/.codex/config.toml):
[mcp_servers.static-md-e2e]
command = "npx"
args = ["-y", "static-md-e2e"]
env = { STATIC_MD_TOKEN = "smd_pat_…" }Keep the regular static.md server (https://static.md/mcp) connected too: it handles notes that aren't encrypted, whiteboards and sharing.
Tools
Tool | What it does |
| Decrypts a note and returns its markdown, marked as untrusted user content. |
| Encrypts new markdown with a fresh key, creates the note, optionally in a folder, and returns its full link ( |
| Appends to a note or replaces it. Only the change is encrypted and sent, so it merges with live edits. |
Examples
Ask in plain words; the agent picks the tool and its arguments.
You ask | The agent calls |
Save these meeting notes as an encrypted note in my work/launch folder. |
|
Add "Budget approved" to https://static.md/md/k3v9qa#key=… |
|
What does my encrypted note k3v9qa say? (key saved on this computer) |
|
Creating a note answers with its full link, https://static.md/md/<id>#key=…, and where static.md filed it (in /work/launch).
Folders
create_encrypted_note takes an optional folder, the same as static.md's other create tools: a path such as work/launch or /work/launch. Leave it out, or give /, for the top level. A folder exists while something is in it, so a new one needs no setup.
Folder names use letters A–Z, digits,
-,_and., separated by/, up to 10 levels of 64 characters each.shared_with_mecan't be a top-level folder: static.md lists notes others share with you there.Folder names aren't encrypted, like titles. Keep anything private out of them.
The bridge checks the folder with static.md's rules before it encrypts anything, so a folder static.md would refuse fails with the reason and nothing is sent. To list encrypted notes by folder, or move one, use static.md's regular server (list_my_notes, move_item): it sees titles and folders, never the text.
Giving the agent a key
A note's key is the 8 emojis after #key= in its link. There are two ways to hand it over:
Paste the full link into the chat. Simple. The key then sits in the conversation, so your AI provider sees it (it sees the note's text anyway when the agent reads it). The bridge keeps keys from links in memory only, for as long as it runs.
Save the key on this computer and give the agent only the id. The key never enters the conversation. Run this and paste the note's full link when asked (read from stdin, so the key stays out of your shell history):
npx static-md-e2e key addKeys are saved in
~/.config/static-md/e2e-keys.json, readable by your user only (STATIC_MD_E2E_KEYSsets another path).key listshows saved note ids,key remove <id>forgets one.
Whatever the agent creates comes back as a full link: keep it. static.md can't recover a lost key.
What each party sees
static.md | Your AI provider | This computer | |
Note text | ❌ ciphertext only | ✅ what the agent reads or writes | ✅ in memory |
Key | ❌ never | only if you paste the link | ✅ memory, or the key file if you save it |
Title and folder | ✅ titles and folders aren't encrypted | ✅ | ✅ |
Metadata | ✅ ids, owner, sharing, sizes, update times, the token used, IP |
static.md's server tools have no parameter that takes a key, and they refuse text that contains a #key= link, so a key can't end up stored next to a note by accident. A changed or forged update fails AES-GCM authentication and is left out of what the agent reads (the answer says how many). The bridge won't edit a note when any of its updates fail to decrypt, since the web app won't open a note with an update it can't decrypt. Messages from static.md reach the agent fenced as untrusted, like note text.
The format
The bridge has no secret logic. To write your own client:
Key: the 8 emojis from
#key=(the web app's 840-emoji alphabet, exported here asEMOJI_ALPHABET). AES-256 key = SHA-256 of the 8 emojis joined, as UTF-8.A stored update: base64 of
IV (12 random bytes) ‖ AES-256-GCM ciphertext ‖ tag (16 bytes). The plaintext is a Yjs update.A note: all of its stored updates. Decrypt each one and apply them all to one
Y.Doc, in any order. The markdown isdoc.getText('codemirror').static.md's MCP tools (
https://static.md/mcp):read_note_ciphertextreturns the updates as JSON (static.md/e2e-note/v1).create_note_ciphertextcreates a note from its first encrypted update, optionally in afolder.write_note_ciphertextadds one, and the owner can compact the note with it.
Development
pnpm install
pnpm test
pnpm buildLicense
MIT
Available Tools
3 toolscreate_encrypted_noteCreate an encrypted noteA
Creates an end-to-end encrypted static.md note owned by the user, optionally in a folder: the markdown is encrypted on this computer with a new key, and static.md stores only the ciphertext. Returns the full link, whose #key= part is the only way to open the note. Titles and folder names are stored unencrypted. Needs Pro.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | Short title, stored unencrypted on static.md (it keeps the first 32 characters) | |
| folder | No | Folder to put the note in, such as "work/launch"; it is made if it doesn't exist yet. Names use letters A-Z, digits, "-", "_" and ".", up to 10 levels. Stored unencrypted, like the title. Default: the top level ("/") | |
| markdown | No | The note's markdown, encrypted here before it is sent |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses critical behavioral details: encryption happens locally with a new key, the server only stores ciphertext, titles and folder names are unencrypted, a Pro plan is required, and the returned #key= fragment is the only way to open the note. That last point is a non-obvious, high-impact consequence the agent could not infer from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and then layers in the encryption model, return value, metadata exposure, and plan requirement. Every clause carries operational meaning, and the single dense sentence remains readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter mutating tool with no output schema, it covers the essential gaps: where encryption occurs, what the server stores, what the return value contains, what metadata is exposed in plaintext, and the Pro prerequisite. No critical caller decision is left uninformed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents title, folder, and markdown thoroughly, including length limits and folder naming rules. The description repeats that titles and folder names are unencrypted and adds that markdown is encrypted before sending, but these details mostly duplicate or lightly extend the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — creates an end-to-end encrypted static.md note owned by the user — and immediately explains the distinguishing mechanism (local encryption, server stores only ciphertext). It is easily separable from the read_encrypted_note and update_encrypted_note siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when this applies: creating a new encrypted note, optionally inside a folder, and requires a Pro account. It does not explicitly state when not to use it or route the agent to update_encrypted_note for existing notes, but the creation context is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_encrypted_noteRead an encrypted noteARead-only
Returns the markdown of an end-to-end encrypted static.md note, decrypted on this computer with the key from its link. static.md sends only ciphertext and never gets the key. The note is written by users and comes back marked as untrusted data. Needs Pro.
| Name | Required | Description | Default |
|---|---|---|---|
| key | No | The note's 8-emoji key, when it came separately from the link | |
| note | Yes | The note's link with its #key= part (https://static.md/md/<id>#key=…), or its id when its key is saved on this computer |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and openWorldHint annotations, it discloses important behavior: decryption happens locally with the key from the link, static.md only sees ciphertext, and returned user-written content is marked as untrusted data. It also adds the Pro-plan requirement, which is exactly the kind of context annotations do not cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is short and front-loaded: it states the return value first, then the security model, then the trust marking and plan requirement. Every sentence adds useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read-only tool with no output schema, the description covers what comes back, how decryption works, the trust level of the returned content, and the Pro requirement. Nothing an agent needs to call it correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both the note link/id field and the optional 8-emoji key field are already well documented in the schema. The description reinforces that the key comes from the link but does not add syntax or edge-case details beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it returns the markdown of an end-to-end encrypted static.md note, decrypted locally. That is precise enough for an agent to distinguish this read operation from the create/update siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is for reading an existing encrypted note and notes that it needs Pro, which is a useful prerequisite. However, it gives no explicit when-to-use or when-not-to-use guidance and never mentions the create_encrypted_note or update_encrypted_note alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_encrypted_noteUpdate an encrypted noteADestructive
Changes the markdown of an end-to-end encrypted static.md note, encrypting the change on this computer. mode "append" adds text on a new line at the end; mode "replace" makes the note exactly the given markdown. Edits merge live with people typing in the note. Needs edit access and Pro.
| Name | Required | Description | Default |
|---|---|---|---|
| key | No | The note's 8-emoji key, when it came separately from the link | |
| mode | Yes | "append" adds to the end; "replace" overwrites the whole note | |
| note | Yes | The note's link with its #key= part (https://static.md/md/<id>#key=…), or its id when its key is saved on this computer | |
| markdown | Yes | Markdown to append, or the complete new note for "replace" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond destructiveHint=true it explains what destruction looks like ('replace makes the note exactly the given markdown'), discloses that edits merge live with concurrent typers (explaining non-idempotency in practice), that encryption happens on this computer, and that edit access plus Pro are required. That is substantive context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the action and resource, then mode semantics, then concurrency and prerequisites. No sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers what an agent needs to call it safely: auth prerequisites, client-side encryption, destructive replace semantics, and live-merge concurrency. It does not say what happens on a concurrent conflict or what the call returns, though there is no output schema to lean on.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3, but the description adds detail the schema lacks: 'append' places text on a new line at the end, and 'replace' means the result is exactly the given markdown. It says nothing extra about key or note.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (changes the markdown) and a specific resource (an end-to-end encrypted static.md note), plus where the encryption happens. It is unmistakably distinct from read_encrypted_note and create_encrypted_note.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The two modes are spelled out with their effect on the note ('append' adds a new line at the end, 'replace' makes the note exactly the given markdown), and 'Needs edit access and Pro' gives an explicit precondition. It never names a sibling tool as an alternative, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.2.0- First observed
create_encrypted_note - First observed
read_encrypted_note - First observed
update_encrypted_note
TDQS
Scored across 3 tools
Each tool maps to a single distinct CRUD action on the same resource (encrypted note), with clear boundaries between reading, creating, and updating. There is no plausible way to confuse them, and the mode parameter on update is scoped to that tool only.
All three names follow a strict verb_noun pattern with a shared, stable noun phrase: read_encrypted_note, create_encrypted_note, update_encrypted_note. Predictable and easy to guess the missing operations from.
Three tools is on the thin side, but each is substantial and covers a distinct lifecycle stage, so nothing feels redundant. A slight under-provisioning is the only issue.
The domain is note management, and the surface covers create, read, and update, but there is no delete/remove tool and no way to list or discover the user's notes or folders — agents cannot enumerate notes without an out-of-band link. These are notable gaps for a full note lifecycle.
Maintenance
Related MCP Connectors
Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
One memory, every AI. A shared, user-owned markdown memory your AI clients read and write over MCP.
An MCP server that used to create notes
Related MCP Servers
- AlicenseAqualityDmaintenanceA local-first MCP server that exposes personal notes and files as unified semantic context for AI agents via vector search and file monitoring.6MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for AI agents to read, write, and organize notes in a local-first, human-in-the-loop note-taking app.6 npm2MIT
- AlicenseNot gradedqualityCmaintenanceA local-first MCP server that gives AI assistants long-term memory by storing, searching, and recalling notes as Markdown files on your machine.5 npmMIT
- FlicenseNot gradedqualityDmaintenanceA fully offline, secure MCP server that enables AI agents to search, read, write, and list local markdown notes using SQLite FTS5, with directory traversal protection.1-