fix(test): fix tool deserialization error (#68)
This commit is contained in:
parent
ac1b8c6ddb
commit
0eea9aad4a
14 changed files with 63 additions and 34 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/rmcp", "crates/rmcp-macros", "examples/*"]
|
members = ["crates/rmcp", "crates/rmcp-macros", "examples/*"]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rmcp-macros"
|
name = "rmcp-macros"
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ pub(crate) fn tool_fn_item(attr: TokenStream, mut input_fn: ItemFn) -> syn::Resu
|
||||||
#input_fn_vis fn #tool_attr_fn_ident() -> rmcp::model::Tool {
|
#input_fn_vis fn #tool_attr_fn_ident() -> rmcp::model::Tool {
|
||||||
rmcp::model::Tool {
|
rmcp::model::Tool {
|
||||||
name: #name.into(),
|
name: #name.into(),
|
||||||
description: #description.into(),
|
description: Some(#description.into()),
|
||||||
input_schema: #schema.into(),
|
input_schema: #schema.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rmcp"
|
name = "rmcp"
|
||||||
|
|
@ -95,5 +95,5 @@ path = "tests/test_with_python.rs"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "test_with_js"
|
name = "test_with_js"
|
||||||
required-features = ["server", "transport-sse-server"]
|
required-features = ["server", "client", "transport-sse-server", "transport-child-process"]
|
||||||
path = "tests/test_with_js.rs"
|
path = "tests/test_with_js.rs"
|
||||||
|
|
@ -14,7 +14,8 @@ pub struct Tool {
|
||||||
/// The name of the tool
|
/// The name of the tool
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
/// A description of what the tool does
|
/// A description of what the tool does
|
||||||
pub description: Cow<'static, str>,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<Cow<'static, str>>,
|
||||||
/// A JSON Schema object defining the expected parameters for the tool
|
/// A JSON Schema object defining the expected parameters for the tool
|
||||||
pub input_schema: Arc<JsonObject>,
|
pub input_schema: Arc<JsonObject>,
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +30,7 @@ impl Tool {
|
||||||
{
|
{
|
||||||
Tool {
|
Tool {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
description: description.into(),
|
description: Some(description.into()),
|
||||||
input_schema: input_schema.into(),
|
input_schema: input_schema.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
crates/rmcp/tests/test_deserialization.rs
Normal file
15
crates/rmcp/tests/test_deserialization.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
use rmcp::model::{JsonRpcResponse, ServerJsonRpcMessage, ServerResult};
|
||||||
|
#[test]
|
||||||
|
fn test_tool_list_result() {
|
||||||
|
let json = std::fs::read("tests/test_deserialization/tool_list_result.json").unwrap();
|
||||||
|
let result: ServerJsonRpcMessage = serde_json::from_slice(&json).unwrap();
|
||||||
|
println!("{result:#?}");
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
result,
|
||||||
|
ServerJsonRpcMessage::Response(JsonRpcResponse {
|
||||||
|
result: ServerResult::ListToolsResult(_),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
28
crates/rmcp/tests/test_deserialization/tool_list_result.json
Normal file
28
crates/rmcp/tests/test_deserialization/tool_list_result.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "add",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 2
|
||||||
|
}
|
||||||
|
|
@ -10,13 +10,13 @@ const BIND_ADDRESS: &str = "127.0.0.1:8000";
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_with_js_client() -> anyhow::Result<()> {
|
async fn test_with_js_client() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::registry()
|
let _ = tracing_subscriber::registry()
|
||||||
.with(
|
.with(
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
.unwrap_or_else(|_| "debug".to_string().into()),
|
.unwrap_or_else(|_| "debug".to_string().into()),
|
||||||
)
|
)
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.try_init();
|
||||||
tokio::process::Command::new("npm")
|
tokio::process::Command::new("npm")
|
||||||
.arg("install")
|
.arg("install")
|
||||||
.current_dir("tests/test_with_js")
|
.current_dir("tests/test_with_js")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "mcp-client-examples"
|
name = "mcp-client-examples"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rig-integration"
|
name = "rig-integration"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rig::tool::{ToolDyn as RigTool, ToolEmbeddingDyn, ToolSet};
|
use rig::tool::{ToolDyn as RigTool, ToolSet};
|
||||||
use rmcp::{
|
use rmcp::{
|
||||||
RoleClient,
|
RoleClient,
|
||||||
model::{CallToolRequestParam, CallToolResult, Tool as McpTool},
|
model::{CallToolRequestParam, CallToolResult, Tool as McpTool},
|
||||||
|
|
@ -24,7 +24,12 @@ impl RigTool for McpToolAdaptor {
|
||||||
{
|
{
|
||||||
Box::pin(std::future::ready(rig::completion::ToolDefinition {
|
Box::pin(std::future::ready(rig::completion::ToolDefinition {
|
||||||
name: self.name(),
|
name: self.name(),
|
||||||
description: self.tool.description.to_string(),
|
description: self
|
||||||
|
.tool
|
||||||
|
.description
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string(),
|
||||||
parameters: self.tool.schema_as_json_value(),
|
parameters: self.tool.schema_as_json_value(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -51,22 +56,6 @@ impl RigTool for McpToolAdaptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolEmbeddingDyn for McpToolAdaptor {
|
|
||||||
fn embedding_docs(&self) -> Vec<String> {
|
|
||||||
vec![
|
|
||||||
self.tool.description.clone().to_string(),
|
|
||||||
format!("Tool name: {}", self.tool.name),
|
|
||||||
format!("Tool capability: {}", self.tool.description),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context(&self) -> serde_json::Result<serde_json::Value> {
|
|
||||||
Ok(serde_json::json!({
|
|
||||||
"tool_name": self.tool.name,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct McpManager {
|
pub struct McpManager {
|
||||||
pub clients: HashMap<String, RunningService<RoleClient, ()>>,
|
pub clients: HashMap<String, RunningService<RoleClient, ()>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "mcp-server-examples"
|
name = "mcp-server-examples"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "transport"
|
name = "transport"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue