chore: add #[non_exhaustive] to remaining public structs (#768)
* chore: add #[non_exhaustive] to remaining public structs * chore: add #[non_exhaustive] to remaining public types * chore: enable exhaustive_structs/enums clippy lints * test: add untagged ServerResult deserialization regression tests
This commit is contained in:
parent
ee1c63c53f
commit
6a3b32d3ab
14 changed files with 430 additions and 211 deletions
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
|
||||
|
||||
use rmcp::{
|
||||
ErrorData as McpError, handler::server::wrapper::Parameters, model::*, schemars, tool,
|
||||
tool_router,
|
||||
|
|
|
|||
|
|
@ -13,3 +13,122 @@ fn test_tool_list_result() {
|
|||
})
|
||||
));
|
||||
}
|
||||
|
||||
/// Regression tests for `#[serde(untagged)]` deserialization of `ServerResult`.
|
||||
///
|
||||
/// `ServerResult` is an untagged enum, so serde tries each variant in declaration
|
||||
/// order. `GetTaskPayloadResult` has a custom `Deserialize` impl that always fails
|
||||
/// so it is skipped, and `CustomResult(Value)` acts as the catch-all. If variant
|
||||
/// ordering changes or the custom impl is removed, these tests will catch the
|
||||
/// regression.
|
||||
mod untagged_server_result {
|
||||
use rmcp::model::{CallToolResult, JsonRpcResponse, ServerJsonRpcMessage, ServerResult};
|
||||
use serde_json::json;
|
||||
|
||||
/// Helper: wrap a result value in a JSON-RPC response envelope.
|
||||
fn wrap_response(result: serde_json::Value) -> serde_json::Value {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": result
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a JSON-RPC response and return the inner `ServerResult`.
|
||||
fn parse_result(json: serde_json::Value) -> ServerResult {
|
||||
let msg: ServerJsonRpcMessage = serde_json::from_value(json).unwrap();
|
||||
match msg {
|
||||
ServerJsonRpcMessage::Response(JsonRpcResponse { result, .. }) => result,
|
||||
other => panic!("expected Response, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialize_result_deserializes_to_correct_variant() {
|
||||
let result = parse_result(wrap_response(json!({
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": {},
|
||||
"serverInfo": {
|
||||
"name": "test-server",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
})));
|
||||
assert!(
|
||||
matches!(result, ServerResult::InitializeResult(_)),
|
||||
"expected InitializeResult, got {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_tool_result_deserializes_to_correct_variant() {
|
||||
let result = parse_result(wrap_response(json!({
|
||||
"content": [
|
||||
{ "type": "text", "text": "hello" }
|
||||
]
|
||||
})));
|
||||
assert!(
|
||||
matches!(result, ServerResult::CallToolResult(_)),
|
||||
"expected CallToolResult, got {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_object_deserializes_to_empty_result() {
|
||||
let result = parse_result(wrap_response(json!({})));
|
||||
assert!(
|
||||
matches!(result, ServerResult::EmptyResult(_)),
|
||||
"expected EmptyResult, got {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_shape_falls_through_to_custom_result() {
|
||||
// A value that doesn't match any known result type should land in
|
||||
// CustomResult, NOT GetTaskPayloadResult.
|
||||
let result = parse_result(wrap_response(json!({
|
||||
"some_unknown_field": "some_value",
|
||||
"number": 42
|
||||
})));
|
||||
assert!(
|
||||
matches!(result, ServerResult::CustomResult(_)),
|
||||
"expected CustomResult, got {result:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arbitrary_json_value_does_not_deserialize_as_get_task_payload_result() {
|
||||
// GetTaskPayloadResult wraps a bare Value, but its custom Deserialize
|
||||
// always fails so serde skips it during untagged resolution.
|
||||
// Any JSON value must fall through to CustomResult instead.
|
||||
for value in [json!(42), json!("hello"), json!(null), json!([1, 2, 3])] {
|
||||
let result = parse_result(wrap_response(value.clone()));
|
||||
assert!(
|
||||
matches!(result, ServerResult::CustomResult(_)),
|
||||
"value {value} should deserialize as CustomResult, got {result:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_initialize_result_preserves_variant() {
|
||||
let json = json!({
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": {},
|
||||
"serverInfo": { "name": "test", "version": "1.0" }
|
||||
});
|
||||
// Parse as ServerResult, serialize back, parse again — must stay InitializeResult.
|
||||
let result = parse_result(wrap_response(json.clone()));
|
||||
assert!(matches!(&result, ServerResult::InitializeResult(_)));
|
||||
let reserialized = serde_json::to_value(&result).unwrap();
|
||||
let result2 = parse_result(wrap_response(reserialized));
|
||||
assert!(matches!(result2, ServerResult::InitializeResult(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_call_tool_result_preserves_variant() {
|
||||
let original = CallToolResult::success(vec![rmcp::model::Content::text("hello world")]);
|
||||
let json = serde_json::to_value(&original).unwrap();
|
||||
let result = parse_result(wrap_response(json));
|
||||
assert!(matches!(result, ServerResult::CallToolResult(_)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1458,20 +1458,15 @@ async fn test_peer_request_options_timeout() {
|
|||
|
||||
let timeout = Some(Duration::from_secs(15));
|
||||
|
||||
let options = PeerRequestOptions {
|
||||
timeout,
|
||||
meta: None,
|
||||
};
|
||||
let mut options = PeerRequestOptions::default();
|
||||
options.timeout = timeout;
|
||||
|
||||
// Verify timeout is properly stored
|
||||
assert_eq!(options.timeout, timeout);
|
||||
assert!(options.meta.is_none());
|
||||
|
||||
// Test with no timeout
|
||||
let options_no_timeout = PeerRequestOptions {
|
||||
timeout: None,
|
||||
meta: None,
|
||||
};
|
||||
let options_no_timeout = PeerRequestOptions::default();
|
||||
|
||||
assert!(options_no_timeout.timeout.is_none());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clippy::exhaustive_structs)]
|
||||
//cargo test --test test_json_schema_detection --features "client server macros"
|
||||
use rmcp::{
|
||||
Json, ServerHandler, handler::server::router::tool::ToolRouter, tool, tool_handler, tool_router,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use rmcp::{
|
|||
model::*,
|
||||
service::{RequestContext, Service},
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
// Tests start here
|
||||
#[tokio::test]
|
||||
|
|
@ -48,13 +47,7 @@ async fn test_context_inclusion_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -85,13 +78,7 @@ async fn test_context_inclusion_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(2),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(2), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -122,13 +109,7 @@ async fn test_context_inclusion_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(3),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(3), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -179,13 +160,7 @@ async fn test_context_inclusion_ignored_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -241,13 +216,7 @@ async fn test_message_sequence_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -307,13 +276,7 @@ async fn test_message_sequence_validation_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -330,13 +293,7 @@ async fn test_message_sequence_validation_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(2),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(2), client.peer().clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
@ -370,13 +327,7 @@ async fn test_selective_context_handling_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -405,13 +356,7 @@ async fn test_selective_context_handling_integration() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(2),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(2), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -457,13 +402,7 @@ async fn test_context_inclusion() -> anyhow::Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Meta::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -388,7 +388,6 @@
|
|||
"content": {
|
||||
"description": "The content returned by the tool (text, images, etc.)",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Annotated"
|
||||
}
|
||||
|
|
@ -403,7 +402,10 @@
|
|||
"structuredContent": {
|
||||
"description": "An optional JSON object that represents the structured result of the tool call"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
"CancelTaskResult": {
|
||||
"description": "Response to a `tasks/cancel` request.\n\nPer spec, `CancelTaskResult = allOf[Result, Task]` — same shape as `GetTaskResult`.",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use rmcp::{
|
|||
model::*,
|
||||
service::{RequestContext, Service},
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_basic_sampling_message_creation() -> Result<()> {
|
||||
|
|
@ -126,13 +125,7 @@ async fn test_sampling_integration_with_test_handlers() -> Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(1),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(1), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -189,13 +182,7 @@ async fn test_sampling_no_context_inclusion() -> Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(2),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(2), client.peer().clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
@ -253,13 +240,7 @@ async fn test_sampling_error_invalid_message_sequence() -> Result<()> {
|
|||
let result = handler
|
||||
.handle_request(
|
||||
request.clone(),
|
||||
RequestContext {
|
||||
peer: client.peer().clone(),
|
||||
ct: CancellationToken::new(),
|
||||
id: NumberOrString::Number(3),
|
||||
meta: Default::default(),
|
||||
extensions: Default::default(),
|
||||
},
|
||||
RequestContext::new(NumberOrString::Number(3), client.peer().clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
|
|||
|
|
@ -74,13 +74,7 @@ async fn start_test_server(ct: CancellationToken, trigger: Arc<Notify>) -> Strin
|
|||
let service = StreamableHttpService::new(
|
||||
move || Ok(server.clone()),
|
||||
Arc::new(LocalSessionManager::default()),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: Some(Duration::from_secs(15)),
|
||||
sse_retry: Some(Duration::from_secs(3)),
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default().with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ async fn spawn_server(
|
|||
#[tokio::test]
|
||||
async fn stateless_json_response_returns_application_json() -> anyhow::Result<()> {
|
||||
let ct = CancellationToken::new();
|
||||
let (client, url, ct) = spawn_server(StreamableHttpServerConfig {
|
||||
stateful_mode: false,
|
||||
json_response: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
})
|
||||
let (client, url, ct) = spawn_server(
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_stateful_mode(false)
|
||||
.with_json_response(true)
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
)
|
||||
.await;
|
||||
|
||||
let response = client
|
||||
|
|
@ -79,13 +79,12 @@ async fn stateless_json_response_returns_application_json() -> anyhow::Result<()
|
|||
#[tokio::test]
|
||||
async fn stateless_sse_mode_default_unchanged() -> anyhow::Result<()> {
|
||||
let ct = CancellationToken::new();
|
||||
let (client, url, ct) = spawn_server(StreamableHttpServerConfig {
|
||||
stateful_mode: false,
|
||||
json_response: false,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
})
|
||||
let (client, url, ct) = spawn_server(
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_stateful_mode(false)
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
)
|
||||
.await;
|
||||
|
||||
let response = client
|
||||
|
|
@ -122,13 +121,12 @@ async fn stateless_sse_mode_default_unchanged() -> anyhow::Result<()> {
|
|||
async fn json_response_ignored_in_stateful_mode() -> anyhow::Result<()> {
|
||||
let ct = CancellationToken::new();
|
||||
// json_response: true has no effect when stateful_mode: true — server still uses SSE
|
||||
let (client, url, ct) = spawn_server(StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
json_response: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
})
|
||||
let (client, url, ct) = spawn_server(
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_json_response(true)
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
)
|
||||
.await;
|
||||
|
||||
let response = client
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@ async fn test_priming_on_stream_start() -> anyhow::Result<()> {
|
|||
StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
Default::default(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
@ -87,12 +84,9 @@ async fn test_priming_on_stream_close() -> anyhow::Result<()> {
|
|||
let service = StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
session_manager.clone(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
|
|||
|
|
@ -32,12 +32,9 @@ async fn test_stale_session_id_returns_status_aware_error() -> anyhow::Result<()
|
|||
StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
Default::default(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
@ -103,12 +100,9 @@ async fn test_transparent_reinitialization_on_session_expiry() -> anyhow::Result
|
|||
let service = StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
session_manager.clone(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
@ -183,12 +177,9 @@ async fn test_session_expired_error_when_reinit_disabled() -> anyhow::Result<()>
|
|||
let service = StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
session_manager.clone(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clippy::exhaustive_structs)]
|
||||
//cargo test --test test_structured_output --features "client server macros"
|
||||
use rmcp::{
|
||||
Json, ServerHandler,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clippy::exhaustive_structs)]
|
||||
//cargo test --test test_tool_builder_methods --features "client server macros"
|
||||
use rmcp::model::{JsonObject, Tool};
|
||||
use schemars::JsonSchema;
|
||||
|
|
|
|||
|
|
@ -69,12 +69,9 @@ async fn test_with_js_streamable_http_client() -> anyhow::Result<()> {
|
|||
StreamableHttpService::new(
|
||||
|| Ok(Calculator::new()),
|
||||
Default::default(),
|
||||
StreamableHttpServerConfig {
|
||||
stateful_mode: true,
|
||||
sse_keep_alive: None,
|
||||
cancellation_token: ct.child_token(),
|
||||
..Default::default()
|
||||
},
|
||||
StreamableHttpServerConfig::default()
|
||||
.with_sse_keep_alive(None)
|
||||
.with_cancellation_token(ct.child_token()),
|
||||
);
|
||||
let router = axum::Router::new().nest_service("/mcp", service);
|
||||
let tcp_listener = tokio::net::TcpListener::bind(STREAMABLE_HTTP_BIND_ADDRESS).await?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue