The Rust SDK implements ContextVM: MCP over Nostr.
In practice, it lets you transport MCP JSON-RPC messages through Nostr events, add server discovery through announcement events, and optionally encrypt direct traffic with NIP-44 plus gift wrapping.
For native Rust applications, ContextVM is primarily a transport for rmcp.
That means the usual shape is:
- define an
rmcpserver or client - create a ContextVM Nostr transport
- attach the transport with
ServiceExt
This is the same pattern shown by the rmcp server and client examples. The only difference is that this SDK replaces stdio, HTTP, or raw sockets with Nostr transports.
Most users should start with one of these entry points:
| Use case | Start with |
|---|---|
| Build a native ContextVM server | NostrServerTransport + rmcp ServiceExt |
| Build a native ContextVM client | NostrClientTransport + rmcp ServiceExt |
| Expose an already-existing MCP server on Nostr | NostrMCPGateway |
| Connect to a remote ContextVM server with a simpler bridge | NostrMCPProxy |
| Discover public servers and capabilities | discover_servers() and related helpers |
| Work directly with the optional bridge layer | NostrMCPGateway::serve_handler() or NostrMCPProxy::serve_client_handler() |
The crate is organized in layers:
core: protocol types, validation, serialization, errorsrelay: relay pool abstractionsigner: key generation and signer helpersencryption: NIP-44 and gift-wrap helperstransport: native ContextVM client and server transportsgateway: wrapper for exposing an existing MCP server flow on Nostrproxy: wrapper for connecting to a remote server without the fullrmcpclient modeldiscovery: announcement and capability discovery
The application-facing rmcp layer provides the ServiceExt integration point together with the usual server and client startup flow.
ContextVM keeps MCP semantics intact and uses Nostr only as the transport envelope.
- MCP payloads are represented by
JsonRpcMessage - direct plaintext ContextVM traffic uses kind
25910 - encrypted traffic uses gift-wrap kinds
1059or21059 - public discovery uses kinds
11316through11320 - routing is done with
ptags and request/response correlation withetags, as reflected in the repository root README
EncryptionMode:Optional,Required,DisabledGiftWrapMode:Optional,Ephemeral,Persistentcontextvm_sdk::ServerInfo: announcement metadataCapabilityExclusion: allowlist bypass rules for specific methods or capabilities
- generate keys with
signer::generate()or load an existing private key withfrom_sk() - configure
NostrServerTransportConfig - create
NostrServerTransport - attach it to an
rmcpserver withServiceExt - optionally publish announcements with
announce()
- generate keys with
signer::generate()or load an existing private key withfrom_sk() - configure
NostrClientTransportConfig - create
NostrClientTransport - attach it to an
rmcpclient withServiceExt
If you are not building a native rmcp service directly, use the wrapper layer:
NostrMCPGatewayfor server-side bridgingNostrMCPProxyfor client-side bridging
- create a
RelayPool - query
discover_servers() - fetch public tools, resources, and prompts with the discovery helpers
The Rust SDK already implements behavior that users should rely on:
- stateless client initialization behavior
- announcement publication and deletion
- encryption negotiation and response mirroring
- rmcp conversion and routing flow
Use the task-oriented pages in this directory for those details. Start with the native server and native client guides if you are building ContextVM applications directly.