#![cfg(not(target_arch = "wasm32"))]
use async_trait::async_trait;
use pmcp::types::protocol::RequestMeta;
use pmcp::types::{ClientCapabilities, Content};
use pmcp::{Client, RequestHandlerExtra, Result, Server, ToolHandler};
use serde_json::{json, Value};
#[path = "common/duplex.rs"]
mod duplex;
use duplex::DuplexTransport;
struct EchoMetaTool;
#[async_trait]
impl ToolHandler for EchoMetaTool {
async fn handle(&self, _args: Value, extra: RequestHandlerExtra) -> Result<Value> {
Ok(extra.request_meta.clone().unwrap_or(Value::Null))
}
}
fn echo_server() -> Server {
Server::builder()
.name("request-meta-echo")
.version("1.0.0")
.tool("echo_meta", EchoMetaTool)
.build()
.expect("server builds")
}
fn text_of(result: &pmcp::types::CallToolResult) -> String {
result
.content
.iter()
.filter_map(|c| match c {
Content::Text { text } => Some(text.as_str()),
_ => None,
})
.collect::<String>()
}
#[tokio::test]
async fn call_tool_with_meta_forwards_custom_key_to_handler() {
let (client_t, server_t) = DuplexTransport::pair();
let server = echo_server();
tokio::spawn(async move {
let _ = server.run(server_t).await;
});
let mut client = Client::new(client_t);
client
.initialize(ClientCapabilities::default())
.await
.expect("client initializes");
let meta = RequestMeta::new().with_meta("x-pmcp-team-depth", json!(3));
let result = client
.call_tool_with_meta("echo_meta".to_string(), json!({}), meta)
.await
.expect("call_tool_with_meta succeeds");
let observed = text_of(&result);
assert!(
observed.contains("x-pmcp-team-depth"),
"custom _meta key not observed server-side: {observed}"
);
assert!(
observed.contains('3'),
"custom _meta value not observed server-side: {observed}"
);
}
#[tokio::test]
async fn call_tool_with_meta_empty_behaves_like_plain_call() {
let (client_t, server_t) = DuplexTransport::pair();
let server = echo_server();
tokio::spawn(async move {
let _ = server.run(server_t).await;
});
let mut client = Client::new(client_t);
client
.initialize(ClientCapabilities::default())
.await
.expect("client initializes");
let result = client
.call_tool_with_meta("echo_meta".to_string(), json!({}), RequestMeta::new())
.await
.expect("call_tool_with_meta with empty meta succeeds");
let observed = text_of(&result);
assert!(
!observed.contains("x-pmcp-team-depth"),
"no custom key should be present for empty meta: {observed}"
);
}