diff --git a/tests/from_mcp_integration.rs b/tests/from_mcp_integration.rs index b67e273..079cdc6 100644 --- a/tests/from_mcp_integration.rs +++ b/tests/from_mcp_integration.rs @@ -420,6 +420,37 @@ async fn import_refuses_tool_name_containing_slash() { } } +#[tokio::test] +async fn forwarding_handler_wraps_non_object_input_as_value_field() { + // A scalar/array tool argument cannot be a JSON object on the MCP + // wire; the adapter wraps it as {"value": } (review-002 + // from_mcp :460-468 arm). Proven over the real rmcp round trip: the + // echo server reflects the arguments object back. + let (endpoint, _handle) = spawn_server().await; + let adapter = FromMCP::new(endpoint, "echo"); + let bundles = adapter.import().await.expect("import succeeds"); + let echo = bundles + .into_iter() + .find(|b| b.spec.name == "echo/echo") + .expect("echo tool present"); + + let ctx = test_context("req-wrap", Capabilities::new()); + let response = match &echo.handler { + HandlerKind::Once(h) => h(serde_json::json!("bare-scalar"), ctx).await, + HandlerKind::Stream(_) | HandlerKind::Sink(_) => panic!("expected Once handler"), + }; + match response.result { + Ok(Value::Object(obj)) => { + assert_eq!( + obj.get("echoed"), + Some(&serde_json::json!({ "value": "bare-scalar" })), + "non-object input must reach the wire as {{\"value\": …}}: got {obj:?}" + ); + } + other => panic!("expected object structured content, got {other:?}"), + } +} + #[tokio::test] async fn forwarding_handler_maps_json_rpc_tool_error_with_code_fidelity() { // A server whose `call_tool` returns a JSON-RPC error (rmcp