Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
31e4bd1
fix: resolve clippy warnings and align naming with TS SDK
aphrodoe Apr 3, 2026
f2f7eb9
feat: add CEP-17/CEP-19 constants and bootstrap relay URLs
aphrodoe Apr 3, 2026
924bc3d
feat: support ephemeral gift wraps (kind 21059) per CEP-19
aphrodoe Apr 3, 2026
79c62fa
fix: return signed event id for encrypted message correlation
piyush-1337 Apr 3, 2026
be67b6d
fix: return signed event id for encrypted message correlation (#2)
ContextVM-org Apr 3, 2026
d7d1ee5
chore: align with CEP-4 and official rmcp SDK
aphrodoe Apr 3, 2026
422ef77
Feat/cep19 alignment (#1)
ContextVM-org Apr 3, 2026
e569fa9
feat: implemented basic rmcp support helpers and functions
kushagra0902 Apr 3, 2026
6c2d732
Merge branch 'main' of https://github.com/kushagra0902/contextVM_sdk_rs
kushagra0902 Apr 3, 2026
78edc9c
feat: wired logic in gateway
kushagra0902 Apr 3, 2026
5505e37
fix: fixed duplicate dependency warning
kushagra0902 Apr 3, 2026
d3d9a2f
feat(rmcp): add typed discovery and client service integration
aphrodoe Apr 3, 2026
84911d7
feat: add rmcp integration test example
aphrodoe Apr 3, 2026
61da016
chore: cargo formatting fixes
aphrodoe Apr 4, 2026
f4e2fd9
fix: removed hard coded values and redundant KIND types
kushagra0902 Apr 4, 2026
136248d
Integrating RMCP to existing rust sdk (#3)
ContextVM-org Apr 4, 2026
905b16a
fix: add missing util::logger module for rmcp build
harsh04044 Apr 5, 2026
ae57431
test: add comprehensive tests for core types
harsh04044 Apr 6, 2026
0f64824
fix: add missing util::logger module for rmcp build (#6)
ContextVM-org Apr 6, 2026
9cc19b1
test: add comprehensive tests for core types (#7)
ContextVM-org Apr 6, 2026
024ee46
feat: implemented tracing setup for subscriptions and custom log form…
kushagra0902 Apr 6, 2026
c84036d
enhancement: improved error logging in encryption module
kushagra0902 Apr 6, 2026
bc10f03
enhancement: improved error logging in gateway and proxy modules
kushagra0902 Apr 6, 2026
3cb5eb0
enhancement: improved error logging in rmcp and transport modules
kushagra0902 Apr 6, 2026
bbe369d
feat: added exmaples with log files
kushagra0902 Apr 6, 2026
7f47857
setup basic github workflow
kushagra0902 Apr 6, 2026
75bb1ab
fix: verify inner event signatures after gift-wrap decryption
Anshumancanrock Apr 7, 2026
8608340
test: add conformance tests for initialization flow wire format
harsh04044 Apr 7, 2026
c3a9073
fix: resolve all clippy warnings, remove orphaned doc comment and dup…
Anshumancanrock Apr 7, 2026
d2ff04d
Setup basic github workflow (#10)
ContextVM-org Apr 7, 2026
03fb6c7
fix: verify inner event signatures after gift-wrap decryption (#11)
ContextVM-org Apr 7, 2026
8697822
test: add conformance tests for initialization flow wire format (#12)
ContextVM-org Apr 7, 2026
2de8652
fix: all Clippy warnings, orphaned doc comment & duplicate constant (…
ContextVM-org Apr 7, 2026
3f00ece
fix: route event loops through validated message parsing
Anshumancanrock Apr 7, 2026
5fe046f
fix: route event loops through validated message parsing (#15)
ContextVM-org Apr 7, 2026
68b4711
test: add wire format conformance tests for tools/list, tools/call, a…
harsh04044 Apr 7, 2026
dbd4484
fix: made the log format across all the files consistent
kushagra0902 Apr 7, 2026
4f5f919
fix: fixed validation logic and improved consistency
kushagra0902 Apr 7, 2026
15cbd5f
Merge branch 'main' into loggerUpdate
kushagra0902 Apr 7, 2026
23eaa58
Add integration test CI harness with local nostr relay
Anshumancanrock Apr 7, 2026
3cb8f50
Fix CI: drop local relay, run integration example in local mode only
Anshumancanrock Apr 7, 2026
02c0bdf
Add stateless conformance tests
Anshumancanrock Apr 8, 2026
963467e
Polish stateless test wording
Anshumancanrock Apr 8, 2026
cbd7999
test: add wire format conformance tests for tools/list, tools/call, a…
ContextVM-org Apr 8, 2026
a74f2db
Enhancement: Improved error logging using Tracer framework (#9)
ContextVM-org Apr 8, 2026
6e18131
Add integration test CI harness with local nostr relay (#18)
ContextVM-org Apr 8, 2026
e48e85c
Add Stateless conformance tests (#20)
ContextVM-org Apr 8, 2026
1bc6b0f
style: apply cargo fmt formatting
harsh04044 Apr 8, 2026
be45cf6
resolve ci
harsh04044 Apr 8, 2026
bc80b8a
test: add conformance tests for signer behavior
harsh04044 Apr 8, 2026
3bdd690
ci: add fmt, clippy, and doc checks (#22)
ContextVM-org Apr 9, 2026
f07bcd9
Merge branch 'main' into test/conformance-signer
harsh04044 Apr 9, 2026
53bb7ed
fix: enforce inbound encryption policy in client event loop The clie…
Anshumancanrock Apr 9, 2026
d6f2107
fix: Client Accepts Plaintext Responses in Required Mode (#24)
ContextVM-org Apr 13, 2026
392538b
fix(transport): fix comment saying we should not use a since filter o…
theAnuragMishra Apr 12, 2026
d6fdb3d
fix: implement gift-wrap deduplication using LRU cache
harsh04044 Apr 9, 2026
f4da9ac
test: add conformance tests for signer behavior (#23)
ContextVM-org Apr 21, 2026
33af83b
fix: implement gift-wrap deduplication using LRU cache (#26)
ContextVM-org Apr 21, 2026
e0f996e
fix(transport): fix comment saying we should not use a since filter o…
ContextVM-org Apr 21, 2026
b7fe8ef
refactor: extract RelayPoolTrait and implement MockRelayPool for testing
harsh04044 Apr 11, 2026
26dfe13
test: add conformance tests for gift-wrap deduplication
harsh04044 Apr 22, 2026
412c664
refactor: extract RelayPoolTrait and add MockRelayPool for integratio…
ContextVM-org Apr 22, 2026
65a0ba7
test: add conformance tests for gift-wrap deduplication (#35)
ContextVM-org Apr 22, 2026
ce94af1
refactor: extract ClientCorrelationStore and ServerEventRouteStore in…
harsh04044 Apr 22, 2026
6bdc586
feat(cep-19): add GiftWrapMode type and ephemeral gift-wrap encryption
kushagra0902 Apr 23, 2026
6f67439
refactor: extract ClientCorrelationStore and ServerEventRouteStore i…
ContextVM-org Apr 23, 2026
eeb4e36
test: add Phase 3 integration tests using MockRelayPool
harsh04044 Apr 22, 2026
e0f3aec
CEP-19: Part 1 (#39)
ContextVM-org Apr 23, 2026
9196a95
test: add Phase 3 integration tests using MockRelayPool (#38)
ContextVM-org Apr 23, 2026
3dfc682
refactor: enrich correlation stores, add SessionStore, and port Phase…
harsh04044 Apr 24, 2026
c2e9c48
feat(cep-19): Added ephemeral package handling logic in client transport
kushagra0902 Apr 23, 2026
e5fe6d6
test: add LRU eviction and registerRequest initialize flag conformanc…
harsh04044 Apr 24, 2026
04a0662
refactor: enrich correlation stores, add SessionStore and port Phase …
ContextVM-org Apr 24, 2026
e5ccd48
test: port remaining integration and stateless mode tests, closing al…
harsh04044 Apr 26, 2026
96963db
fix(lru): unbounded LRU cache initialisation
kushagra0902 Apr 26, 2026
6c82a8c
CEP1-19 Part2: Client side integration (#42)
ContextVM-org Apr 28, 2026
e61d5a0
Merge branch 'main' into test/remaining-integration-tests
ContextVM-org Apr 28, 2026
350d569
test: add GiftWrapMode import to stateless mode conformance test
ContextVM-org Apr 28, 2026
fa0030b
test: port remaining integration and stateless mode tests (#44)
ContextVM-org Apr 28, 2026
98d9fd4
style: reorder import statements alphabetically
ContextVM-org Apr 28, 2026
123471c
fix(lru): unbounded LRU cache initialisation (#50)
ContextVM-org Apr 28, 2026
060b701
fix(lru): unbounded LRU cache initialisation (#50)
ContextVM-org Apr 28, 2026
996980f
bug(transport): non atomic send_response behaviour (#48)
ContextVM-org Apr 29, 2026
7413e10
format
ContextVM-org Apr 29, 2026
be992f0
fix: send JSON-RPC -32000 Unauthorized error on announced servers wit…
harsh04044 Apr 29, 2026
8838ca3
fix (bug): send JSON-RPC -32000 Unauthorized error on announced serve…
ContextVM-org Apr 29, 2026
4280b1e
feat: add discovery tags module, PeerCapabilities, and server config …
harsh04044 Apr 26, 2026
1e49572
refactor: remove merge_discovery_tags per CEP-35 spec update
harsh04044 Apr 30, 2026
f0ea097
fix: register client request before publish and drop uncorrelated res…
harsh04044 Apr 30, 2026
31e454d
fix (bug): register client request before publish and drop uncorrelat…
ContextVM-org May 1, 2026
5501d11
feat: add discovery tags module and server config foundation for CEP-…
ContextVM-org May 1, 2026
676ca49
feat: add discovery tags module and server config foundation for CEP-…
ContextVM-org May 1, 2026
c499b8d
feat(cep19): added missing test cases
kushagra0902 May 2, 2026
9817860
feat(cep19): server side CEP 19 transport logic (#56)
ContextVM-org May 2, 2026
14d5328
feat: enrich ClientSession, add tag composition, server discovery tag…
harsh04044 May 1, 2026
062d377
feat: add tag composition, server discovery tag emission and capabili…
ContextVM-org May 2, 2026
378ae4d
feat: client discovery tag emission and capability learning for CEP-35
harsh04044 May 2, 2026
9fb4179
test: add integration tests for CEP-35 OR-assign, baseline freeze, an…
harsh04044 May 4, 2026
a7abfb6
feat: client discovery tag emission and capability learning for CEP-3…
ContextVM-org May 5, 2026
ae9c851
fix: add TTL sweep to client and server correlation stores to prevent…
harsh04044 May 5, 2026
5d4d5b7
fix: remove single-peer barrier in RMCP worker and add LRU-bounded Se…
harsh04044 May 4, 2026
4eafd78
docs: clarify timeout and request_timeout as correlation-retention TTLs
harsh04044 May 5, 2026
0b6a9f8
fix: remove single-peer barrier in RMCP worker and add LRU-bounded Se…
ContextVM-org May 5, 2026
b78e699
fix: wire client timeout config and add TTL sweep to prevent pending-…
ContextVM-org May 5, 2026
bd381be
fix: cancel spawned event loop tasks on close() using CancellationToken
harsh04044 May 5, 2026
6368c71
fix: stop event loop tasks on close() (#63)
ContextVM-org May 6, 2026
4d705aa
refactor(rmcp): add direct transport adapters for native ContextVM se…
ContextVM-org May 6, 2026
6469c92
fix: pre-release blockers - non_exhaustive, Lagged handling, zero-cap…
harsh04044 May 6, 2026
ad37229
refactor(rmcp): simplify transport API by removing wrapper adapters
ContextVM-org May 6, 2026
675d272
fix: pre-release blockers for 0.1.0 (#68)
ContextVM-org May 6, 2026
6230b4e
refactor(rmcp): add direct transport adapters for native ContextVM se…
ContextVM-org May 7, 2026
53c2a9e
refactor(examples): use builder pattern for transport configuration
ContextVM-org May 7, 2026
938a5be
chore: add crates.io publishing metadata, CHANGELOG, remove tracing-s…
harsh04044 May 7, 2026
235685c
chore: publishing config, CHANGELOG, and pre-release cleanup (#69)
ContextVM-org May 7, 2026
f33ef68
fix(rmcp): bridge stateless CEP-35 requests into rmcp lifecycle
ContextVM-org May 7, 2026
efe52ea
refactor(rmcp): move stateless client bootstrap logic from transport …
ContextVM-org May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Integration Tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

env:
CARGO_TERM_COLOR: always

jobs:
integration:
name: Integration tests
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache dependencies
uses: Swatinem/rust-cache@v2

- name: Run unit and local integration tests
run: cargo test --all-features

- name: Run integration example (local RMCP)
run: cargo run --example rmcp_integration_test --features rmcp -- local
42 changes: 42 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Rust CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

env:
CARGO_TERM_COLOR: always

jobs:
ci:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache dependencies
uses: Swatinem/rust-cache@v2

- name: Check
run: cargo check --all --all-features

- name: Test
run: cargo test --all --all-features

- name: Format check
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all --all-features -- -D warnings

- name: Doc check
run: cargo doc --no-deps --all-features

- name: Build
run: cargo build --verbose
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Changelog

## [0.1.0] - 2026-05-07

### Added

- Core transport layer: `NostrClientTransport` and `NostrServerTransport` over NIP-59 gift wraps
- Gateway and Proxy high-level APIs for bridging MCP over Nostr
- Discovery API: `discover_servers`, `discover_tools`, `discover_resources`, `discover_prompts`, `discover_resource_templates`
- CEP-6: server announcement publishing and querying (kinds 11316–11320)
- CEP-19: ephemeral gift wraps (kind 21059) with `GiftWrapMode` negotiation on both client and server
- CEP-35: stateless session discovery, tag composition, and capability learning
- LRU-bounded session store with configurable capacity (default 1000 sessions) and TTL expiry
- Multi-client support in `NostrServerWorker` (removed single-peer barrier)
- Direct rmcp transport adapters via `into_rmcp_transport()` for native `ContextVM` services
- `CancellationToken`-based graceful shutdown on `close()`
- TTL sweep for client and server correlation stores to prevent pending-request leaks
- `MockRelayPool` for deterministic offline testing
- Builder pattern for all transport and worker configuration structs
- Four examples: gateway, proxy, discovery, and rmcp integration test

### Fixed

- Single-peer barrier in RMCP worker rejected concurrent clients (#60)
- Pending-request leak: correlation store entries never expired by TTL (#61)
- Event loop tasks not cancelled on `close()`, causing resource leaks (#63)
- `RecvError::Lagged` killing event loop under high relay throughput (#68)
- Client race condition: responses lost when publish completed before correlation registration (#55)
- Uncorrelated responses (missing `e` tag) forwarded to consumer instead of dropped (#55)
- Non-atomic `send_response` behavior in server transport (#48)
- Unbounded LRU cache initialization with zero capacity (#50)
- Announced servers not sending JSON-RPC `-32000 Unauthorized` error for disallowed clients (#53)
38 changes: 36 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
name = "contextvm-sdk"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"
description = "Rust SDK for the ContextVM protocol — MCP over Nostr"
license = "MIT"
repository = "https://github.com/k0sti/rust-contextvm-sdk"
readme = "README.md"
repository = "https://github.com/ContextVM/rs-sdk"
homepage = "https://contextvm.org"
documentation = "https://docs.rs/contextvm-sdk"
keywords = ["nostr", "mcp", "model-context-protocol", "decentralized", "ai"]
categories = ["network-programming", "api-bindings", "asynchronous"]

[dependencies]
# Async runtime
Expand All @@ -24,6 +30,34 @@ nostr-sdk = { version = "0.43", features = ["nip59"] }
# Logging
tracing = "0.1"

# Optional MCP integration (Rust equivalent to TS @modelcontextprotocol/sdk)
rmcp = { version = "0.16.0", features = ["server", "client", "macros", "transport-worker"], optional = true }

# LRU cache for gift-wrap (outer event id) deduplication
lru = "0.12"

# CancellationToken for graceful event-loop shutdown
tokio-util = { version = "0.7", features = ["rt"] }

[features]
# Enable rmcp by default while keeping legacy APIs available.
default = ["rmcp"]
rmcp = ["dep:rmcp"]

[[example]]
name = "rmcp_integration_test"
required-features = ["rmcp"]

[[example]]
name = "native_echo_server"
required-features = ["rmcp"]

[[example]]
name = "native_echo_client"
required-features = ["rmcp"]

[dev-dependencies]
tokio-test = "0.4"
tracing-subscriber = "0.3"
anyhow = "1"
schemars = "0.8"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ tracing-subscriber = "0.3"
- Unit test: rejects events from wrong server pubkey

- [x] **2.4** Implement `NostrServerTransport`
- Config: relay_urls, encryption_mode, server_info, is_public_server, allowed_public_keys, excluded_capabilities, cleanup_interval_ms, session_timeout_ms
- Config: relay_urls, encryption_mode, server_info, is_announced_server, allowed_public_keys, excluded_capabilities, cleanup_interval_ms, session_timeout_ms
- Implements `Transport` trait
- Features:
- Subscribe to events targeting server pubkey
Expand Down
38 changes: 16 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,16 @@ use contextvm_sdk::signer;
async fn main() -> contextvm_sdk::Result<()> {
let keys = signer::generate();

let config = GatewayConfig {
nostr_config: NostrServerTransportConfig {
relay_urls: vec!["wss://relay.damus.io".into()],
encryption_mode: EncryptionMode::Optional,
server_info: Some(ServerInfo {
name: Some("My MCP Server".into()),
about: Some("Tools via Nostr".into()),
..Default::default()
}),
is_public_server: true,
..Default::default()
},
};
let config = GatewayConfig::new(
NostrServerTransportConfig::default()
.with_encryption_mode(EncryptionMode::Optional)
.with_server_info(
ServerInfo::default()
.with_name("My MCP Server")
.with_about("Tools via Nostr"),
)
.with_announced_server(true),
);

let mut gateway = NostrMCPGateway::new(keys, config).await?;
let mut requests = gateway.start().await?;
Expand All @@ -116,14 +113,11 @@ use contextvm_sdk::signer;
async fn main() -> contextvm_sdk::Result<()> {
let keys = signer::generate();

let config = ProxyConfig {
nostr_config: NostrClientTransportConfig {
relay_urls: vec!["wss://relay.damus.io".into()],
server_pubkey: "abc123...server_hex_pubkey".into(),
encryption_mode: EncryptionMode::Optional,
..Default::default()
},
};
let config = ProxyConfig::new(
NostrClientTransportConfig::default()
.with_server_pubkey("abc123...server_hex_pubkey")
.with_encryption_mode(EncryptionMode::Optional),
);

let mut proxy = NostrMCPProxy::new(keys, config).await?;
let mut responses = proxy.start().await?;
Expand Down Expand Up @@ -201,7 +195,7 @@ metadata-private delivery. Server announcements (kinds 11316–11320) are always
| `relay_urls` | `["wss://relay.damus.io"]` | Nostr relays to connect to |
| `encryption_mode` | `Optional` | Encryption policy |
| `server_info` | `None` | Server metadata for announcements |
| `is_public_server` | `false` | Whether to publish announcements |
| `is_announced_server` | `false` | Whether to publish announcements (CEP-6) |
| `allowed_public_keys` | `[]` (allow all) | Client pubkey allowlist (hex) |
| `excluded_capabilities` | `[]` | Methods exempt from allowlist |
| `session_timeout` | `300s` | Inactive session expiry |
Expand Down
3 changes: 1 addition & 2 deletions examples/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ async fn main() -> contextvm_sdk::Result<()> {
println!(" Resources: {} found", resources.len());
}

let prompts =
discovery::discover_prompts(client, &server.pubkey_parsed, &relays).await?;
let prompts = discovery::discover_prompts(client, &server.pubkey_parsed, &relays).await?;
if !prompts.is_empty() {
println!(" Prompts: {} found", prompts.len());
}
Expand Down
22 changes: 10 additions & 12 deletions examples/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//!
//! This demonstrates how to create a ContextVM gateway that receives
//! MCP requests over Nostr and responds to them.
//!
//! Usage: cargo run --example gateway

use contextvm_sdk::core::types::*;
use contextvm_sdk::gateway::{GatewayConfig, NostrMCPGateway};
Expand All @@ -17,18 +19,14 @@ async fn main() -> contextvm_sdk::Result<()> {
println!("Server pubkey: {}", keys.public_key().to_hex());

// Configure the gateway
let config = GatewayConfig {
nostr_config: NostrServerTransportConfig {
relay_urls: vec!["wss://relay.damus.io".to_string()],
server_info: Some(ServerInfo {
name: Some("Echo Server".to_string()),
about: Some("A simple echo tool exposed via ContextVM".to_string()),
..Default::default()
}),
is_public_server: true,
..Default::default()
},
};
let nostr_config = NostrServerTransportConfig::default()
.with_server_info(
ServerInfo::default()
.with_name("Echo Server")
.with_about("A simple echo tool exposed via ContextVM"),
)
.with_announced_server(true);
let config = GatewayConfig::new(nostr_config);

let mut gateway = NostrMCPGateway::new(keys, config).await?;
let mut rx = gateway.start().await?;
Expand Down
94 changes: 94 additions & 0 deletions examples/native_echo_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Example: Native rmcp client over ContextVM/Nostr.
//!
//! Usage:
//! cargo run --example native_echo_client -- <server_pubkey_hex>

use anyhow::{Context, Result};
use contextvm_sdk::transport::client::{NostrClientTransport, NostrClientTransportConfig};
use contextvm_sdk::{signer, EncryptionMode, GiftWrapMode};
use rmcp::{
model::{CallToolRequestParams, CallToolResult},
ClientHandler, ServiceExt,
};

const RELAY_URL: &str = "wss://relay.contextvm.org";

#[derive(Clone, Default)]
struct EchoClient;

impl ClientHandler for EchoClient {}

#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::from_default_env()
.add_directive("contextvm_sdk=info".parse()?)
.add_directive("rmcp=warn".parse()?),
)
.init();

let server_pubkey = std::env::args()
.nth(1)
.context("Usage: native_echo_client <server_pubkey_hex>")?;

let signer = signer::generate();
println!("Native ContextVM echo client starting");
println!("Relay: {RELAY_URL}");
println!("Client pubkey: {}", signer.public_key().to_hex());
println!("Target server pubkey: {server_pubkey}");

let transport = NostrClientTransport::new(
signer,
NostrClientTransportConfig::default()
.with_relay_urls(vec![RELAY_URL.to_string()])
.with_server_pubkey(server_pubkey)
.with_encryption_mode(EncryptionMode::Optional)
.with_gift_wrap_mode(GiftWrapMode::Optional),
)
.await?;

let client = EchoClient.serve(transport).await?;

let peer_info = client
.peer_info()
.context("server did not provide peer info after initialize")?;
println!("Connected to: {:?}", peer_info.server_info.name);

let tools = client.list_all_tools().await?;
println!("Discovered {} tool(s):", tools.len());
for tool in &tools {
println!("- {}", tool.name);
}

let result = client
.call_tool(CallToolRequestParams {
name: "echo".into(),
arguments: serde_json::from_value(serde_json::json!({
"message": "hello from native contextvm client"
}))
.ok(),
meta: None,
task: None,
})
.await?;

println!("Echo result: {}", first_text(&result));

client.cancel().await?;
Ok(())
}

fn first_text(result: &CallToolResult) -> String {
result
.content
.iter()
.find_map(|content| {
if let rmcp::model::RawContent::Text(text) = &content.raw {
Some(text.text.clone())
} else {
None
}
})
.unwrap_or_default()
}
Loading