Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ nostr-sdk = { version = "0.43", features = ["nip59"] }
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 }
rmcp = { git = "https://github.com/ContextVM-org/rust-sdk", branch = "progress-aware-request-timeouts", features = ["server", "client", "macros", "transport-worker"], optional = true }

# LRU cache for gift-wrap (outer event id) deduplication
lru = "0.12"
Expand Down
17 changes: 8 additions & 9 deletions examples/native_echo_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ async fn main() -> Result<()> {
}

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,
})
.call_tool(
CallToolRequestParams::new("echo").with_arguments(
serde_json::from_value(serde_json::json!({
"message": "hello from native contextvm client"
}))
.unwrap(),
),
)
.await?;

println!("Echo result: {}", first_text(&result));
Expand Down
20 changes: 7 additions & 13 deletions examples/native_echo_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,13 @@ impl EchoServer {
#[tool_handler]
impl ServerHandler for EchoServer {
fn get_info(&self) -> rmcp::model::ServerInfo {
rmcp::model::ServerInfo {
protocol_version: ProtocolVersion::LATEST,
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation {
name: "contextvm-native-echo".to_string(),
title: Some("ContextVM Native Echo Server".to_string()),
version: "0.1.0".to_string(),
description: Some("Native rmcp echo server over ContextVM/Nostr".to_string()),
icons: None,
website_url: None,
},
instructions: Some("Call the echo tool with a message string".to_string()),
}
rmcp::model::ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(
Implementation::new("contextvm-native-echo", "0.1.0")
.with_title("ContextVM Native Echo Server")
.with_description("Native rmcp echo server over ContextVM/Nostr"),
)
.with_instructions("Call the echo tool with a message string")
}
}

Expand Down
41 changes: 17 additions & 24 deletions examples/rmcp_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,18 @@ impl DemoServer {
#[tool_handler]
impl ServerHandler for DemoServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::LATEST,
capabilities: ServerCapabilities::builder()
ServerInfo::new(
ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.build(),
server_info: Implementation {
name: "contextvm-demo".to_string(),
title: Some("ContextVM Demo Server".to_string()),
version: "0.1.0".to_string(),
description: Some("Demonstrates rmcp integration over ContextVM".to_string()),
icons: None,
website_url: None,
},
instructions: Some("Try: echo, add, get_echo_count".to_string()),
}
)
.with_server_info(
Implementation::new("contextvm-demo", "0.1.0")
.with_title("ContextVM Demo Server")
.with_description("Demonstrates rmcp integration over ContextVM"),
)
.with_instructions("Try: echo, add, get_echo_count")
}

