client-dev
ContextVM Client Development
Build MCP clients that connect to ContextVM servers over the Nostr network.
Quick Start
Connect to a ContextVM server:
import { Client } from '@modelcontextprotocol/sdk/client';
import {
NostrClientTransport,
PrivateKeySigner,
ApplesauceRelayPool,
EncryptionMode,
} from '@contextvm/sdk';
const signer = new PrivateKeySigner(process.env.CLIENT_PRIVATE_KEY!);
const relayPool = new ApplesauceRelayPool(['wss://relay.contextvm.org', 'wss://cvm.otherstuff.ai']);
const SERVER_PUBKEY = 'server-public-key-hex';
const transport = new NostrClientTransport({
signer,
serverPubkey: SERVER_PUBKEY,
encryptionMode: EncryptionMode.OPTIONAL,
});
const client = new Client({
name: 'my-client',
version: '1.0.0',
});
await client.connect(transport);
// Use the client
const tools = await client.listTools();
const result = await client.callTool({
name: 'echo',
arguments: { message: 'Hello' },
});
Server Discovery
Direct Connection (Known Pubkey)
Connect when you know the server's public key:
const transport = new NostrClientTransport({
signer,
relayHandler: relayPool,
serverPubkey: 'known-server-pubkey',
});
Discovery via Announcements
Find servers broadcasting on the network:
import { CTXVM_MESSAGES_KIND, SERVER_ANNOUNCEMENT_KIND } from '@contextvm/sdk';
// Query relays for server announcements
await relayPool.subscribe([{ kinds: [SERVER_ANNOUNCEMENT_KIND] }], (event) => {
const serverInfo = JSON.parse(event.content);
console.log(`Found server: ${serverInfo.serverInfo.name}`);
console.log(`Pubkey: ${event.pubkey}`);
});
NostrClientTransport Options
| Option | Type | Description |
|---|---|---|
signer |
NostrSigner |
Required. Signs all Nostr events |
relayHandler |
RelayHandler | string[] |
Optional explicit operational relays |
serverPubkey |
string |
Required. Target server's public key |
discoveryRelayUrls |
string[] |
Optional relay URLs for CEP-17 discovery lookups |
encryptionMode |
EncryptionMode |
OPTIONAL, REQUIRED, or DISABLED |
isStateless |
boolean |
Skip initialization handshake. Default: false |
oversizedTransfer |
object |
CEP-22 oversized payload transfer configuration |
logLevel |
LogLevel |
Logging verbosity |
Oversized Transfer
NostrClientTransport supports CEP-22 oversized payload transfer automatically.
- enabled by default
- used when a request is too large for practical relay event limits
- also used when receiving oversized server responses
- usually requires no application-level logic
For stateless client-to-server flows, the transport may send start, wait for accept, and then continue automatically.
Example:
const transport = new NostrClientTransport({
signer,
serverPubkey: SERVER_PUBKEY,
oversizedTransfer: {
enabled: true,
thresholdBytes: 48_000,
},
});
Disable it only when you explicitly do not want CEP-22 fragmentation:
const transport = new NostrClientTransport({
signer,
serverPubkey: SERVER_PUBKEY,
oversizedTransfer: {
enabled: false,
},
});
Relay Resolution Order
NostrClientTransport resolves operational relays in this order:
- explicit operational relays from
relayHandler - relay hints embedded in
nprofile - CEP-17 relay-list discovery via
discoveryRelayUrls - SDK bootstrap discovery relays when
discoveryRelayUrlsis omitted
This allows leaner client setup when the target server already publishes kind:10002 metadata.
Use relayHandler when you already know the relays you want to operate against or when you want tighter control over relay behavior. Omit it when you want the SDK to resolve relays dynamically from server hints and relay-list metadata.
Stateless Mode
Skip the initialization handshake for faster connections:
const transport = new NostrClientTransport({
signer,
serverPubkey: SERVER_PUBKEY,
isStateless: true, // Skip initialize roundtrip
});
Proxy Pattern
Use NostrMCPProxy to connect existing MCP clients to ContextVM servers:
import { NostrMCPProxy } from '@contextvm/sdk';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const proxy = new NostrMCPProxy({
// Local transport for existing client to connect to
mcpHostTransport: new StdioServerTransport(),
// Remote server connection
nostrTransportOptions: {
signer,
serverPubkey: SERVER_PUBKEY,
},
});
await proxy.start();
This allows any standard MCP client to use ContextVM servers without native support.
Encryption
Control encryption behavior:
// Require encrypted connections only
encryptionMode: EncryptionMode.REQUIRED;
// Use encryption if server supports it (default)
encryptionMode: EncryptionMode.OPTIONAL;
// Never use encryption
encryptionMode: EncryptionMode.DISABLED;
Client Templates
See assets/client-template.ts for a complete boilerplate.
Tooling: ctxcn (generate a typed TypeScript client)
If you are building a TypeScript app and want remote ContextVM tools to feel like local functions, use ctxcn.
High-level behavior:
- Connects to a ContextVM server.
- Reads
tools/listschemas. - Generates TypeScript client code into your repo (shadcn-style: you own the generated code).
From ContextVM docs/blog references, the basic flow is:
npx @contextvm/ctxcn init
npx @contextvm/ctxcn add <server-pubkey>
npx @contextvm/ctxcn update
Use this when:
- You want end-to-end type safety.
- You want IDE autocomplete for server tools.
- You want to avoid hand-writing tool interfaces.
Reference Materials
references/nostr-way-without-sdks.md- The Nostr primitives behind CVM (raw events, JSON-RPC, manual implementation)references/discovery.md- Server discovery patternsreferences/proxy-pattern.md- Using NostrMCPProxyreferences/stateless-mode.md- Stateless connection details
More from contextvm/cvmi
overview
Understand ContextVM protocol fundamentals, architecture, and core concepts. Use when users need to learn about ContextVM basics, how it bridges MCP with Nostr, protocol design principles, event kinds, or the relationship between MCP and Nostr in decentralized communication.
32concepts
Understand ContextVM core concepts, architecture decisions, and frequently asked questions. Use when users need clarification on what ContextVM is, why it uses Nostr, decentralization benefits, public vs private servers, network topology, or comparisons with traditional MCP.
32typescript-sdk
Use the @contextvm/sdk TypeScript SDK effectively. Reference for core interfaces, signers, relay handlers, transports, encryption, logging, and SDK patterns. Use when implementing SDK components, extending interfaces, configuring transports, or debugging SDK usage.
32server-dev
Build MCP servers that expose capabilities over the Nostr network using ContextVM. Use when creating new servers, converting existing MCP servers to ContextVM, configuring server transports, implementing access control, or setting up public server announcements.
31payments
Implement CEP-8 payments in ContextVM using the @contextvm/sdk payments middleware. Use when building paid servers/clients, configuring priced capabilities, integrating payment processors/handlers, or troubleshooting payment notification flows.
24troubleshooting
Diagnose and resolve ContextVM connection issues, relay problems, encryption failures, authentication errors, and common deployment problems. Use when users report errors, connection failures, timeout issues, or unexpected behavior with ContextVM clients, servers, gateways, or proxies.
17