feat: add meta to elicitation results (#792)
This commit is contained in:
parent
be321a4abe
commit
5f432834a1
8 changed files with 80 additions and 5 deletions
|
|
@ -122,6 +122,7 @@ impl ClientHandler for ElicitationDefaultsClientHandler {
|
|||
Ok(CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content,
|
||||
meta: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -174,6 +175,7 @@ impl ClientHandler for FullClientHandler {
|
|||
Ok(CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content: Some(json!({"username": "testuser", "email": "test@example.com"})),
|
||||
meta: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
|
|||
/// Ok(CreateElicitationResult {
|
||||
/// action: ElicitationAction::Accept,
|
||||
/// content: Some(user_input),
|
||||
/// meta: None,
|
||||
/// })
|
||||
/// }
|
||||
/// CreateElicitationRequestParam::UrlElicitationParam {meta, message, url, elicitation_id,} => {
|
||||
|
|
@ -154,6 +155,7 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
|
|||
/// Ok(CreateElicitationResult {
|
||||
/// action: ElicitationAction::Accept,
|
||||
/// content: None,
|
||||
/// meta: None,
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
|
|
@ -171,6 +173,7 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
|
|||
std::future::ready(Ok(CreateElicitationResult {
|
||||
action: ElicitationAction::Decline,
|
||||
content: None,
|
||||
meta: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2699,6 +2699,10 @@ pub struct CreateElicitationResult {
|
|||
/// Only present when action is Accept.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub content: Option<Value>,
|
||||
|
||||
/// Optional protocol-level metadata for this result.
|
||||
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
|
||||
pub meta: Option<Meta>,
|
||||
}
|
||||
|
||||
impl CreateElicitationResult {
|
||||
|
|
@ -2707,6 +2711,7 @@ impl CreateElicitationResult {
|
|||
Self {
|
||||
action,
|
||||
content: None,
|
||||
meta: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2715,6 +2720,12 @@ impl CreateElicitationResult {
|
|||
self.content = Some(content);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the metadata on this result.
|
||||
pub fn with_meta(mut self, meta: Meta) -> Self {
|
||||
self.meta = Some(meta);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for creating an elicitation to gather user input
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//cargo test --test test_elicitation --features "client server"
|
||||
|
||||
use rmcp::{model::*, service::*};
|
||||
use rmcp::{model::*, object, service::*};
|
||||
// For typed elicitation tests
|
||||
#[cfg(feature = "schemars")]
|
||||
use schemars::JsonSchema;
|
||||
|
|
@ -98,6 +98,7 @@ async fn test_elicitation_result_serialization() {
|
|||
let accept_result = CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content: Some(json!({"email": "user@example.com"})),
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&accept_result).unwrap();
|
||||
|
|
@ -111,6 +112,7 @@ async fn test_elicitation_result_serialization() {
|
|||
let decline_result = CreateElicitationResult {
|
||||
action: ElicitationAction::Decline,
|
||||
content: None,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&decline_result).unwrap();
|
||||
|
|
@ -124,6 +126,26 @@ async fn test_elicitation_result_serialization() {
|
|||
let deserialized: CreateElicitationResult = serde_json::from_value(expected).unwrap();
|
||||
assert_eq!(deserialized.action, ElicitationAction::Decline);
|
||||
assert_eq!(deserialized.content, None);
|
||||
assert_eq!(deserialized.meta, None);
|
||||
|
||||
// Test protocol-level metadata round-trips as _meta.
|
||||
let meta_result =
|
||||
CreateElicitationResult::new(ElicitationAction::Accept).with_meta(Meta(object!({
|
||||
"traceId": "elicitation-123"
|
||||
})));
|
||||
|
||||
let json = serde_json::to_value(&meta_result).unwrap();
|
||||
let expected = json!({
|
||||
"action": "accept",
|
||||
"_meta": {"traceId": "elicitation-123"}
|
||||
});
|
||||
assert_eq!(json, expected);
|
||||
|
||||
let deserialized: CreateElicitationResult = serde_json::from_value(expected).unwrap();
|
||||
assert_eq!(
|
||||
deserialized.meta,
|
||||
Some(Meta(object!({ "traceId": "elicitation-123" })))
|
||||
);
|
||||
}
|
||||
|
||||
/// Test that elicitation requests can be created and handled through the JSON-RPC protocol
|
||||
|
|
@ -843,6 +865,7 @@ async fn test_elicitation_direction_server_to_client() {
|
|||
let client_result = ClientResult::CreateElicitationResult(CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content: Some(json!("John Doe")),
|
||||
meta: None,
|
||||
});
|
||||
|
||||
// Verify client result can be serialized
|
||||
|
|
@ -893,6 +916,7 @@ async fn test_elicitation_json_rpc_direction() {
|
|||
ClientResult::CreateElicitationResult(CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content: Some(json!(true)),
|
||||
meta: None,
|
||||
}),
|
||||
RequestId::Number(1),
|
||||
);
|
||||
|
|
@ -928,6 +952,7 @@ async fn test_elicitation_actions_compliance() {
|
|||
ElicitationAction::Accept => Some(serde_json::json!("some data")),
|
||||
_ => None,
|
||||
},
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&result).unwrap();
|
||||
|
|
@ -958,6 +983,7 @@ async fn test_elicitation_result_in_client_result() {
|
|||
let result = ClientResult::CreateElicitationResult(CreateElicitationResult {
|
||||
action: ElicitationAction::Decline,
|
||||
content: None,
|
||||
meta: None,
|
||||
});
|
||||
|
||||
match result {
|
||||
|
|
@ -2161,6 +2187,7 @@ async fn test_url_elicitation_action_workflow() {
|
|||
let accept_result = CreateElicitationResult {
|
||||
action: ElicitationAction::Accept,
|
||||
content: None, // URL elicitation doesn't return content, just confirmation
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&accept_result).unwrap();
|
||||
|
|
@ -2172,6 +2199,7 @@ async fn test_url_elicitation_action_workflow() {
|
|||
let decline_result = CreateElicitationResult {
|
||||
action: ElicitationAction::Decline,
|
||||
content: None,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&decline_result).unwrap();
|
||||
|
|
@ -2181,6 +2209,7 @@ async fn test_url_elicitation_action_workflow() {
|
|||
let cancel_result = CreateElicitationResult {
|
||||
action: ElicitationAction::Cancel,
|
||||
content: None,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&cancel_result).unwrap();
|
||||
|
|
|
|||
|
|
@ -418,6 +418,14 @@
|
|||
"description": "The result returned by a client in response to an elicitation request.\n\nContains the user's decision (accept/decline/cancel) and optionally their input data\nif they chose to accept the request.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_meta": {
|
||||
"description": "Optional protocol-level metadata for this result.",
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"action": {
|
||||
"description": "The user's decision on how to handle the elicitation request",
|
||||
"allOf": [
|
||||
|
|
|
|||
|
|
@ -418,6 +418,14 @@
|
|||
"description": "The result returned by a client in response to an elicitation request.\n\nContains the user's decision (accept/decline/cancel) and optionally their input data\nif they chose to accept the request.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_meta": {
|
||||
"description": "Optional protocol-level metadata for this result.",
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"action": {
|
||||
"description": "The user's decision on how to handle the elicitation request",
|
||||
"allOf": [
|
||||
|
|
|
|||
|
|
@ -655,6 +655,14 @@
|
|||
"description": "The result returned by a client in response to an elicitation request.\n\nContains the user's decision (accept/decline/cancel) and optionally their input data\nif they chose to accept the request.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_meta": {
|
||||
"description": "Optional protocol-level metadata for this result.",
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"action": {
|
||||
"description": "The user's decision on how to handle the elicitation request",
|
||||
"allOf": [
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@
|
|||
"content": {
|
||||
"description": "The content returned by the tool (text, images, etc.)",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Annotated"
|
||||
}
|
||||
|
|
@ -402,10 +403,7 @@
|
|||
"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`.",
|
||||
|
|
@ -657,6 +655,14 @@
|
|||
"description": "The result returned by a client in response to an elicitation request.\n\nContains the user's decision (accept/decline/cancel) and optionally their input data\nif they chose to accept the request.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_meta": {
|
||||
"description": "Optional protocol-level metadata for this result.",
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"action": {
|
||||
"description": "The user's decision on how to handle the elicitation request",
|
||||
"allOf": [
|
||||
|
|
|
|||
Loading…
Reference in a new issue