Note
A Docs MCP Can Read the Whole Protocol. The Line to Watch Is Signing
A documentation MCP server is read-only by design, and that is what makes it easy to say yes to. The line worth watching is not what an agent can read, but whether anything in its reach can sign.
2026-10-20 · nostr, mcp, agents, nostrbook, nip-01, nip-19, nip-46, remote-signing, read-only, spec-from-source, trust-boundary, civil-liberties

AI coding assistants are now routinely asked to write Nostr clients, bots and relay tools. The weak spot is usually the spec, not the code. A model's memory of NIP-01 or NIP-46 is a snapshot from training, and where it has gaps it guesses with confidence.
Nostrbook is a Nostr documentation site from Soapbox, and its MCP server, published on npm as @nostrbook/mcp, aims at exactly that gap. In its own words, it "allows AI tools and agents to directly access structured Nostr documentation programmatically." As of 20 October 2026, the npm registry's latest version is 0.5.3, published on 3 March 2026.
Thesis, stated once: a documentation MCP server is read-only by design, and that is what makes it easy to say yes to. The line worth watching is not what an agent can read, but whether anything in its reach can sign. This server can't. Keep it that way when you add the next tool.
What the seven tools actually do
We read the tool definitions in src/NostrMcpServer.ts on GitLab and checked them against the published 0.5.3 bundle. There are seven:
read_nip(nip)fetches a NIP as markdown straight from the nostr-protocol/nips repository on GitHub. The argument must be a two-character uppercase hex string such as01orC7.read_kind(kind)fetches the documentation page for an event kind from nostrbook.dev, for example/kinds/1.md.read_tag(tag)does the same for a tag, for example/tags/p.md.read_protocol(doc)fetches one of five basics pages:index,event,filter,clientorrelay.read_nips_index()fetches the NIPs repository README, with its tables of NIPs, kinds and tags.fetch_event(identifier)takes a NIP-19 identifier (npub,nprofile,note,neventornaddr) and looks the event up on relays. For a profile identifier it returns the person's kind-0 metadata. For kind 30817, which the source code describes as a NIP document, it returns only the event's content. For everything else it returns the full event JSON.generate_kind(range)picks a kind number in one of four ranges: regular (1000–9999), replaceable (10000–19999), ephemeral (20000–29999) or addressable (30000–39999). It reads the NIPs README, collects every kind number listed in its tables, avoids those numbers and their neighbours within two, skips even numbers and multiples of five, and returns a random survivor. The output reads "Generated unused kind: N", followed by the range description and the line "This kind number is currently not used by any official NIP."
Two details matter. generate_kind reserves nothing: it's a random pick checked against README tables, so a kind defined only in a NIP's text won't be avoided, and two people can draw the same number. And the nostrbook.dev/mcp page currently lists only the first five tools. The npm README and the source list all seven, so trust the source.
None of the seven tools builds, signs or publishes an event. fetch_event goes further and rejects nsec input outright: "nsec identifiers are not supported for security reasons." The bundled Nostr libraries do contain signing functions, as any Nostr library does, but no registered tool calls them.
Why spec-from-source beats training data
Nostr's spec is a living repository, not a book. NIP-46 itself opens with a "Changes" section noting that remote-signer-key was introduced, that clients "must call get_public_key after connect", and that "nip05 login is removed". A model trained before those edits will describe the old flow fluently. A tool that fetches 46.md when you ask is only as current as the repository's master branch, which is the point.
Exact details matter too. NIP-01 defines the event id as the sha256 of a specific serialisation and the sig as a Schnorr signature over that id on secp256k1. It also defines kind ranges precisely: "for kind n such that 20000 <= n < 30000, events are ephemeral, which means they are not expected to be stored by relays."
One distinction: read_nip and read_nips_index pull from the NIPs repository itself, while read_kind, read_tag and read_protocol pull Nostrbook's own explanatory pages. The protocol/event page, for instance, restates NIP-01's event fields faithfully. But when two sources disagree, the NIPs repository is the spec. Cite it, not the summary and not the model.
fetch_event reads public data, and the relay sees you ask
fetch_event is the only tool that touches the live network, and its behaviour in 0.5.3 is specific. For an npub or note, it queries four default relays hardcoded in the source: relay.ditto.pub, relay.primal.net, relay.damus.io and nos.lol. For nprofile, nevent and naddr, it uses the relay hints inside the identifier and falls back to those defaults if there are none. The source has no setting to change the default list.
Three things follow. None is a flaw; it's how Nostr works.
The relay sees what you ask for. Under NIP-01 a client sends ["REQ", <subscription_id>, <filters>] over a WebSocket, and the filter carries the author pubkey, event id or d tag you're after. Whoever runs the relay receives that filter from the network address where the MCP server is running. NIP-01 also notes that relays "MAY limit number of connections from specific IP/client/etc.", which is a reminder that they can see those addresses. If an agent looks up a series of profiles, those relays see the series.
Relays are sources, not authorities. A relay returns what it has, or nothing. "No event found" means these relays didn't return it, not that it doesn't exist. Full event JSON comes back with id, pubkey and sig, so the signature can be checked against NIP-01 if it matters. The kind-30817 shortcut returns the content alone, without the signature or author fields.
Event content is untrusted input. A stranger's note goes straight into the model's context. A read-only tool can't act on it; an agent with other tools might. That's the general point of our note on MCP trust boundaries: the risk of a tool depends on what else is in the loop.
Where signing belongs, if an agent ever needs to publish
Sooner or later someone wants the agent to post, and the shortcut is an nsec in the MCP config or an environment variable. Don't. NIP-19 says bech32 strings like nsec are "only meant for displaying to users, copy-pasting, sharing, rendering QR codes and inputting data." And a config file or environment variable is readable by the agent host, by every process it starts, and often by logs, backups and dotfile syncs. Our note on why the nsec is your account covers the wider handling.
NIP-46 is the spec-level answer. Its rationale reads: "Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface." The agent's side holds only a disposable client keypair. It sends sign_event with an unsigned {kind, content, tags, created_at}, and the remote signer, which holds the user key, decides whether to return a signed event. Permissions can be scoped by kind, such as sign_event:1. The spec also warns that client-supplied metadata "MUST NOT" be used for authorisation, and that logout "is not a security boundary".
The spec leaves per-request prompting to the signer. For an agent, choose a setup that asks before each event, scoped to the kind you expect. Then the model can draft, but only you can sign.
What this is not
This is not a review, endorsement or ranking of Nostrbook, its MCP server, any signer or any relay, and not an installation guide. It doesn't claim the server is unsafe; its tool set is narrow and its nsec refusal deliberate. It isn't a privacy guarantee either: we describe what the 0.5.3 source does, not how any relay operator handles logs. Nor is it a case against agents publishing; it's about who holds the key. It has nothing to do with tokens, prices, custody or trading. The tool list and default relays are true of 0.5.3 as of 20 October 2026. The README installs @nostrbook/mcp@latest, so read the source of the version you actually run.
The one-line takeaway: let the agent read the spec from source, treat what relays return as public and observed, and keep the key behind a signer that asks. Running the agent in a container doesn't change that; the container is not the boundary.
Related on CryptoDecentral
- Nostr pillar
- Your nsec Is Not a Password: Nostr Key Handling and Signers
- Inference Residency Is Not Action Residency: Tools, MCP, and Local LLMs
- Docker Is the Starting Line, Not the Boundary: Mapping Every Bridge Out of Agent Zero
- AUTH Is Not Identity: What NIP-42 Relay Challenges Actually Prove
- NIP-65: outbox relay lists
Further reading (primary)
- Nostrbook: Nostr MCP integration
- @nostrbook/mcp on npm (README, version 0.5.3)
- soapbox-pub/nostrbook-mcp on GitLab (source)
- NostrMcpServer.ts (tool definitions)
- soapbox-pub/nostrbook on GitLab (documentation site)
- NIP-01: basic protocol (events, signatures, REQ, kind ranges)
- NIP-19: bech32-encoded entities (npub, nsec, nevent, naddr)
- NIP-46: Nostr remote signing
- NIPs README (index of NIPs, kinds and tags)
Sources
- https://nostrbook.dev/mcp
- https://www.npmjs.com/package/@nostrbook/mcp
- https://registry.npmjs.org/@nostrbook/mcp
- https://gitlab.com/soapbox-pub/nostrbook-mcp
- https://gitlab.com/soapbox-pub/nostrbook-mcp/-/blob/main/src/NostrMcpServer.ts
- https://gitlab.com/soapbox-pub/nostrbook
- https://nostrbook.dev/protocol/event.md
- https://nostrbook.dev/protocol/client.md
- https://github.com/nostr-protocol/nips/blob/master/01.md
- https://github.com/nostr-protocol/nips/blob/master/19.md
- https://github.com/nostr-protocol/nips/blob/master/46.md
- https://github.com/nostr-protocol/nips/blob/master/README.md