Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions conformance/src/bin/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ impl ClientHandler for ElicitationDefaultsClientHandler {
Ok(CreateElicitationResult {
action: ElicitationAction::Accept,
content,
meta: None,
})
}
}
Expand Down Expand Up @@ -174,6 +175,7 @@ impl ClientHandler for FullClientHandler {
Ok(CreateElicitationResult {
action: ElicitationAction::Accept,
content: Some(json!({"username": "testuser", "email": "test@example.com"})),
meta: None,
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/rmcp/src/handler/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,} => {
Expand All @@ -154,6 +155,7 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
/// Ok(CreateElicitationResult {
/// action: ElicitationAction::Accept,
/// content: None,
/// meta: None,
/// })
/// }
/// }
Expand All @@ -171,6 +173,7 @@ pub trait ClientHandler: Sized + Send + Sync + 'static {
std::future::ready(Ok(CreateElicitationResult {
action: ElicitationAction::Decline,
content: None,
meta: None,
}))
}

Expand Down
11 changes: 11 additions & 0 deletions crates/rmcp/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -2707,6 +2711,7 @@ impl CreateElicitationResult {
Self {
action,
content: None,
meta: None,
}
}

Expand All @@ -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
Expand Down
31 changes: 30 additions & 1 deletion crates/rmcp/tests/test_elicitation.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@
"content": {
"description": "The content returned by the tool (text, images, etc.)",
"type": "array",
"default": [],
"items": {
"$ref": "#/definitions/Annotated"
}
Expand All @@ -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`.",
Expand Down Expand Up @@ -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": [
Expand Down
Loading