async fn list_resources(
Expand All @@ -177,12 +173,10 @@ impl ServerHandler for DemoServer {
_ctx: RequestContext<RoleServer>,
) -> Result<ReadResourceResult, ErrorData> {
match req.uri.as_str() {
"demo://readme" => Ok(ReadResourceResult {
contents: vec![ResourceContents::text(
"This server demonstrates the ContextVM rmcp integration.",
req.uri,
)],
}),
"demo://readme" => Ok(ReadResourceResult::new(vec![ResourceContents::text(
"This server demonstrates the ContextVM rmcp integration.",
req.uri,
)])),
other => Err(ErrorData::resource_not_found(
"not_found",
Some(serde_json::json!({ "uri": other })),
Expand Down Expand Up @@ -696,12 +690,11 @@ fn assert_error_response(response: &JsonRpcMessage) -> Result<()> {
}

fn call_params(name: &'static str, args: Option<serde_json::Value>) -> CallToolRequestParams {
CallToolRequestParams {
name: name.into(),
arguments: args.and_then(|v| serde_json::from_value(v).ok()),
meta: None,
task: None,
let mut params = CallToolRequestParams::new(name);
if let Some(v) = args.and_then(|v| serde_json::from_value(v).ok()) {
params = params.with_arguments(v);
}
params
}

fn first_text(result: &CallToolResult) -> String {
Expand Down
39 changes: 16 additions & 23 deletions src/rmcp_transport/pipeline_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,13 @@ mod tests {
#[tool_handler]
impl ServerHandler for StatelessTestServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::LATEST,
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation {
name: "stateless-test-server".to_string(),
title: Some("Stateless Test Server".to_string()),
version: "0.1.0".to_string(),
description: Some("Stateless rmcp regression test server".to_string()),
icons: None,
website_url: None,
},
instructions: Some("Use the echo tool".to_string()),
}
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(
Implementation::new("stateless-test-server", "0.1.0")
.with_title("Stateless Test Server")
.with_description("Stateless rmcp regression test server"),
)
.with_instructions("Use the echo tool")
}
}

Expand Down Expand Up @@ -381,7 +375,7 @@ mod tests {
message: "Method not found".into(),
data: None,
},
RequestId::String(std::sync::Arc::from(event_id)),
Some(RequestId::String(std::sync::Arc::from(event_id))),
);
let internal = rmcp_server_tx_to_internal(rmcp_err).unwrap();

Expand Down Expand Up @@ -454,15 +448,14 @@ mod tests {
);

let result = client
.call_tool(CallToolRequestParams {
name: "echo".into(),
arguments: serde_json::from_value(serde_json::json!({
"message": "hello from stateless test"
}))
.ok(),
meta: None,
task: None,
})
.call_tool(
CallToolRequestParams::new("echo").with_arguments(
serde_json::from_value(serde_json::json!({
"message": "hello from stateless test"
}))
.unwrap(),
),
)
.await
.expect("tools/call should succeed");

Expand Down
38 changes: 13 additions & 25 deletions tests/e2e_happy_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,13 @@ impl DemoServer {
#[tool_handler]
impl ServerHandler for DemoServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::LATEST,
capabilities: ServerCapabilities::builder()
ServerInfo::new(
ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.build(),
server_info: Implementation {
name: "e2e-test-server".to_string(),
title: None,
version: "0.1.0".to_string(),
description: None,
icons: None,
website_url: None,
},
instructions: None,
}
)
.with_server_info(Implementation::new("e2e-test-server", "0.1.0"))
}

async fn list_resources(
Expand All @@ -125,9 +116,10 @@ impl ServerHandler for DemoServer {
_ctx: RequestContext<RoleServer>,
) -> Result<ReadResourceResult, ErrorData> {
match req.uri.as_str() {
"demo://readme" => Ok(ReadResourceResult {
contents: vec![ResourceContents::text("Demo content.", req.uri)],
}),
"demo://readme" => Ok(ReadResourceResult::new(vec![ResourceContents::text(
"Demo content.",
req.uri,
)])),
other => Err(ErrorData::resource_not_found(
"not_found",
Some(serde_json::json!({ "uri": other })),
Expand Down Expand Up @@ -158,12 +150,11 @@ fn first_text(result: &CallToolResult) -> String {
}

fn call_params(name: &'static str, args: Option<serde_json::Value>) -> CallToolRequestParams {
CallToolRequestParams {
name: name.into(),
arguments: args.and_then(|v| serde_json::from_value(v).ok()),
meta: None,
task: None,
let mut params = CallToolRequestParams::new(name);
if let Some(v) = args.and_then(|v| serde_json::from_value(v).ok()) {
params = params.with_arguments(v);
}
params
}

// ── Core scenario runner ──────────────────────────────────────────────────
Expand Down Expand Up @@ -300,10 +291,7 @@ async fn run_e2e_scenario(mode: EncryptionMode) {
assert_eq!(resources[0].name.as_str(), "Demo README");

let read_result = client
.read_resource(ReadResourceRequestParams {
uri: "demo://readme".to_string(),
meta: None,
})
.read_resource(ReadResourceRequestParams::new("demo://readme"))
.await
.expect("read_resource");
assert_eq!(read_result.contents.len(), 1);
Expand Down
47 changes: 16 additions & 31 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,13 @@ impl DemoServer {
#[tool_handler]
impl ServerHandler for DemoServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::LATEST,
capabilities: ServerCapabilities::builder()
ServerInfo::new(
ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.build(),
server_info: Implementation {
name: "integration-test".to_string(),
title: None,
version: "0.1.0".to_string(),
description: None,
icons: None,
website_url: None,
},
instructions: None,
}
)
.with_server_info(Implementation::new("integration-test", "0.1.0"))
}

async fn list_resources(
Expand All @@ -115,9 +106,10 @@ impl ServerHandler for DemoServer {
_ctx: RequestContext<RoleServer>,
) -> Result<ReadResourceResult, ErrorData> {
match req.uri.as_str() {
"demo://readme" => Ok(ReadResourceResult {
contents: vec![ResourceContents::text("Demo content.", req.uri)],
}),
"demo://readme" => Ok(ReadResourceResult::new(vec![ResourceContents::text(
"Demo content.",
req.uri,
)])),
other => Err(ErrorData::resource_not_found(
"not_found",
Some(serde_json::json!({ "uri": other })),
Expand Down Expand Up @@ -162,27 +154,20 @@ async fn test_local_rmcp() {
let tools = client.list_all_tools().await.expect("list tools");
assert_eq!(tools.len(), 3);

let add = client
.call_tool(CallToolRequestParams {
name: "add".into(),
arguments: serde_json::from_value(serde_json::json!({ "a": 7, "b": 5 })).ok(),
meta: None,
task: None,
})
.await
.expect("call add");
let add =
client
.call_tool(CallToolRequestParams::new("add").with_arguments(
serde_json::from_value(serde_json::json!({ "a": 7, "b": 5 })).unwrap(),
))
.await
.expect("call add");
assert!(first_text(&add).contains("12"));

let resources = client.list_all_resources().await.expect("list resources");
assert_eq!(resources.len(), 1);

match client
.call_tool(CallToolRequestParams {
name: "no_such_tool".into(),
arguments: None,
meta: None,
task: None,
})
.call_tool(CallToolRequestParams::new("no_such_tool"))
.await
{
Err(_) => {}
Expand Down
Loading