mirror of
https://github.com/aljazceru/contextvm-docs.git
synced 2025-12-19 06:54:21 +01:00
fix: some typos and format
This commit is contained in:
8
.github/workflows/deploy.yml
vendored
8
.github/workflows/deploy.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
# Trigger the workflow every time you push to the `main` branch
|
# Trigger the workflow every time you push to the `main` branch
|
||||||
# Using a different branch name? Replace `main` with your branch’s name
|
# Using a different branch name? Replace `main` with your branch’s name
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
# Allows you to run this workflow manually from the Actions tab on GitHub.
|
# Allows you to run this workflow manually from the Actions tab on GitHub.
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -23,9 +23,9 @@ jobs:
|
|||||||
- name: Install, build, and upload your site
|
- name: Install, build, and upload your site
|
||||||
uses: withastro/action@v3
|
uses: withastro/action@v3
|
||||||
# with:
|
# with:
|
||||||
# path: . # The root location of your Astro project inside the repository. (optional)
|
# path: . # The root location of your Astro project inside the repository. (optional)
|
||||||
# node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 20. (optional)
|
# node-version: 20 # The specific version of Node that should be used to build your site. Defaults to 20. (optional)
|
||||||
# package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional)
|
# package-manager: pnpm@latest # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional)
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
needs: build
|
needs: build
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -1,54 +1,4 @@
|
|||||||
# Starlight Starter Kit: Basics
|
# ContextVM Docs
|
||||||
|
|
||||||
[](https://starlight.astro.build)
|
This is the documentation site for the ContextVM protocol.
|
||||||
|
Built with [Astro](https://astro.build), and starlight theme.
|
||||||
```
|
|
||||||
bun create astro@latest -- --template starlight
|
|
||||||
```
|
|
||||||
|
|
||||||
[](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)
|
|
||||||
[](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)
|
|
||||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics)
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)
|
|
||||||
|
|
||||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
|
||||||
|
|
||||||
## 🚀 Project Structure
|
|
||||||
|
|
||||||
Inside of your Astro + Starlight project, you'll see the following folders and files:
|
|
||||||
|
|
||||||
```
|
|
||||||
.
|
|
||||||
├── public/
|
|
||||||
├── src/
|
|
||||||
│ ├── assets/
|
|
||||||
│ ├── content/
|
|
||||||
│ │ └── docs/
|
|
||||||
│ └── content.config.ts
|
|
||||||
├── astro.config.mjs
|
|
||||||
├── package.json
|
|
||||||
└── tsconfig.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Starlight looks for `` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
|
|
||||||
|
|
||||||
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
|
|
||||||
|
|
||||||
Static assets, like favicons, can be placed in the `public/` directory.
|
|
||||||
|
|
||||||
## 🧞 Commands
|
|
||||||
|
|
||||||
All commands are run from the root of the project, from a terminal:
|
|
||||||
|
|
||||||
| Command | Action |
|
|
||||||
| :------------------------ | :----------------------------------------------- |
|
|
||||||
| `bun install` | Installs dependencies |
|
|
||||||
| `bun dev` | Starts local dev server at `localhost:4321` |
|
|
||||||
| `bun build` | Build your production site to `./dist/` |
|
|
||||||
| `bun preview` | Preview your build locally, before deploying |
|
|
||||||
| `bun astro ...` | Run CLI commands like `astro add`, `astro check` |
|
|
||||||
| `bun astro -- --help` | Get help using the Astro CLI |
|
|
||||||
|
|
||||||
## 👀 Want to learn more?
|
|
||||||
|
|
||||||
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
|
|
||||||
140
astro.config.mjs
140
astro.config.mjs
@@ -1,68 +1,82 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from "astro/config";
|
||||||
import starlight from '@astrojs/starlight';
|
import starlight from "@astrojs/starlight";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://contextvm.github.io',
|
site: "https://contextvm.github.io",
|
||||||
base: 'contextvm-docs',
|
base: "contextvm-docs",
|
||||||
integrations: [
|
integrations: [
|
||||||
starlight({
|
starlight({
|
||||||
title: 'ContextVM Documentation',
|
title: "ContextVM Documentation",
|
||||||
description: 'Documentation for the ContextVM SDK - a virtual machine for context-controlled computation',
|
description: "Documentation for ContextVM",
|
||||||
logo: {
|
logo: {
|
||||||
light: './src/assets/contextvm-logo.svg',
|
light: "./src/assets/contextvm-logo.svg",
|
||||||
dark: './src/assets/contextvm-logo.svg',
|
dark: "./src/assets/contextvm-logo.svg",
|
||||||
replacesTitle: false,
|
replacesTitle: false,
|
||||||
},
|
},
|
||||||
social: [
|
social: [
|
||||||
{ icon: 'github', label: 'ContextVM', href: 'https://github.com/contextvm/ts-sdk' },
|
{
|
||||||
],
|
icon: "github",
|
||||||
sidebar: [
|
label: "ContextVM",
|
||||||
{
|
href: "https://github.com/contextvm/ts-sdk",
|
||||||
label: 'Getting Started',
|
},
|
||||||
items: [
|
],
|
||||||
{ label: 'Quick Overview', slug: 'getting-started/quick-overview' },
|
sidebar: [
|
||||||
],
|
{
|
||||||
},
|
label: "Getting Started",
|
||||||
{
|
items: [
|
||||||
label: 'Specification',
|
{ label: "Quick Overview", slug: "getting-started/quick-overview" },
|
||||||
items: [
|
],
|
||||||
{ label: 'Specification', slug: 'ctxvm-draft-spec' },
|
},
|
||||||
],
|
{
|
||||||
},
|
label: "Specification",
|
||||||
{
|
items: [{ label: "Specification", slug: "ctxvm-draft-spec" }],
|
||||||
label: 'Core Concepts',
|
},
|
||||||
items: [
|
{
|
||||||
{ label: 'Constants', slug: 'core/constants' },
|
label: "Core Concepts",
|
||||||
{ label: 'Interfaces', slug: 'core/interfaces' },
|
items: [
|
||||||
{ label: 'Encryption', slug: 'core/encryption' },
|
{ label: "Constants", slug: "core/constants" },
|
||||||
],
|
{ label: "Interfaces", slug: "core/interfaces" },
|
||||||
},
|
{ label: "Encryption", slug: "core/encryption" },
|
||||||
{
|
],
|
||||||
label: 'Components',
|
},
|
||||||
items: [
|
{
|
||||||
{ label: 'Gateway', slug: 'gateway/overview' },
|
label: "Transports",
|
||||||
{ label: 'Relay', slug: 'relay/simple-relay-pool' },
|
items: [
|
||||||
{ label: 'Signer', slug: 'signer/private-key-signer' },
|
{
|
||||||
{ label: 'Proxy', slug: 'proxy/overview' },
|
label: "Base Nostr Transport",
|
||||||
],
|
slug: "transports/base-nostr-transport",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Tutorials',
|
label: "Nostr Client",
|
||||||
items: [
|
slug: "transports/nostr-client-transport",
|
||||||
{ label: 'Client-Server Communication', slug: 'tutorials/client-server-communication' },
|
},
|
||||||
],
|
{
|
||||||
},
|
label: "Nostr Server",
|
||||||
{
|
slug: "transports/nostr-server-transport",
|
||||||
label: 'Transports',
|
},
|
||||||
items: [
|
],
|
||||||
{ label: 'Base Nostr Transport', slug: 'transports/base-nostr-transport' },
|
},
|
||||||
{ label: 'Nostr Client', slug: 'transports/nostr-client-transport' },
|
{
|
||||||
{ label: 'Nostr Server', slug: 'transports/nostr-server-transport' },
|
label: "Components",
|
||||||
],
|
items: [
|
||||||
},
|
{ label: "Gateway", slug: "gateway/overview" },
|
||||||
],
|
{ label: "Relay", slug: "relay/simple-relay-pool" },
|
||||||
}),
|
{ label: "Signer", slug: "signer/private-key-signer" },
|
||||||
],
|
{ label: "Proxy", slug: "proxy/overview" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Tutorials",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: "Client-Server Communication",
|
||||||
|
slug: "tutorials/client-server-communication",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
3
bun.lock
3
bun.lock
@@ -6,6 +6,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.35.0",
|
"@astrojs/starlight": "^0.35.0",
|
||||||
"astro": "^5.6.1",
|
"astro": "^5.6.1",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
"sharp": "^0.34.2",
|
"sharp": "^0.34.2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -689,6 +690,8 @@
|
|||||||
|
|
||||||
"postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
"postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||||
|
|
||||||
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||||
|
|
||||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||||
|
|
||||||
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.35.0",
|
"@astrojs/starlight": "^0.35.0",
|
||||||
"astro": "^5.6.1",
|
"astro": "^5.6.1",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
"sharp": "^0.34.2"
|
"sharp": "^0.34.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defineCollection } from 'astro:content';
|
import { defineCollection } from "astro:content";
|
||||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
import { docsLoader } from "@astrojs/starlight/loaders";
|
||||||
import { docsSchema } from '@astrojs/starlight/schema';
|
import { docsSchema } from "@astrojs/starlight/schema";
|
||||||
|
|
||||||
export const collections = {
|
export const collections = {
|
||||||
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,29 +11,29 @@ The `@contextvm/sdk` exports a set of constants that are used throughout the lib
|
|||||||
|
|
||||||
The ContextVM protocol defines several Nostr event kinds for different types of messages.
|
The ContextVM protocol defines several Nostr event kinds for different types of messages.
|
||||||
|
|
||||||
| Constant | Kind | Description |
|
| Constant | Kind | Description |
|
||||||
| ----------------------------- | ----- | --------------------------------------------------------------------------- |
|
| ----------------------------- | ----- | ----------------------------------------------------------------------------- |
|
||||||
| `CTXVM_MESSAGES_KIND` | 25910 | The kind for standard, ephemeral ContextVM messages. |
|
| `CTXVM_MESSAGES_KIND` | 25910 | The kind for standard, ephemeral ContextVM messages. |
|
||||||
| `GIFT_WRAP_KIND` | 1059 | The kind for encrypted messages, wrapped using the NIP-59 gift wrap standard. |
|
| `GIFT_WRAP_KIND` | 1059 | The kind for encrypted messages, wrapped using the NIP-59 gift wrap standard. |
|
||||||
| `SERVER_ANNOUNCEMENT_KIND` | 11316 | A replaceable event for announcing a server's presence and basic info. |
|
| `SERVER_ANNOUNCEMENT_KIND` | 11316 | A replaceable event for announcing a server's presence and basic info. |
|
||||||
| `TOOLS_LIST_KIND` | 11317 | A replaceable event for listing a server's available tools. |
|
| `TOOLS_LIST_KIND` | 11317 | A replaceable event for listing a server's available tools. |
|
||||||
| `RESOURCES_LIST_KIND` | 11318 | A replaceable event for listing a server's available resources. |
|
| `RESOURCES_LIST_KIND` | 11318 | A replaceable event for listing a server's available resources. |
|
||||||
| `RESOURCETEMPLATES_LIST_KIND` | 11319 | A replaceable event for listing a server's available resource templates. |
|
| `RESOURCETEMPLATES_LIST_KIND` | 11319 | A replaceable event for listing a server's available resource templates. |
|
||||||
| `PROMPTS_LIST_KIND` | 11320 | A replaceable event for listing a server's available prompts. |
|
| `PROMPTS_LIST_KIND` | 11320 | A replaceable event for listing a server's available prompts. |
|
||||||
|
|
||||||
## Nostr Tags
|
## Nostr Tags
|
||||||
|
|
||||||
The SDK defines an object `NOSTR_TAGS` that contains constants for the various Nostr event tags used in the ContextVM protocol.
|
The SDK defines an object `NOSTR_TAGS` that contains constants for the various Nostr event tags used in the ContextVM protocol.
|
||||||
|
|
||||||
| Key | Tag | Description |
|
| Key | Tag | Description |
|
||||||
| -------------------- | -------------------- | ------------------------------------------------------------------------ |
|
| -------------------- | -------------------- | ---------------------------------------------------------------------- |
|
||||||
| `PUBKEY` | `p` | The public key of the message recipient. |
|
| `PUBKEY` | `p` | The public key of the message recipient. |
|
||||||
| `EVENT_ID` | `e` | The event ID used to correlate requests and responses. |
|
| `EVENT_ID` | `e` | The event ID used to correlate requests and responses. |
|
||||||
| `CAPABILITY` | `cap` | A tag for specifying pricing metadata for a tool, resource, or prompt. |
|
| `CAPABILITY` | `cap` | A tag for specifying pricing metadata for a tool, resource, or prompt. |
|
||||||
| `NAME` | `name` | The human-readable name of a server in an announcement. |
|
| `NAME` | `name` | The human-readable name of a server in an announcement. |
|
||||||
| `WEBSITE` | `website` | The URL of a server's website in an announcement. |
|
| `WEBSITE` | `website` | The URL of a server's website in an announcement. |
|
||||||
| `PICTURE` | `picture` | The URL of a server's icon in an announcement. |
|
| `PICTURE` | `picture` | The URL of a server's icon in an announcement. |
|
||||||
| `SUPPORT_ENCRYPTION` | `support_encryption` | A tag indicating that a server supports end-to-end encryption. |
|
| `SUPPORT_ENCRYPTION` | `support_encryption` | A tag indicating that a server supports end-to-end encryption. |
|
||||||
|
|
||||||
## Announcement Methods
|
## Announcement Methods
|
||||||
|
|
||||||
@@ -41,11 +41,11 @@ The `announcementMethods` object maps capability types to their corresponding MC
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export const announcementMethods = {
|
export const announcementMethods = {
|
||||||
server: 'initialize',
|
server: "initialize",
|
||||||
tools: 'tools/list',
|
tools: "tools/list",
|
||||||
resources: 'resources/list',
|
resources: "resources/list",
|
||||||
resourceTemplates: 'resources/templates/list',
|
resourceTemplates: "resources/templates/list",
|
||||||
prompts: 'prompts/list',
|
prompts: "prompts/list",
|
||||||
} as const;
|
} as const;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ The standard implementation of NIP-17 is designed for persistent private message
|
|||||||
Encryption is configured at the transport level using the `EncryptionMode` enum. You can set the desired mode when creating a `NostrClientTransport` or `NostrServerTransport`.
|
Encryption is configured at the transport level using the `EncryptionMode` enum. You can set the desired mode when creating a `NostrClientTransport` or `NostrServerTransport`.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { NostrClientTransport } from '@ctxvm/sdk/transport';
|
import { NostrClientTransport } from "@ctxvm/sdk/transport";
|
||||||
import { EncryptionMode } from '@ctxvm/sdk/core';
|
import { EncryptionMode } from "@ctxvm/sdk/core";
|
||||||
|
|
||||||
const transport = new NostrClientTransport({
|
const transport = new NostrClientTransport({
|
||||||
// ... other options
|
// ... other options
|
||||||
@@ -47,9 +47,9 @@ const transport = new NostrClientTransport({
|
|||||||
|
|
||||||
### `EncryptionMode`
|
### `EncryptionMode`
|
||||||
|
|
||||||
- **`REQUIRED`**: The transport will only communicate with peers that support encryption. If the other party does not support it, the connection will fail.
|
- **`REQUIRED`**: The transport will only communicate with peers that support encryption. If the other party does not support it, the connection will fail.
|
||||||
- **`OPTIONAL`**: (Default) The transport will attempt to use encryption if the peer supports it. If not, it will fall back to unencrypted communication.
|
- **`OPTIONAL`**: (Default) The transport will attempt to use encryption if the peer supports it. If not, it will fall back to unencrypted communication.
|
||||||
- **`DISABLED`**: The transport will not use encryption, even if the peer supports it.
|
- **`DISABLED`**: The transport will not use encryption, even if the peer supports it.
|
||||||
|
|
||||||
## Encryption Support Discovery
|
## Encryption Support Discovery
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ Clients and servers can discover if a peer supports encryption in two ways:
|
|||||||
|
|
||||||
The core encryption functions are exposed in the `core` module:
|
The core encryption functions are exposed in the `core` module:
|
||||||
|
|
||||||
- `encryptMessage(message: string, recipientPublicKey: string): NostrEvent`
|
- `encryptMessage(message: string, recipientPublicKey: string): NostrEvent`
|
||||||
- `decryptMessage(event: NostrEvent, signer: NostrSigner): Promise<string>`
|
- `decryptMessage(event: NostrEvent, signer: NostrSigner): Promise<string>`
|
||||||
|
|
||||||
These functions handle the low-level details of gift wrapping and unwrapping, but in most cases, you will interact with encryption through the transport's `encryptionMode` setting.
|
These functions handle the low-level details of gift wrapping and unwrapping, but in most cases, you will interact with encryption through the transport's `encryptionMode` setting.
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ export interface NostrSigner {
|
|||||||
|
|
||||||
Any object that implements this interface can be used to sign events, allowing you to integrate with various key management systems, such as web, hardware wallets or remote signing services. The SDK provides a default implementation, `PrivateKeySigner`, which signs events using a raw private key.
|
Any object that implements this interface can be used to sign events, allowing you to integrate with various key management systems, such as web, hardware wallets or remote signing services. The SDK provides a default implementation, `PrivateKeySigner`, which signs events using a raw private key.
|
||||||
|
|
||||||
- **Learn more:** [`NostrSigner` Deep Dive](/contextvm-docs/signer/nostr-signer-interface/)
|
- **Learn more:** [`NostrSigner` Deep Dive](/contextvm-docs/signer/nostr-signer-interface/)
|
||||||
- **Default Implementation:** [`PrivateKeySigner`](/contextvm-docs/signer/private-key-signer/)
|
- **Default Implementation:** [`PrivateKeySigner`](/contextvm-docs/signer/private-key-signer/)
|
||||||
|
|
||||||
## `RelayHandler`
|
## `RelayHandler`
|
||||||
|
|
||||||
@@ -59,8 +59,8 @@ export interface RelayHandler {
|
|||||||
|
|
||||||
By implementing this interface, you can create custom relay management logic, such as sophisticated relay selection strategies or custom reconnection policies. The SDK includes `SimpleRelayPool` as a default implementation.
|
By implementing this interface, you can create custom relay management logic, such as sophisticated relay selection strategies or custom reconnection policies. The SDK includes `SimpleRelayPool` as a default implementation.
|
||||||
|
|
||||||
- **Learn more:** [`RelayHandler` Deep Dive](/contextvm-docs/relay/relay-handler-interface)
|
- **Learn more:** [`RelayHandler` Deep Dive](/contextvm-docs/relay/relay-handler-interface)
|
||||||
- **Default Implementation:** [`SimpleRelayPool`](/contextvm-docs/relay/simple-relay-pool)
|
- **Default Implementation:** [`SimpleRelayPool`](/contextvm-docs/relay/simple-relay-pool)
|
||||||
|
|
||||||
## `EncryptionMode`
|
## `EncryptionMode`
|
||||||
|
|
||||||
@@ -68,15 +68,15 @@ The `EncryptionMode` enum defines the encryption policy for a transport.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export enum EncryptionMode {
|
export enum EncryptionMode {
|
||||||
OPTIONAL = 'optional',
|
OPTIONAL = "optional",
|
||||||
REQUIRED = 'required',
|
REQUIRED = "required",
|
||||||
DISABLED = 'disabled',
|
DISABLED = "disabled",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This enum is used to configure the encryption behavior of the `NostrClientTransport` and `NostrServerTransport`.
|
This enum is used to configure the encryption behavior of the `NostrClientTransport` and `NostrServerTransport`.
|
||||||
|
|
||||||
- **Learn more:** [Encryption](/contextvm-docs/core/encryption)
|
- **Learn more:** [Encryption](/contextvm-docs/core/encryption)
|
||||||
|
|
||||||
## `AnnouncementMethods`
|
## `AnnouncementMethods`
|
||||||
|
|
||||||
@@ -84,11 +84,11 @@ The `AnnouncementMethods` interface defines methods for announcing server capabi
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export interface AnnouncementMethods {
|
export interface AnnouncementMethods {
|
||||||
server: InitializeRequest['method'];
|
server: InitializeRequest["method"];
|
||||||
tools: ListToolsRequest['method'];
|
tools: ListToolsRequest["method"];
|
||||||
resources: ListResourcesRequest['method'];
|
resources: ListResourcesRequest["method"];
|
||||||
resourceTemplates: ListResourceTemplatesRequest['method'];
|
resourceTemplates: ListResourceTemplatesRequest["method"];
|
||||||
prompts: ListPromptsRequest['method'];
|
prompts: ListPromptsRequest["method"];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ title: ContextVM Protocol Specification
|
|||||||
description: Technical specification for the ContextVM protocol
|
description: Technical specification for the ContextVM protocol
|
||||||
---
|
---
|
||||||
|
|
||||||
**MCP Version:** `mcp:2025-03-26`
|
|
||||||
**Status:** Draft
|
**Status:** Draft
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
The Context Vending Machine (ContextVM) specification defines how Nostr and Context Vending Machines can be used to expose Model Context Protocol (MCP) server capabilities, enabling standardized usage of these resources through a decentralized, cryptographically secure messaging system.
|
The Context Vending Machine (ContextVM) specification defines how Nostr can be used to expose Model Context Protocol (MCP) server capabilities, enabling standardized usage of these resources through a decentralized, cryptographically secure messaging system.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ The gateway's role is to forward requests from Nostr clients to the MCP server a
|
|||||||
|
|
||||||
The `NostrMCPGateway` is ideal for:
|
The `NostrMCPGateway` is ideal for:
|
||||||
|
|
||||||
- **Exposing Existing Servers**: If you have an existing MCP server, you can use the gateway to make it accessible to Nostr clients without modifying the server's core logic. The server continues to operate with its standard transport, while the gateway handles all Nostr-related communication.
|
- **Exposing Existing Servers**: If you have an existing MCP server, you can use the gateway to make it accessible to Nostr clients without modifying the server's core logic. The server continues to operate with its standard transport, while the gateway handles all Nostr-related communication.
|
||||||
- **Decoupling Services**: You can run your core MCP server in a secure environment and use the gateway as a public-facing entry point on the Nostr network. The gateway can be configured with its own security policies (like `allowedPublicKeys`).
|
- **Decoupling Services**: You can run your core MCP server in a secure environment and use the gateway as a public-facing entry point on the Nostr network. The gateway can be configured with its own security policies (like `allowedPublicKeys`).
|
||||||
- **Adding Nostr Capabilities**: It allows you to add features like public server announcements and decentralized discovery to a conventional MCP server.
|
- **Adding Nostr Capabilities**: It allows you to add features like public server announcements and decentralized discovery to a conventional MCP server.
|
||||||
|
|
||||||
## `NostrMCPGatewayOptions`
|
## `NostrMCPGatewayOptions`
|
||||||
|
|
||||||
@@ -35,27 +35,27 @@ export interface NostrMCPGatewayOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`mcpServerTransport`**: An instance of a client-side MCP transport that the gateway will use to connect to your existing MCP server. For example, `new StdioClientTransport(...)`.
|
- **`mcpServerTransport`**: An instance of a client-side MCP transport that the gateway will use to connect to your existing MCP server. For example, `new StdioClientTransport(...)`.
|
||||||
- **`nostrTransportOptions`**: The full configuration object required by the `NostrServerTransport`. This includes the `signer`, `relayHandler`, and options like `isPublicServer`.
|
- **`nostrTransportOptions`**: The full configuration object required by the `NostrServerTransport`. This includes the `signer`, `relayHandler`, and options like `isPublicServer`.
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
This example shows how to create a gateway that connects to a local MCP server (running in a separate process) and exposes it to the Nostr network.
|
This example shows how to create a gateway that connects to a local MCP server (running in a separate process) and exposes it to the Nostr network.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/stdio';
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/stdio";
|
||||||
import { NostrMCPGateway } from '@ctxvm/sdk/gateway';
|
import { NostrMCPGateway } from "@ctxvm/sdk/gateway";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
|
|
||||||
// 1. Configure the signer and relay handler for the Nostr transport
|
// 1. Configure the signer and relay handler for the Nostr transport
|
||||||
const signer = new PrivateKeySigner('your-gateway-private-key');
|
const signer = new PrivateKeySigner("your-gateway-private-key");
|
||||||
const relayPool = new SimpleRelayPool(['wss://relay.damus.io']);
|
const relayPool = new SimpleRelayPool(["wss://relay.damus.io"]);
|
||||||
|
|
||||||
// 2. Configure the transport to connect to your existing MCP server
|
// 2. Configure the transport to connect to your existing MCP server
|
||||||
const serverTransport = new StdioClientTransport({
|
const serverTransport = new StdioClientTransport({
|
||||||
command: 'bun',
|
command: "bun",
|
||||||
args: ['run', 'path/to/your/mcp-server.ts'],
|
args: ["run", "path/to/your/mcp-server.ts"],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. Create the gateway instance
|
// 3. Create the gateway instance
|
||||||
@@ -71,7 +71,7 @@ const gateway = new NostrMCPGateway({
|
|||||||
// 4. Start the gateway
|
// 4. Start the gateway
|
||||||
await gateway.start();
|
await gateway.start();
|
||||||
|
|
||||||
console.log('Gateway is running, exposing the MCP server to Nostr.');
|
console.log("Gateway is running, exposing the MCP server to Nostr.");
|
||||||
|
|
||||||
// To stop the gateway: await gateway.stop();
|
// To stop the gateway: await gateway.stop();
|
||||||
```
|
```
|
||||||
@@ -80,4 +80,4 @@ console.log('Gateway is running, exposing the MCP server to Nostr.');
|
|||||||
|
|
||||||
This concludes the core components of the SDK. The final section provides practical examples of how to use these components together.
|
This concludes the core components of the SDK. The final section provides practical examples of how to use these components together.
|
||||||
|
|
||||||
- **[Tutorials](/contextvm-docs/tutorials/client-server-communication)**
|
- **[Tutorials](/contextvm-docs/tutorials/client-server-communication)**
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ This will install the SDK and its dependencies into your project.
|
|||||||
|
|
||||||
The SDK is organized into several modules, each providing a specific set of functionalities:
|
The SDK is organized into several modules, each providing a specific set of functionalities:
|
||||||
|
|
||||||
- **[Core](/contextvm-docs/core/interfaces)**: Contains fundamental definitions, constants, interfaces, and utilities (e.g., encryption, serialization).
|
- **[Core](/contextvm-docs/core/interfaces)**: Contains fundamental definitions, constants, interfaces, and utilities (e.g., encryption, serialization).
|
||||||
- **[Transports](/contextvm-docs/transports/base-nostr-transport)**: Critical for communication, this module provides `NostrClientTransport` and `NostrServerTransport` implementations for enabling MCP over Nostr.
|
- **[Transports](/contextvm-docs/transports/base-nostr-transport)**: Critical for communication, this module provides `NostrClientTransport` and `NostrServerTransport` implementations for enabling MCP over Nostr.
|
||||||
- **[Signer](/contextvm-docs/signer/nostr-signer-interface)**: Provides cryptographic signing capabilities required for Nostr events
|
- **[Signer](/contextvm-docs/signer/nostr-signer-interface)**: Provides cryptographic signing capabilities required for Nostr events
|
||||||
- **[Relay](/contextvm-docs/relay/relay-handler-interface)**: Manages Nostr relay connections, abstracting the complexity of relay interactions.
|
- **[Relay](/contextvm-docs/relay/relay-handler-interface)**: Manages Nostr relay connections, abstracting the complexity of relay interactions.
|
||||||
- **[Proxy](/contextvm-docs/proxy/overview)**: A client-side MCP server that connects to other servers through Nostr, exposing their capabilities locally, specially useful for clients that don't natively support Nostr transport.
|
- **[Proxy](/contextvm-docs/proxy/overview)**: A client-side MCP server that connects to other servers through Nostr, exposing their capabilities locally, specially useful for clients that don't natively support Nostr transport.
|
||||||
- **[Gateway](/contextvm-docs/gateway/overview)**: An MCP server transport that binds to another MCP server, exposing its capabilities to the Nostr network, specially useful for servers that don't natively support Nostr transport.
|
- **[Gateway](/contextvm-docs/gateway/overview)**: An MCP server transport that binds to another MCP server, exposing its capabilities to the Nostr network, specially useful for servers that don't natively support Nostr transport.
|
||||||
|
|
||||||
## Core Concepts
|
## Core Concepts
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ The `@contextvm/sdk` is built around a few core concepts that enable the bridgin
|
|||||||
|
|
||||||
At the heart of the SDK are two key interfaces:
|
At the heart of the SDK are two key interfaces:
|
||||||
|
|
||||||
- **`NostrSigner`**: An interface for signing Nostr events. The SDK includes a default `PrivateKeySigner`, but you can create a custom implementation to integrate with other signing mechanisms (e.g., Window.nostr for web, remote signers, etc).
|
- **`NostrSigner`**: An interface for signing Nostr events. The SDK includes a default `PrivateKeySigner`, but you can create a custom implementation to integrate with other signing mechanisms (e.g., Window.nostr for web, remote signers, etc).
|
||||||
- **`RelayHandler`**: An interface for managing connections to Nostr relays. The default `SimpleRelayPool` provides basic relay management, but you can implement your own logic for more sophisticated relay selection and management.
|
- **`RelayHandler`**: An interface for managing connections to Nostr relays. The default `SimpleRelayPool` provides basic relay management, but you can implement your own logic for more sophisticated relay selection and management.
|
||||||
|
|
||||||
These components are fundamental for creating and broadcasting Nostr events, which are the backbone of ContextVM communication.
|
These components are fundamental for creating and broadcasting Nostr events, which are the backbone of ContextVM communication.
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ These components are fundamental for creating and broadcasting Nostr events, whi
|
|||||||
|
|
||||||
The SDK provides two specialized transports to send and receive MCP messages over the Nostr network:
|
The SDK provides two specialized transports to send and receive MCP messages over the Nostr network:
|
||||||
|
|
||||||
- [`NostrClientTransport`](/contextvm-docs/transports/nostr-client-transport): Used by MCP clients to connect to remote MCP servers exposed via Nostr.
|
- [`NostrClientTransport`](/contextvm-docs/transports/nostr-client-transport): Used by MCP clients to connect to remote MCP servers exposed via Nostr.
|
||||||
- [`NostrServerTransport`](/contextvm-docs/transports/nostr-server-transport): Used by MCP servers to expose their capabilities through Nostr.
|
- [`NostrServerTransport`](/contextvm-docs/transports/nostr-server-transport): Used by MCP servers to expose their capabilities through Nostr.
|
||||||
|
|
||||||
These transports handle the serialization of MCP messages into Nostr events and manage the communication flow.
|
These transports handle the serialization of MCP messages into Nostr events and manage the communication flow.
|
||||||
|
|
||||||
@@ -60,8 +60,8 @@ These transports handle the serialization of MCP messages into Nostr events and
|
|||||||
|
|
||||||
To simplify integration with existing MCP applications, the SDK provides two high-level bridging components:
|
To simplify integration with existing MCP applications, the SDK provides two high-level bridging components:
|
||||||
|
|
||||||
- [`NostrMCPProxy`](/contextvm-docs/proxy/overview): A client-side bridge that allows an MCP client to communicate with a remote MCP server over Nostr without requiring native Nostr support in the client.
|
- [`NostrMCPProxy`](/contextvm-docs/proxy/overview): A client-side bridge that allows an MCP client to communicate with a remote MCP server over Nostr without requiring native Nostr support in the client.
|
||||||
- [`NostrMCPGateway`](/contextvm-docs/gateway/overview): A server-side bridge that exposes an existing MCP server to the Nostr network, allowing it to be discovered and used by Nostr-native clients.
|
- [`NostrMCPGateway`](/contextvm-docs/gateway/overview): A server-side bridge that exposes an existing MCP server to the Nostr network, allowing it to be discovered and used by Nostr-native clients.
|
||||||
|
|
||||||
These components abstract away the underlying transport complexities, making it easy to connect conventional MCP setups with the decentralized Nostr ecosystem.
|
These components abstract away the underlying transport complexities, making it easy to connect conventional MCP setups with the decentralized Nostr ecosystem.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: ContextVM Documentation
|
title: ContextVM Documentation
|
||||||
description: A virtual machine for context-controlled computation using Nostr protocol
|
description: ContextVM - The intersection of Nostr and MCP
|
||||||
template: splash
|
template: splash
|
||||||
hero:
|
hero:
|
||||||
tagline: ContextVM - The context-controlled virtual machine built on Nostr
|
tagline: ContextVM - The intersection of Nostr and MCP
|
||||||
image:
|
image:
|
||||||
file: ../../assets/contextvm-logo.svg
|
file: ../../assets/contextvm-logo.svg
|
||||||
actions:
|
actions:
|
||||||
@@ -16,18 +16,18 @@ hero:
|
|||||||
variant: minimal
|
variant: minimal
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
import { Card, CardGrid } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
## Core Concepts
|
## Core Concepts
|
||||||
|
|
||||||
<CardGrid stagger>
|
<CardGrid stagger>
|
||||||
<Card title="Architecture" icon="document">
|
<Card title="Architecture" icon="document">
|
||||||
Learn the foundational concepts and architecture of ContextVM
|
Learn the foundational concepts and architecture of ContextVM
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="SDK Documentation" icon="code">
|
<Card title="SDK Documentation" icon="code">
|
||||||
Explore our comprehensive documentation and implementation examples
|
Explore our comprehensive documentation and implementation examples
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Integrations" icon="puzzle">
|
<Card title="Integrations" icon="puzzle">
|
||||||
Discover how ContextVM integrates MCP with Nostr
|
Discover how ContextVM integrates MCP with Nostr
|
||||||
</Card>
|
</Card>
|
||||||
</CardGrid>
|
</CardGrid>
|
||||||
@@ -20,9 +20,9 @@ The proxy sits in the middle, seamlessly forwarding messages between these two t
|
|||||||
|
|
||||||
The `NostrMCPProxy` is particularly useful in the following scenarios:
|
The `NostrMCPProxy` is particularly useful in the following scenarios:
|
||||||
|
|
||||||
- **Integrating with Existing Clients**: If you have an existing MCP client that does not have native Nostr support, you can use the proxy to enable it to communicate with Nostr-based MCP servers without modifying the client's code. The client simply connects to the proxy's local transport.
|
- **Integrating with Existing Clients**: If you have an existing MCP client that does not have native Nostr support, you can use the proxy to enable it to communicate with Nostr-based MCP servers without modifying the client's code. The client simply connects to the proxy's local transport.
|
||||||
- **Simplifying Client-Side Logic**: The proxy abstracts away all the complexities of Nostr communication (signing, relay management, encryption), allowing your main client application to remain simple and focused on its core tasks.
|
- **Simplifying Client-Side Logic**: The proxy abstracts away all the complexities of Nostr communication (signing, relay management, encryption), allowing your main client application to remain simple and focused on its core tasks.
|
||||||
- **Local Development and Testing**: The proxy can be a valuable tool for local development, allowing you to easily test a client against a remote Nostr server.
|
- **Local Development and Testing**: The proxy can be a valuable tool for local development, allowing you to easily test a client against a remote Nostr server.
|
||||||
|
|
||||||
## `NostrMCPProxyOptions`
|
## `NostrMCPProxyOptions`
|
||||||
|
|
||||||
@@ -35,23 +35,23 @@ export interface NostrMCPProxyOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`mcpHostTransport`**: An instance of a server-side MCP transport that the local client will connect to. For example, `new StdioServerTransport()`.
|
- **`mcpHostTransport`**: An instance of a server-side MCP transport that the local client will connect to. For example, `new StdioServerTransport()`.
|
||||||
- **`nostrTransportOptions`**: The full configuration object required by the `NostrClientTransport`. This includes the `signer`, `relayHandler`, and the remote `serverPubkey`.
|
- **`nostrTransportOptions`**: The full configuration object required by the `NostrClientTransport`. This includes the `signer`, `relayHandler`, and the remote `serverPubkey`.
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
This example demonstrates how to create a proxy that listens for a local client over standard I/O and connects to a remote server over Nostr.
|
This example demonstrates how to create a proxy that listens for a local client over standard I/O and connects to a remote server over Nostr.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/stdio';
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/stdio";
|
||||||
import { NostrMCPProxy } from '@ctxvm/sdk/proxy';
|
import { NostrMCPProxy } from "@ctxvm/sdk/proxy";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
|
|
||||||
// 1. Configure the signer and relay handler for the Nostr connection
|
// 1. Configure the signer and relay handler for the Nostr connection
|
||||||
const signer = new PrivateKeySigner('your-private-key');
|
const signer = new PrivateKeySigner("your-private-key");
|
||||||
const relayPool = new SimpleRelayPool(['wss://relay.damus.io']);
|
const relayPool = new SimpleRelayPool(["wss://relay.damus.io"]);
|
||||||
const REMOTE_SERVER_PUBKEY = 'remote-server-public-key';
|
const REMOTE_SERVER_PUBKEY = "remote-server-public-key";
|
||||||
|
|
||||||
// 2. Configure the transport for the local client
|
// 2. Configure the transport for the local client
|
||||||
// In this case, a stdio transport that the local client can connect to
|
// In this case, a stdio transport that the local client can connect to
|
||||||
@@ -70,7 +70,7 @@ const proxy = new NostrMCPProxy({
|
|||||||
// 4. Start the proxy
|
// 4. Start the proxy
|
||||||
await proxy.start();
|
await proxy.start();
|
||||||
|
|
||||||
console.log('Proxy is running. Connect your local MCP client.');
|
console.log("Proxy is running. Connect your local MCP client.");
|
||||||
|
|
||||||
// To stop the proxy: await proxy.stop();
|
// To stop the proxy: await proxy.stop();
|
||||||
```
|
```
|
||||||
@@ -81,4 +81,4 @@ In this setup, a separate MCP client process could connect to this proxy's `Stdi
|
|||||||
|
|
||||||
Next, we'll look at the server-side equivalent of the proxy:
|
Next, we'll look at the server-side equivalent of the proxy:
|
||||||
|
|
||||||
- **[Gateway](/contextvm-docs/gateway/overview)**
|
- **[Gateway](/contextvm-docs/gateway/overview)**
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ The `@contextvm/sdk`'s-pluggable architecture, centered around the [`RelayHandle
|
|||||||
|
|
||||||
You might want to create a custom `RelayHandler` for several reasons:
|
You might want to create a custom `RelayHandler` for several reasons:
|
||||||
|
|
||||||
- **Intelligent Relay Selection**: To dynamically select relays based on performance, reliability, or the specific type of data being requested. For example, you might use a different set of relays for fetching user metadata versus broadcasting messages.
|
- **Intelligent Relay Selection**: To dynamically select relays based on performance, reliability, or the specific type of data being requested. For example, you might use a different set of relays for fetching user metadata versus broadcasting messages.
|
||||||
- **Auth Relays**: To integrate with auth relays that require authentication or specific connection logic.
|
- **Auth Relays**: To integrate with auth relays that require authentication or specific connection logic.
|
||||||
- **Dynamic Relay Discovery**: To discover and connect to new relays at runtime, rather than using a static list.
|
- **Dynamic Relay Discovery**: To discover and connect to new relays at runtime, rather than using a static list.
|
||||||
- **Custom Caching**: To implement a custom caching layer to reduce redundant requests to relays.
|
- **Custom Caching**: To implement a custom caching layer to reduce redundant requests to relays.
|
||||||
- **Resiliency and-failover**: To build more robust-failover logic, such as automatically retrying failed connections or switching to backup relays.
|
- **Resiliency and-failover**: To build more robust-failover logic, such as automatically retrying failed connections or switching to backup relays.
|
||||||
|
|
||||||
## Implementing the `RelayHandler` Interface
|
## Implementing the `RelayHandler` Interface
|
||||||
|
|
||||||
@@ -26,28 +26,30 @@ To create a custom relay handler, you need to create a class that implements the
|
|||||||
Here is a simple example of a custom `RelayHandler` that wraps the default `SimpleRelayPool` and adds logging to each operation. This illustrates how you can extend or compose existing handlers.
|
Here is a simple example of a custom `RelayHandler` that wraps the default `SimpleRelayPool` and adds logging to each operation. This illustrates how you can extend or compose existing handlers.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { RelayHandler } from '@ctxvm/sdk/core';
|
import { RelayHandler } from "@ctxvm/sdk/core";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
import { Filter, NostrEvent } from 'nostr-tools';
|
import { Filter, NostrEvent } from "nostr-tools";
|
||||||
|
|
||||||
class LoggingRelayHandler implements RelayHandler {
|
class LoggingRelayHandler implements RelayHandler {
|
||||||
private readonly innerHandler: RelayHandler;
|
private readonly innerHandler: RelayHandler;
|
||||||
|
|
||||||
constructor(relayUrls: string[]) {
|
constructor(relayUrls: string[]) {
|
||||||
this.innerHandler = new SimpleRelayPool(relayUrls);
|
this.innerHandler = new SimpleRelayPool(relayUrls);
|
||||||
console.log(`[LoggingRelayHandler] Initialized with relays: ${relayUrls.join(', ')}`);
|
console.log(
|
||||||
|
`[LoggingRelayHandler] Initialized with relays: ${relayUrls.join(", ")}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(): Promise<void> {
|
async connect(): Promise<void> {
|
||||||
console.log('[LoggingRelayHandler] Attempting to connect...');
|
console.log("[LoggingRelayHandler] Attempting to connect...");
|
||||||
await this.innerHandler.connect();
|
await this.innerHandler.connect();
|
||||||
console.log('[LoggingRelayHandler] Connected successfully.');
|
console.log("[LoggingRelayHandler] Connected successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnect(): Promise<void> {
|
async disconnect(): Promise<void> {
|
||||||
console.log('[LoggingRelayHandler] Disconnecting...');
|
console.log("[LoggingRelayHandler] Disconnecting...");
|
||||||
await this.innerHandler.disconnect();
|
await this.innerHandler.disconnect();
|
||||||
console.log('[LoggingRelayHandler] Disconnected.');
|
console.log("[LoggingRelayHandler] Disconnected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
publish(event: NostrEvent): void {
|
publish(event: NostrEvent): void {
|
||||||
@@ -64,13 +66,13 @@ class LoggingRelayHandler implements RelayHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(): void {
|
unsubscribe(): void {
|
||||||
console.log('[LoggingRelayHandler] Unsubscribing from all.');
|
console.log("[LoggingRelayHandler] Unsubscribing from all.");
|
||||||
this.innerHandler.unsubscribe();
|
this.innerHandler.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage
|
// Usage
|
||||||
const loggingHandler = new LoggingRelayHandler(['wss://relay.damus.io']);
|
const loggingHandler = new LoggingRelayHandler(["wss://relay.damus.io"]);
|
||||||
|
|
||||||
const transport = new NostrClientTransport({
|
const transport = new NostrClientTransport({
|
||||||
relayHandler: loggingHandler,
|
relayHandler: loggingHandler,
|
||||||
@@ -88,5 +90,5 @@ Once your custom handler class is created, you can instantiate it and pass it to
|
|||||||
|
|
||||||
With the `Relay` component covered, we will now look at the high-level bridging components provided by the SDK.
|
With the `Relay` component covered, we will now look at the high-level bridging components provided by the SDK.
|
||||||
|
|
||||||
- **[Proxy](/contextvm-docs/proxy/overview)**
|
- **[Proxy](/contextvm-docs/proxy/overview)**
|
||||||
- **[Gateway](/contextvm-docs/gateway/overview)**
|
- **[Gateway](/contextvm-docs/gateway/overview)**
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ The `RelayHandler` interface is another crucial abstraction in the `@contextvm/s
|
|||||||
|
|
||||||
The `RelayHandler`'s purpose is to abstract the complexities of relay management, including:
|
The `RelayHandler`'s purpose is to abstract the complexities of relay management, including:
|
||||||
|
|
||||||
- Connecting and disconnecting from a set of relays.
|
- Connecting and disconnecting from a set of relays.
|
||||||
- Subscribing to events with specific filters.
|
- Subscribing to events with specific filters.
|
||||||
- Publishing events to the network.
|
- Publishing events to the network.
|
||||||
- Handling relay-specific logic, such as connection retries, timeouts, and relay selection.
|
- Handling relay-specific logic, such as connection retries, timeouts, and relay selection.
|
||||||
|
|
||||||
By depending on this interface, the SDK's transports can remain agnostic about the specific relay management strategy being used. This allows developers to plug in different relay handlers to suit their needs.
|
By depending on this interface, the SDK's transports can remain agnostic about the specific relay management strategy being used. This allows developers to plug in different relay handlers to suit their needs.
|
||||||
|
|
||||||
@@ -32,20 +32,20 @@ export interface RelayHandler {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `connect()`: Establishes connections to the configured relays.
|
- `connect()`: Establishes connections to the configured relays.
|
||||||
- `disconnect()`: Closes connections to all relays.
|
- `disconnect()`: Closes connections to all relays.
|
||||||
- `subscribe(filters, onEvent)`: Creates a subscription on the connected relays, listening for events that match the provided filters and passing them to the `onEvent` callback.
|
- `subscribe(filters, onEvent)`: Creates a subscription on the connected relays, listening for events that match the provided filters and passing them to the `onEvent` callback.
|
||||||
- `unsubscribe()`: Closes all active subscriptions.
|
- `unsubscribe()`: Closes all active subscriptions.
|
||||||
- `publish(event)`: Publishes a Nostr event to the connected relays.
|
- `publish(event)`: Publishes a Nostr event to the connected relays.
|
||||||
|
|
||||||
## Implementations
|
## Implementations
|
||||||
|
|
||||||
The SDK provides a default implementation for common use cases and allows for custom implementations for advanced scenarios.
|
The SDK provides a default implementation for common use cases and allows for custom implementations for advanced scenarios.
|
||||||
|
|
||||||
- **[SimpleRelayPool](/contextvm-docs/relay/simple-relay-pool)**: The default implementation, which manages a pool of relays and handles connection and subscription logic.
|
- **[SimpleRelayPool](/contextvm-docs/relay/simple-relay-pool)**: The default implementation, which manages a pool of relays and handles connection and subscription logic.
|
||||||
- **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**: For creating custom relay handlers that integrate with specific relay management systems, such as auth relays or custom caching.
|
- **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**: For creating custom relay handlers that integrate with specific relay management systems, such as auth relays or custom caching.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Learn about the default implementation: **[SimpleRelayPool](/contextvm-docs/relay/simple-relay-pool)**
|
- Learn about the default implementation: **[SimpleRelayPool](/contextvm-docs/relay/simple-relay-pool)**
|
||||||
- Learn how to create your own: **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**
|
- Learn how to create your own: **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ The `SimpleRelayPool` is the default implementation of the [`RelayHandler`](/con
|
|||||||
|
|
||||||
The `SimpleRelayPool` provides a straightforward way to manage relay connections for most common use cases. Its responsibilities include:
|
The `SimpleRelayPool` provides a straightforward way to manage relay connections for most common use cases. Its responsibilities include:
|
||||||
|
|
||||||
- Connecting to a predefined list of Nostr relays.
|
- Connecting to a predefined list of Nostr relays.
|
||||||
- Publishing events to all relays in the pool.
|
- Publishing events to all relays in the pool.
|
||||||
- Subscribing to events from all relays in the pool.
|
- Subscribing to events from all relays in the pool.
|
||||||
- Managing the lifecycle of connections and subscriptions.
|
- Managing the lifecycle of connections and subscriptions.
|
||||||
|
|
||||||
It is a simple but effective solution for applications that need to interact with a static set of relays.
|
It is a simple but effective solution for applications that need to interact with a static set of relays.
|
||||||
|
|
||||||
@@ -22,19 +22,19 @@ It is a simple but effective solution for applications that need to interact wit
|
|||||||
|
|
||||||
The constructor takes a single argument:
|
The constructor takes a single argument:
|
||||||
|
|
||||||
- **`relayUrls`**: An array of strings, where each string is the URL of a Nostr relay (e.g., `wss://relay.damus.io`).
|
- **`relayUrls`**: An array of strings, where each string is the URL of a Nostr relay (e.g., `wss://relay.damus.io`).
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
import { NostrClientTransport } from '@ctxvm/sdk/transport';
|
import { NostrClientTransport } from "@ctxvm/sdk/transport";
|
||||||
|
|
||||||
// 1. Define the list of relays you want to connect to
|
// 1. Define the list of relays you want to connect to
|
||||||
const myRelays = [
|
const myRelays = [
|
||||||
'wss://relay.damus.io',
|
"wss://relay.damus.io",
|
||||||
'wss://relay.primal.net',
|
"wss://relay.primal.net",
|
||||||
'wss://nos.lol',
|
"wss://nos.lol",
|
||||||
];
|
];
|
||||||
|
|
||||||
// 2. Create an instance of the SimpleRelayPool
|
// 2. Create an instance of the SimpleRelayPool
|
||||||
@@ -51,11 +51,11 @@ const transport = new NostrClientTransport({
|
|||||||
|
|
||||||
The `SimpleRelayPool` wraps the `SimplePool` from `nostr-tools` and implements the methods of the `RelayHandler` interface:
|
The `SimpleRelayPool` wraps the `SimplePool` from `nostr-tools` and implements the methods of the `RelayHandler` interface:
|
||||||
|
|
||||||
- **`connect()`**: Iterates through the provided `relayUrls` and calls `pool.ensureRelay()` for each one, which establishes a connection if one doesn't already exist.
|
- **`connect()`**: Iterates through the provided `relayUrls` and calls `pool.ensureRelay()` for each one, which establishes a connection if one doesn't already exist.
|
||||||
- **`disconnect()`**: Closes the connections to the specified relays.
|
- **`disconnect()`**: Closes the connections to the specified relays.
|
||||||
- **`publish(event)`**: Publishes the given event to all relays in the pool by calling `pool.publish()`.
|
- **`publish(event)`**: Publishes the given event to all relays in the pool by calling `pool.publish()`.
|
||||||
- **`subscribe(filters, onEvent)`**: Creates a subscription on all relays in the pool using `pool.subscribeMany()`. It tracks all active subscriptions so they can be closed later.
|
- **`subscribe(filters, onEvent)`**: Creates a subscription on all relays in the pool using `pool.subscribeMany()`. It tracks all active subscriptions so they can be closed later.
|
||||||
- **`unsubscribe()`**: Closes all active subscriptions that were created through the `subscribe` method.
|
- **`unsubscribe()`**: Closes all active subscriptions that were created through the `subscribe` method.
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
@@ -65,4 +65,4 @@ For applications that require more sophisticated relay management, you may want
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Learn how to build a custom relay handler: **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**
|
- Learn how to build a custom relay handler: **[Custom Relay Handler](/contextvm-docs/relay/custom-relay-handler)**
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ One of the key design features of the `@contextvm/sdk` is its modularity, which
|
|||||||
|
|
||||||
While the [`PrivateKeySigner`](/contextvm-docs/signer/private-key-signer) is a common choice for most applications, there are cases where you may need to use a different approach:
|
While the [`PrivateKeySigner`](/contextvm-docs/signer/private-key-signer) is a common choice for most applications, there are cases where you may need to use a different approach:
|
||||||
|
|
||||||
- **Security is paramount**: You need to keep private keys isolated from the main application logic, for example, in a hardware security module (HSM) or a secure enclave.
|
- **Security is paramount**: You need to keep private keys isolated from the main application logic, for example, in a hardware security module (HSM) or a secure enclave.
|
||||||
- **Interacting with external wallets**: Your application needs to request signatures from a user's wallet, such as a browser extension (e.g., Alby, Noster) or a mobile wallet.
|
- **Interacting with external wallets**: Your application needs to request signatures from a user's wallet, such as a browser extension (e.g., Alby, Noster) or a mobile wallet.
|
||||||
- **Complex key management**: Your application uses a more complex key management architecture that doesn't involve direct access to raw private keys.
|
- **Complex key management**: Your application uses a more complex key management architecture that doesn't involve direct access to raw private keys.
|
||||||
|
|
||||||
## Implementing the `NostrSigner` Interface
|
## Implementing the `NostrSigner` Interface
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ A common use case for a custom signer is in a web application that needs to inte
|
|||||||
Here is how you could implement a `NostrSigner` that wraps the `window.nostr` object:
|
Here is how you could implement a `NostrSigner` that wraps the `window.nostr` object:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { NostrSigner } from '@ctxvm/sdk/core';
|
import { NostrSigner } from "@ctxvm/sdk/core";
|
||||||
import { UnsignedEvent, NostrEvent } from 'nostr-tools';
|
import { UnsignedEvent, NostrEvent } from "nostr-tools";
|
||||||
|
|
||||||
// Define the NIP-07 window.nostr interface for type-safety
|
// Define the NIP-07 window.nostr interface for type-safety
|
||||||
declare global {
|
declare global {
|
||||||
@@ -46,31 +46,31 @@ declare global {
|
|||||||
class Nip07Signer implements NostrSigner {
|
class Nip07Signer implements NostrSigner {
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!window.nostr) {
|
if (!window.nostr) {
|
||||||
throw new Error('NIP-07 compatible browser extension not found.');
|
throw new Error("NIP-07 compatible browser extension not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPublicKey(): Promise<string> {
|
async getPublicKey(): Promise<string> {
|
||||||
if (!window.nostr) throw new Error('window.nostr not found.');
|
if (!window.nostr) throw new Error("window.nostr not found.");
|
||||||
return await window.nostr.getPublicKey();
|
return await window.nostr.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
async signEvent(event: UnsignedEvent): Promise<NostrEvent> {
|
async signEvent(event: UnsignedEvent): Promise<NostrEvent> {
|
||||||
if (!window.nostr) throw new Error('window.nostr not found.');
|
if (!window.nostr) throw new Error("window.nostr not found.");
|
||||||
return await window.nostr.signEvent(event);
|
return await window.nostr.signEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
nip44 = {
|
nip44 = {
|
||||||
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
||||||
if (!window.nostr?.nip44) {
|
if (!window.nostr?.nip44) {
|
||||||
throw new Error('The extension does not support NIP-44 encryption.');
|
throw new Error("The extension does not support NIP-44 encryption.");
|
||||||
}
|
}
|
||||||
return await window.nostr.nip44.encrypt(pubkey, plaintext);
|
return await window.nostr.nip44.encrypt(pubkey, plaintext);
|
||||||
},
|
},
|
||||||
|
|
||||||
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
||||||
if (!window.nostr?.nip44) {
|
if (!window.nostr?.nip44) {
|
||||||
throw new Error('The extension does not support NIP-44 decryption.');
|
throw new Error("The extension does not support NIP-44 decryption.");
|
||||||
}
|
}
|
||||||
return await window.nostr.nip44.decrypt(pubkey, ciphertext);
|
return await window.nostr.nip44.decrypt(pubkey, ciphertext);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ The primary purpose of the `NostrSigner` is to abstract the process of event sig
|
|||||||
|
|
||||||
This design offers several key benefits:
|
This design offers several key benefits:
|
||||||
|
|
||||||
- **Security**: Private keys can be managed in secure environments (e.g., web extensions, hardware wallets, dedicated signing services) without exposing them to the application logic.
|
- **Security**: Private keys can be managed in secure environments (e.g., web extensions, hardware wallets, dedicated signing services) without exposing them to the application logic.
|
||||||
- **Flexibility**: Developers can easily swap out the default signer with a custom implementation that meets their specific needs.
|
- **Flexibility**: Developers can easily swap out the default signer with a custom implementation that meets their specific needs.
|
||||||
- **Modularity**: The signing logic is decoupled from the communication logic, leading to a cleaner, more maintainable codebase.
|
- **Modularity**: The signing logic is decoupled from the communication logic, leading to a cleaner, more maintainable codebase.
|
||||||
|
|
||||||
## Interface Definition
|
## Interface Definition
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ export interface NostrSigner {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `getPublicKey()`: Asynchronously returns the public key corresponding to the signer's private key.
|
- `getPublicKey()`: Asynchronously returns the public key corresponding to the signer's private key.
|
||||||
- `signEvent(event)`: Takes an unsigned Nostr event, signs it, and returns the signature.
|
- `signEvent(event)`: Takes an unsigned Nostr event, signs it, and returns the signature.
|
||||||
- `nip04`: (Deprecated) Provides NIP-04 encryption support.
|
- `nip04`: (Deprecated) Provides NIP-04 encryption support.
|
||||||
- `nip44`: Provides NIP-44 encryption support.
|
- `nip44`: Provides NIP-44 encryption support.
|
||||||
|
|
||||||
Any class that implements this interface can be used as a signer throughout the SDK.
|
Any class that implements this interface can be used as a signer throughout the SDK.
|
||||||
|
|
||||||
@@ -51,10 +51,10 @@ Any class that implements this interface can be used as a signer throughout the
|
|||||||
|
|
||||||
The SDK provides a default implementation for common use cases and allows for custom implementations for advanced scenarios.
|
The SDK provides a default implementation for common use cases and allows for custom implementations for advanced scenarios.
|
||||||
|
|
||||||
- **[PrivateKeySigner](/contextvm-docs/signer/private-key-signer)**: The default implementation, which takes a raw private key string and performs signing operations locally.
|
- **[PrivateKeySigner](/contextvm-docs/signer/private-key-signer)**: The default implementation, which takes a raw private key string and performs signing operations locally.
|
||||||
- **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**: For creating custom signers that integrate with key management systems, such as hardware wallets or remote signing services.
|
- **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**: For creating custom signers that integrate with key management systems, such as hardware wallets or remote signing services.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Learn about the default implementation: **[PrivateKeySigner](/contextvm-docs/signer/private-key-signer)**
|
- Learn about the default implementation: **[PrivateKeySigner](/contextvm-docs/signer/private-key-signer)**
|
||||||
- Learn how to create your own: **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**
|
- Learn how to create your own: **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**
|
||||||
|
|||||||
@@ -11,25 +11,25 @@ The `PrivateKeySigner` is the default implementation of the [`NostrSigner`](/con
|
|||||||
|
|
||||||
The `PrivateKeySigner` is designed for scenarios where the private key is readily available in the application's environment. It handles all the necessary cryptographic operations locally, including:
|
The `PrivateKeySigner` is designed for scenarios where the private key is readily available in the application's environment. It handles all the necessary cryptographic operations locally, including:
|
||||||
|
|
||||||
- Deriving the corresponding public key.
|
- Deriving the corresponding public key.
|
||||||
- Signing Nostr events.
|
- Signing Nostr events.
|
||||||
- Encrypting and decrypting messages using NIP-44.
|
- Encrypting and decrypting messages using NIP-44.
|
||||||
|
|
||||||
## `constructor(privateKey: string)`
|
## `constructor(privateKey: string)`
|
||||||
|
|
||||||
The constructor takes a single argument:
|
The constructor takes a single argument:
|
||||||
|
|
||||||
- **`privateKey`**: A hexadecimal string representing the 32-byte Nostr private key.
|
- **`privateKey`**: A hexadecimal string representing the 32-byte Nostr private key.
|
||||||
|
|
||||||
When instantiated, the `PrivateKeySigner` will immediately convert the hex string into a `Uint8Array` and derive the public key, which is then cached for future calls to `getPublicKey()`.
|
When instantiated, the `PrivateKeySigner` will immediately convert the hex string into a `Uint8Array` and derive the public key, which is then cached for future calls to `getPublicKey()`.
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
|
|
||||||
// Replace with a securely stored private key
|
// Replace with a securely stored private key
|
||||||
const privateKeyHex = 'your-32-byte-private-key-in-hex';
|
const privateKeyHex = "your-32-byte-private-key-in-hex";
|
||||||
|
|
||||||
const signer = new PrivateKeySigner(privateKeyHex);
|
const signer = new PrivateKeySigner(privateKeyHex);
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ Takes an unsigned Nostr event, signs it using the private key, and returns a pro
|
|||||||
|
|
||||||
The `PrivateKeySigner` also provides a `nip44` object that implements NIP-44 encryption and decryption. This is used internally by the transports when encryption is enabled, and it allows the `decryptMessage` function to work seamlessly with this signer.
|
The `PrivateKeySigner` also provides a `nip44` object that implements NIP-44 encryption and decryption. This is used internally by the transports when encryption is enabled, and it allows the `decryptMessage` function to work seamlessly with this signer.
|
||||||
|
|
||||||
- `encrypt(pubkey, plaintext)`: Encrypts a plaintext message for a given recipient public key.
|
- `encrypt(pubkey, plaintext)`: Encrypts a plaintext message for a given recipient public key.
|
||||||
- `decrypt(pubkey, ciphertext)`: Decrypts a ciphertext message received from a given sender public key.
|
- `decrypt(pubkey, ciphertext)`: Decrypts a ciphertext message received from a given sender public key.
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
@@ -67,4 +67,4 @@ For applications requiring a higher level of security, consider creating a custo
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Learn how to build a custom signer: **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**
|
- Learn how to build a custom signer: **[Custom Signer Development](/contextvm-docs/signer/custom-signer-development)**
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ The `BaseNostrTransport` is an abstract class that provides the core functionali
|
|||||||
|
|
||||||
The `BaseNostrTransport` is responsible for:
|
The `BaseNostrTransport` is responsible for:
|
||||||
|
|
||||||
- **Connection Management**: Establishing and terminating connections to the Nostr relay network via a `RelayHandler`.
|
- **Connection Management**: Establishing and terminating connections to the Nostr relay network via a `RelayHandler`.
|
||||||
- **Event Serialization**: Converting MCP JSON-RPC messages into Nostr events and vice-versa.
|
- **Event Serialization**: Converting MCP JSON-RPC messages into Nostr events and vice-versa.
|
||||||
- **Cryptographic Operations**: Signing Nostr events using a `NostrSigner`.
|
- **Cryptographic Operations**: Signing Nostr events using a `NostrSigner`.
|
||||||
- **Message Publishing**: Publishing events to the Nostr network.
|
- **Message Publishing**: Publishing events to the Nostr network.
|
||||||
- **Subscription Management**: Creating and managing subscriptions to listen for relevant events.
|
- **Subscription Management**: Creating and managing subscriptions to listen for relevant events.
|
||||||
- **Encryption Handling**: Managing the encryption and decryption of messages based on the configured `EncryptionMode`.
|
- **Encryption Handling**: Managing the encryption and decryption of messages based on the configured `EncryptionMode`.
|
||||||
|
|
||||||
## `BaseNostrTransportOptions`
|
## `BaseNostrTransportOptions`
|
||||||
|
|
||||||
@@ -30,19 +30,19 @@ export interface BaseNostrTransportOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`signer`**: An instance of a `NostrSigner` for signing events. This is a required parameter.
|
- **`signer`**: An instance of a `NostrSigner` for signing events. This is a required parameter.
|
||||||
- **`relayHandler`**: An instance of a `RelayHandler` for managing relay connections. This is a required parameter.
|
- **`relayHandler`**: An instance of a `RelayHandler` for managing relay connections. This is a required parameter.
|
||||||
- **`encryptionMode`**: An optional `EncryptionMode` enum that determines the encryption policy for the transport. Defaults to `OPTIONAL`.
|
- **`encryptionMode`**: An optional `EncryptionMode` enum that determines the encryption policy for the transport. Defaults to `OPTIONAL`.
|
||||||
|
|
||||||
## Key Methods
|
## Key Methods
|
||||||
|
|
||||||
The `BaseNostrTransport` provides several protected methods that are used by its subclasses to implement their specific logic:
|
The `BaseNostrTransport` provides several protected methods that are used by its subclasses to implement their specific logic:
|
||||||
|
|
||||||
- `connect()`: Connects to the configured Nostr relays.
|
- `connect()`: Connects to the configured Nostr relays.
|
||||||
- `disconnect()`: Disconnects from the relays and clears subscriptions.
|
- `disconnect()`: Disconnects from the relays and clears subscriptions.
|
||||||
- `subscribe(filters, onEvent)`: Subscribes to Nostr events that match the given filters.
|
- `subscribe(filters, onEvent)`: Subscribes to Nostr events that match the given filters.
|
||||||
- `sendMcpMessage(...)`: Converts an MCP message to a Nostr event, signs it, optionally encrypts it, and publishes it to the network.
|
- `sendMcpMessage(...)`: Converts an MCP message to a Nostr event, signs it, optionally encrypts it, and publishes it to the network.
|
||||||
- `createSubscriptionFilters(...)`: A helper method to create standard filters for listening to messages directed at a specific public key.
|
- `createSubscriptionFilters(...)`: A helper method to create standard filters for listening to messages directed at a specific public key.
|
||||||
|
|
||||||
## How It Fits Together
|
## How It Fits Together
|
||||||
|
|
||||||
@@ -52,5 +52,5 @@ The `BaseNostrTransport` encapsulates the shared logic of Nostr communication, a
|
|||||||
|
|
||||||
Now that you understand the foundation of the Nostr transports, let's explore the concrete implementations:
|
Now that you understand the foundation of the Nostr transports, let's explore the concrete implementations:
|
||||||
|
|
||||||
- **[Nostr Client Transport](/contextvm-docs/transports/nostr-client-transport)**: For building MCP clients that communicate over Nostr.
|
- **[Nostr Client Transport](/contextvm-docs/transports/nostr-client-transport)**: For building MCP clients that communicate over Nostr.
|
||||||
- **[Nostr Server Transport](/contextvm-docs/transports/nostr-server-transport)**: For exposing MCP servers to the Nostr network.
|
- **[Nostr Server Transport](/contextvm-docs/transports/nostr-server-transport)**: For exposing MCP servers to the Nostr network.
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ The `NostrClientTransport` is a key component of the `@contextvm/sdk`, enabling
|
|||||||
|
|
||||||
The `NostrClientTransport` handles all the complexities of Nostr-based communication, including:
|
The `NostrClientTransport` handles all the complexities of Nostr-based communication, including:
|
||||||
|
|
||||||
- Connecting to Nostr relays.
|
- Connecting to Nostr relays.
|
||||||
- Subscribing to events from a specific server.
|
- Subscribing to events from a specific server.
|
||||||
- Sending MCP requests as Nostr events.
|
- Sending MCP requests as Nostr events.
|
||||||
- Receiving and processing responses and notifications.
|
- Receiving and processing responses and notifications.
|
||||||
- Handling encryption and decryption of messages.
|
- Handling encryption and decryption of messages.
|
||||||
|
|
||||||
By using this transport, an MCP client can interact with a Nostr-enabled MCP server without needing to implement any Nostr-specific logic itself.
|
By using this transport, an MCP client can interact with a Nostr-enabled MCP server without needing to implement any Nostr-specific logic itself.
|
||||||
|
|
||||||
@@ -29,25 +29,25 @@ export interface NostrTransportOptions extends BaseNostrTransportOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`serverPubkey`**: The public key of the target MCP server. The transport will only listen for events from this public key.
|
- **`serverPubkey`**: The public key of the target MCP server. The transport will only listen for events from this public key.
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
Here's how you can use the `NostrClientTransport` with an MCP client from the `@modelcontextprotocol/sdk`:
|
Here's how you can use the `NostrClientTransport` with an MCP client from the `@modelcontextprotocol/sdk`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client';
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||||
import { NostrClientTransport } from '@ctxvm/sdk/transport';
|
import { NostrClientTransport } from "@ctxvm/sdk/transport";
|
||||||
import { EncryptionMode } from '@ctxvm/sdk/core';
|
import { EncryptionMode } from "@ctxvm/sdk/core";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
|
|
||||||
// 1. Configure the signer and relay handler
|
// 1. Configure the signer and relay handler
|
||||||
const signer = new PrivateKeySigner('your-private-key'); // Replace with your actual private key
|
const signer = new PrivateKeySigner("your-private-key"); // Replace with your actual private key
|
||||||
const relayPool = new SimpleRelayPool(['wss://relay.damus.io']);
|
const relayPool = new SimpleRelayPool(["wss://relay.damus.io"]);
|
||||||
|
|
||||||
// 2. Set the public key of the target server
|
// 2. Set the public key of the target server
|
||||||
const REMOTE_SERVER_PUBKEY = 'remote-server-public-key';
|
const REMOTE_SERVER_PUBKEY = "remote-server-public-key";
|
||||||
|
|
||||||
// 3. Create the transport instance
|
// 3. Create the transport instance
|
||||||
const clientNostrTransport = new NostrClientTransport({
|
const clientNostrTransport = new NostrClientTransport({
|
||||||
@@ -63,7 +63,7 @@ await mcpClient.connect(clientNostrTransport);
|
|||||||
|
|
||||||
// 5. Use the client to interact with the server
|
// 5. Use the client to interact with the server
|
||||||
const tools = await mcpClient.listTools();
|
const tools = await mcpClient.listTools();
|
||||||
console.log('Available tools:', tools);
|
console.log("Available tools:", tools);
|
||||||
|
|
||||||
// 6. Close the connection when done
|
// 6. Close the connection when done
|
||||||
// await mcpClient.close();
|
// await mcpClient.close();
|
||||||
@@ -73,16 +73,16 @@ console.log('Available tools:', tools);
|
|||||||
|
|
||||||
1. **`start()`**: When `mcpClient.connect()` is called, it internally calls the transport's `start()` method. This method connects to the relays and subscribes to events targeting the client's public key.
|
1. **`start()`**: When `mcpClient.connect()` is called, it internally calls the transport's `start()` method. This method connects to the relays and subscribes to events targeting the client's public key.
|
||||||
2. **`send(message)`**: When you call an MCP method like `mcpClient.listTools()`, the client creates a JSON-RPC request and passes it to the transport's `send()` method. The transport then:
|
2. **`send(message)`**: When you call an MCP method like `mcpClient.listTools()`, the client creates a JSON-RPC request and passes it to the transport's `send()` method. The transport then:
|
||||||
- Wraps the message in a Nostr event.
|
- Wraps the message in a Nostr event.
|
||||||
- Signs the event.
|
- Signs the event.
|
||||||
- Optionally encrypts it.
|
- Optionally encrypts it.
|
||||||
- Publishes it to the relays, targeting the `serverPubkey`.
|
- Publishes it to the relays, targeting the `serverPubkey`.
|
||||||
3. **Event Processing**: The transport listens for incoming events. When an event is received, it is decrypted (if necessary) and converted back into a JSON-RPC message.
|
3. **Event Processing**: The transport listens for incoming events. When an event is received, it is decrypted (if necessary) and converted back into a JSON-RPC message.
|
||||||
- If the message is a **response** (correlated by the original event ID), it is passed to the MCP client to resolve the pending request.
|
- If the message is a **response** (correlated by the original event ID), it is passed to the MCP client to resolve the pending request.
|
||||||
- If the message is a **notification**, it is emitted through the `onmessage` handler.
|
- If the message is a **notification**, it is emitted through the `onmessage` handler.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Next, we will look at the server-side counterpart to this transport:
|
Next, we will look at the server-side counterpart to this transport:
|
||||||
|
|
||||||
- **[Nostr Server Transport](/contextvm-docs/transports/nostr-server-transport)**: For exposing MCP servers to the Nostr network.
|
- **[Nostr Server Transport](/contextvm-docs/transports/nostr-server-transport)**: For exposing MCP servers to the Nostr network.
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ The `NostrServerTransport` is the server-side counterpart to the [`NostrClientTr
|
|||||||
|
|
||||||
The `NostrServerTransport` is responsible for:
|
The `NostrServerTransport` is responsible for:
|
||||||
|
|
||||||
- Listening for incoming MCP requests from Nostr clients.
|
- Listening for incoming MCP requests from Nostr clients.
|
||||||
- Managing individual client sessions and their state (e.g., initialization, encryption).
|
- Managing individual client sessions and their state (e.g., initialization, encryption).
|
||||||
- Handling request/response correlation to ensure responses are sent to the correct client.
|
- Handling request/response correlation to ensure responses are sent to the correct client.
|
||||||
- Sending responses and notifications back to clients over Nostr.
|
- Sending responses and notifications back to clients over Nostr.
|
||||||
- Optionally announcing the server and its capabilities to the network for public discovery.
|
- Optionally announcing the server and its capabilities to the network for public discovery.
|
||||||
|
|
||||||
## `NostrServerTransportOptions`
|
## `NostrServerTransportOptions`
|
||||||
|
|
||||||
@@ -29,28 +29,28 @@ export interface NostrServerTransportOptions extends BaseNostrTransportOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`serverInfo`**: (Optional) Information about the server (`name`, `picture`, `website`) to be used in public announcements.
|
- **`serverInfo`**: (Optional) Information about the server (`name`, `picture`, `website`) to be used in public announcements.
|
||||||
- **`isPublicServer`**: (Optional) If `true`, the transport will automatically announce the server's capabilities on the Nostr network. Defaults to `false`.
|
- **`isPublicServer`**: (Optional) If `true`, the transport will automatically announce the server's capabilities on the Nostr network. Defaults to `false`.
|
||||||
- **`allowedPublicKeys`**: (Optional) A list of client public keys that are allowed to connect. If not provided, any client can connect.
|
- **`allowedPublicKeys`**: (Optional) A list of client public keys that are allowed to connect. If not provided, any client can connect.
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
Here's how to use the `NostrServerTransport` with an `McpServer` from the `@modelcontextprotocol/sdk`:
|
Here's how to use the `NostrServerTransport` with an `McpServer` from the `@modelcontextprotocol/sdk`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { McpServer } from '@modelcontextprotocol/sdk/server';
|
import { McpServer } from "@modelcontextprotocol/sdk/server";
|
||||||
import { NostrServerTransport } from '@ctxvm/sdk/transport';
|
import { NostrServerTransport } from "@ctxvm/sdk/transport";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
|
|
||||||
// 1. Configure the signer and relay pool
|
// 1. Configure the signer and relay pool
|
||||||
const signer = new PrivateKeySigner('your-server-private-key');
|
const signer = new PrivateKeySigner("your-server-private-key");
|
||||||
const relayPool = new SimpleRelayPool(['wss://relay.damus.io']);
|
const relayPool = new SimpleRelayPool(["wss://relay.damus.io"]);
|
||||||
|
|
||||||
// 2. Create the McpServer instance
|
// 2. Create the McpServer instance
|
||||||
const mcpServer = new McpServer({
|
const mcpServer = new McpServer({
|
||||||
name: 'demo-server',
|
name: "demo-server",
|
||||||
version: '1.0.0',
|
version: "1.0.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register your server's tools, resources, etc.
|
// Register your server's tools, resources, etc.
|
||||||
@@ -62,15 +62,15 @@ const serverNostrTransport = new NostrServerTransport({
|
|||||||
relayHandler: relayPool,
|
relayHandler: relayPool,
|
||||||
isPublicServer: true, // Announce the server publicly
|
isPublicServer: true, // Announce the server publicly
|
||||||
serverInfo: {
|
serverInfo: {
|
||||||
name: 'My Awesome MCP Server',
|
name: "My Awesome MCP Server",
|
||||||
website: 'https://example.com',
|
website: "https://example.com",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. Connect the server
|
// 4. Connect the server
|
||||||
await mcpServer.connect(serverNostrTransport);
|
await mcpServer.connect(serverNostrTransport);
|
||||||
|
|
||||||
console.log('MCP server is running and available on Nostr.');
|
console.log("MCP server is running and available on Nostr.");
|
||||||
|
|
||||||
// Keep the process running...
|
// Keep the process running...
|
||||||
// To shut down: await mcpServer.close();
|
// To shut down: await mcpServer.close();
|
||||||
@@ -88,10 +88,10 @@ console.log('MCP server is running and available on Nostr.');
|
|||||||
|
|
||||||
The `NostrServerTransport` manages a session for each unique client public key. Each session tracks:
|
The `NostrServerTransport` manages a session for each unique client public key. Each session tracks:
|
||||||
|
|
||||||
- If the client has completed the MCP initialization handshake.
|
- If the client has completed the MCP initialization handshake.
|
||||||
- Whether the session is encrypted.
|
- Whether the session is encrypted.
|
||||||
- A map of pending requests to correlate responses.
|
- A map of pending requests to correlate responses.
|
||||||
- The timestamp of the last activity, used for cleaning up inactive sessions.
|
- The timestamp of the last activity, used for cleaning up inactive sessions.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ We will build two separate scripts:
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- You have completed the [Quick Overview](/contextvm-docs/getting-started/quick-overview/).
|
- You have completed the [Quick Overview](/contextvm-docs/getting-started/quick-overview/).
|
||||||
- You have two Nostr private keys (one for the server, one for the client). You can generate new keys using various tools, or by running `nostr-tools` commands.
|
- You have two Nostr private keys (one for the server, one for the client). You can generate new keys using various tools, or by running `nostr-tools` commands.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -28,16 +28,17 @@ First, let's create the MCP server. This server will use the `NostrServerTranspo
|
|||||||
Create a new file named `server.ts`:
|
Create a new file named `server.ts`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { McpServer, Tool } from '@modelcontextprotocol/sdk/server';
|
import { McpServer, Tool } from "@modelcontextprotocol/sdk/server";
|
||||||
import { NostrServerTransport } from '@ctxvm/sdk/transport';
|
import { NostrServerTransport } from "@ctxvm/sdk/transport";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
import { generateSecretKey, getPublicKey } from 'nostr-tools/pure';
|
import { generateSecretKey, getPublicKey } from "nostr-tools/pure";
|
||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
// IMPORTANT: Replace with your own private key
|
// IMPORTANT: Replace with your own private key
|
||||||
const SERVER_PRIVATE_KEY_HEX = process.env.SERVER_PRIVATE_KEY || 'your-32-byte-server-private-key-in-hex';
|
const SERVER_PRIVATE_KEY_HEX =
|
||||||
const RELAYS = ['wss://relay.damus.io', 'wss://nos.lol'];
|
process.env.SERVER_PRIVATE_KEY || "your-32-byte-server-private-key-in-hex";
|
||||||
|
const RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
|
||||||
|
|
||||||
// --- Main Server Logic ---
|
// --- Main Server Logic ---
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -47,33 +48,26 @@ async function main() {
|
|||||||
const serverPubkey = await signer.getPublicKey();
|
const serverPubkey = await signer.getPublicKey();
|
||||||
|
|
||||||
console.log(`Server Public Key: ${serverPubkey}`);
|
console.log(`Server Public Key: ${serverPubkey}`);
|
||||||
console.log('Connecting to relays...');
|
console.log("Connecting to relays...");
|
||||||
|
|
||||||
// 2. Create and Configure the MCP Server
|
// 2. Create and Configure the MCP Server
|
||||||
const mcpServer = new McpServer({
|
const mcpServer = new McpServer({
|
||||||
name: 'nostr-echo-server',
|
name: "nostr-echo-server",
|
||||||
version: '1.0.0',
|
version: "1.0.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. Define a simple "echo" tool
|
// 3. Define a simple "echo" tool
|
||||||
const echoTool = new Tool({
|
server.registerTool(
|
||||||
name: 'echo',
|
"echo",
|
||||||
description: 'Replies with the input it received.',
|
{
|
||||||
inputSchema: {
|
title: "Echo Tool",
|
||||||
type: 'object',
|
description: "Echoes back the provided message",
|
||||||
properties: {
|
inputSchema: { message: z.string() },
|
||||||
message: { type: 'string' },
|
|
||||||
},
|
|
||||||
required: ['message'],
|
|
||||||
},
|
},
|
||||||
execute: async (input) => {
|
async ({ message }) => ({
|
||||||
return {
|
content: [{ type: "text", text: `Tool echo: ${message}` }],
|
||||||
content: `You said: ${input.message}`
|
}),
|
||||||
};
|
);
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
mcpServer.tools.add(echoTool);
|
|
||||||
|
|
||||||
// 4. Configure the Nostr Server Transport
|
// 4. Configure the Nostr Server Transport
|
||||||
const serverTransport = new NostrServerTransport({
|
const serverTransport = new NostrServerTransport({
|
||||||
@@ -81,19 +75,19 @@ async function main() {
|
|||||||
relayHandler: relayPool,
|
relayHandler: relayPool,
|
||||||
isPublicServer: true, // Announce this server on the Nostr network
|
isPublicServer: true, // Announce this server on the Nostr network
|
||||||
serverInfo: {
|
serverInfo: {
|
||||||
name: 'CTXVM Echo Server',
|
name: "CTXVM Echo Server",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. Connect the server
|
// 5. Connect the server
|
||||||
await mcpServer.connect(serverTransport);
|
await mcpServer.connect(serverTransport);
|
||||||
|
|
||||||
console.log('Server is running and listening for requests on Nostr...');
|
console.log("Server is running and listening for requests on Nostr...");
|
||||||
console.log('Press Ctrl+C to exit.');
|
console.log("Press Ctrl+C to exit.");
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
console.error('Failed to start server:', error);
|
console.error("Failed to start server:", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -117,31 +111,27 @@ Next, let's create the client that will connect to our server.
|
|||||||
Create a new file named `client.ts`:
|
Create a new file named `client.ts`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client';
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||||
import { NostrClientTransport } from '@ctxvm/sdk/transport';
|
import { NostrClientTransport } from "@ctxvm/sdk/transport";
|
||||||
import { PrivateKeySigner } from '@ctxvm/sdk/signer';
|
import { PrivateKeySigner } from "@ctxvm/sdk/signer";
|
||||||
import { SimpleRelayPool } from '@ctxvm/sdk/relay';
|
import { SimpleRelayPool } from "@ctxvm/sdk/relay";
|
||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
// IMPORTANT: Replace with the server's public key from the server output
|
// IMPORTANT: Replace with the server's public key from the server output
|
||||||
const SERVER_PUBKEY = 'the-public-key-printed-by-server.ts';
|
const SERVER_PUBKEY = "the-public-key-printed-by-server.ts";
|
||||||
|
|
||||||
// IMPORTANT: Replace with your own private key
|
// IMPORTANT: Replace with your own private key
|
||||||
const CLIENT_PRIVATE_KEY_HEX = process.env.CLIENT_PRIVATE_KEY || 'your-32-byte-client-private-key-in-hex';
|
const CLIENT_PRIVATE_KEY_HEX =
|
||||||
const RELAYS = ['wss://relay.damus.io', 'wss://nos.lol'];
|
process.env.CLIENT_PRIVATE_KEY || "your-32-byte-client-private-key-in-hex";
|
||||||
|
const RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
|
||||||
|
|
||||||
// --- Main Client Logic ---
|
// --- Main Client Logic ---
|
||||||
async function main() {
|
async function main() {
|
||||||
if (SERVER_PUBKEY.startsWith('the-public-key')) {
|
|
||||||
console.error('Please replace SERVER_PUBKEY with the actual public key from the server.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 1. Setup Signer and Relay Pool
|
// 1. Setup Signer and Relay Pool
|
||||||
const signer = new PrivateKeySigner(CLIENT_PRIVATE_KEY_HEX);
|
const signer = new PrivateKeySigner(CLIENT_PRIVATE_KEY_HEX);
|
||||||
const relayPool = new SimpleRelayPool(RELAYS);
|
const relayPool = new SimpleRelayPool(RELAYS);
|
||||||
|
|
||||||
console.log('Connecting to relays...');
|
console.log("Connecting to relays...");
|
||||||
|
|
||||||
// 2. Configure the Nostr Client Transport
|
// 2. Configure the Nostr Client Transport
|
||||||
const clientTransport = new NostrClientTransport({
|
const clientTransport = new NostrClientTransport({
|
||||||
@@ -154,26 +144,28 @@ async function main() {
|
|||||||
const mcpClient = new Client();
|
const mcpClient = new Client();
|
||||||
await mcpClient.connect(clientTransport);
|
await mcpClient.connect(clientTransport);
|
||||||
|
|
||||||
console.log('Connected to server!');
|
console.log("Connected to server!");
|
||||||
|
|
||||||
// 4. List the available tools
|
// 4. List the available tools
|
||||||
console.log('\nListing available tools...');
|
console.log("\nListing available tools...");
|
||||||
const tools = await mcpClient.listTools();
|
const tools = await mcpClient.listTools();
|
||||||
console.log('Tools:', tools);
|
console.log("Tools:", tools);
|
||||||
|
|
||||||
// 5. Call the "echo" tool
|
// 5. Call the "echo" tool
|
||||||
console.log('\nCalling the "echo" tool...');
|
console.log('\nCalling the "echo" tool...');
|
||||||
const echoResult = await mcpClient.callTool('echo', { message: 'Hello, Nostr!' });
|
const echoResult = await mcpClient.callTool({
|
||||||
console.log('Echo result:', echoResult.content);
|
name: "echo",
|
||||||
|
arguments: { message: "Hello, Nostr!" },
|
||||||
|
});
|
||||||
|
console.log("Echo result:", echoResult);
|
||||||
|
|
||||||
// 6. Close the connection
|
// 6. Close the connection
|
||||||
await mcpClient.close();
|
await mcpClient.close();
|
||||||
console.log('\nConnection closed.');
|
console.log("\nConnection closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
console.error('Client failed:', error);
|
console.error("Client failed:", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -212,4 +204,5 @@ Echo result: You said: Hello, Nostr!
|
|||||||
|
|
||||||
Connection closed.
|
Connection closed.
|
||||||
```
|
```
|
||||||
|
|
||||||
And that's it! You've successfully created an MCP client and server that communicate securely and decentrally over the Nostr network.
|
And that's it! You've successfully created an MCP client and server that communicate securely and decentrally over the Nostr network.
|
||||||
Reference in New Issue
Block a user