fix: conformance syntax changes (#723)

This commit is contained in:
Alex Hancock 2026-03-05 09:07:21 -05:00 committed by GitHub
parent a8ea0f49b1
commit 770937a9fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 176 additions and 319 deletions

View file

@ -5,8 +5,7 @@ use rmcp::{
model::*, model::*,
service::RequestContext, service::RequestContext,
transport::{ transport::{
AuthClient, AuthorizationManager, StreamableHttpClientTransport, AuthClient, AuthorizationManager, StreamableHttpClientTransport, auth::OAuthState,
auth::{OAuthClientConfig, OAuthState},
streamable_http_client::StreamableHttpClientTransportConfig, streamable_http_client::StreamableHttpClientTransportConfig,
}, },
}; };
@ -17,9 +16,6 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[derive(Debug, Default, serde::Deserialize)] #[derive(Debug, Default, serde::Deserialize)]
struct ConformanceContext { struct ConformanceContext {
#[serde(default)]
name: Option<String>,
// pre-registration / client-credentials-basic
#[serde(default)] #[serde(default)]
client_id: Option<String>, client_id: Option<String>,
#[serde(default)] #[serde(default)]
@ -29,15 +25,6 @@ struct ConformanceContext {
private_key_pem: Option<String>, private_key_pem: Option<String>,
#[serde(default)] #[serde(default)]
signing_algorithm: Option<String>, signing_algorithm: Option<String>,
// cross-app-access
#[serde(default)]
idp_client_id: Option<String>,
#[serde(default)]
idp_id_token: Option<String>,
#[serde(default)]
idp_issuer: Option<String>,
#[serde(default)]
idp_token_endpoint: Option<String>,
} }
fn load_context() -> ConformanceContext { fn load_context() -> ConformanceContext {
@ -175,17 +162,17 @@ impl ClientHandler for FullClientHandler {
.and_then(|c| c.as_text()) .and_then(|c| c.as_text())
.map(|t| t.text.clone()) .map(|t| t.text.clone())
.unwrap_or_default(); .unwrap_or_default();
Ok(CreateMessageResult { Ok(CreateMessageResult::new(
message: SamplingMessage::new( SamplingMessage::new(
Role::Assistant, Role::Assistant,
SamplingMessageContent::text(format!( SamplingMessageContent::text(format!(
"This is a mock LLM response to: {}", "This is a mock LLM response to: {}",
prompt_text prompt_text
)), )),
), ),
model: "mock-model".into(), "mock-model".into(),
stop_reason: Some("endTurn".into()), )
}) .with_stop_reason("endTurn"))
} }
} }
@ -216,7 +203,7 @@ const REDIRECT_URI: &str = "http://localhost:3000/callback";
/// 4. Return an `AuthClient` wrapping `reqwest::Client` /// 4. Return an `AuthClient` wrapping `reqwest::Client`
async fn perform_oauth_flow( async fn perform_oauth_flow(
server_url: &str, server_url: &str,
ctx: &ConformanceContext, _ctx: &ConformanceContext,
) -> anyhow::Result<AuthClient<reqwest::Client>> { ) -> anyhow::Result<AuthClient<reqwest::Client>> {
let mut oauth = OAuthState::new(server_url, None).await?; let mut oauth = OAuthState::new(server_url, None).await?;
@ -335,12 +322,7 @@ async fn run_auth_client(server_url: &str, ctx: &ConformanceContext) -> anyhow::
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await; .await;
} }
@ -352,7 +334,7 @@ async fn run_auth_client(server_url: &str, ctx: &ConformanceContext) -> anyhow::
/// then call tool which triggers 403 → re-auth with expanded scopes → retry. /// then call tool which triggers 403 → re-auth with expanded scopes → retry.
async fn run_auth_scope_step_up_client( async fn run_auth_scope_step_up_client(
server_url: &str, server_url: &str,
ctx: &ConformanceContext, _ctx: &ConformanceContext,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
// First auth // First auth
let mut oauth = OAuthState::new(server_url, None).await?; let mut oauth = OAuthState::new(server_url, None).await?;
@ -388,12 +370,7 @@ async fn run_auth_scope_step_up_client(
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
match client match client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args.clone()))
meta: None,
name: tool.name.clone(),
arguments: args.clone(),
task: None,
})
.await .await
{ {
Ok(_) => { Ok(_) => {
@ -428,12 +405,7 @@ async fn run_auth_scope_step_up_client(
); );
let client2 = BasicClientHandler.serve(transport2).await?; let client2 = BasicClientHandler.serve(transport2).await?;
let _ = client2 let _ = client2
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await; .await;
client2.cancel().await.ok(); client2.cancel().await.ok();
return Ok(()); return Ok(());
@ -481,12 +453,7 @@ async fn run_auth_scope_retry_limit_client(
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
match client match client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await .await
{ {
Ok(_) => {} Ok(_) => {}
@ -539,12 +506,7 @@ async fn run_auth_preregistered_client(
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await; .await;
} }
client.cancel().await?; client.cancel().await?;
@ -597,12 +559,7 @@ async fn run_client_credentials_basic(
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await; .await;
} }
client.cancel().await?; client.cancel().await?;
@ -667,12 +624,7 @@ async fn run_client_credentials_jwt(
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await; .await;
} }
client.cancel().await?; client.cancel().await?;
@ -783,6 +735,18 @@ async fn headless_authorize(auth_url: &str) -> anyhow::Result<(String, String)>
Ok((code, state)) Ok((code, state))
} }
/// Build a `CallToolRequestParams` for a tool, optionally with arguments.
fn call_tool_params(
name: std::borrow::Cow<'static, str>,
arguments: Option<serde_json::Map<String, Value>>,
) -> CallToolRequestParams {
let mut p = CallToolRequestParams::new(name);
if let Some(a) = arguments {
p = p.with_arguments(a);
}
p
}
/// Build arguments for a tool based on its input schema. /// Build arguments for a tool based on its input schema.
fn build_tool_arguments(tool: &Tool) -> Option<serde_json::Map<String, Value>> { fn build_tool_arguments(tool: &Tool) -> Option<serde_json::Map<String, Value>> {
let schema = &tool.input_schema; let schema = &tool.input_schema;
@ -840,12 +804,7 @@ async fn run_tools_call_client(server_url: &str) -> anyhow::Result<()> {
for tool in &tools.tools { for tool in &tools.tools {
let args = build_tool_arguments(tool); let args = build_tool_arguments(tool);
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), args))
meta: None,
name: tool.name.clone(),
arguments: args,
task: None,
})
.await?; .await?;
} }
client.cancel().await?; client.cancel().await?;
@ -862,12 +821,7 @@ async fn run_elicitation_defaults_client(server_url: &str) -> anyhow::Result<()>
}); });
if let Some(tool) = test_tool { if let Some(tool) = test_tool {
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), None))
meta: None,
name: tool.name.clone(),
arguments: None,
task: None,
})
.await?; .await?;
} }
client.cancel().await?; client.cancel().await?;
@ -884,12 +838,7 @@ async fn run_sse_retry_client(server_url: &str) -> anyhow::Result<()> {
.find(|t| t.name.as_ref() == "test_reconnection") .find(|t| t.name.as_ref() == "test_reconnection")
{ {
let _ = client let _ = client
.call_tool(CallToolRequestParams { .call_tool(call_tool_params(tool.name.clone(), None))
meta: None,
name: tool.name.clone(),
arguments: None,
task: None,
})
.await?; .await?;
} }
client.cancel().await?; client.cancel().await?;

View file

@ -48,24 +48,16 @@ impl ServerHandler for ConformanceServer {
_cx: RequestContext<RoleServer>, _cx: RequestContext<RoleServer>,
) -> impl Future<Output = Result<InitializeResult, ErrorData>> + Send + '_ { ) -> impl Future<Output = Result<InitializeResult, ErrorData>> + Send + '_ {
async { async {
Ok(InitializeResult { Ok(InitializeResult::new(
server_info: Implementation { ServerCapabilities::builder()
name: "rust-conformance-server".into(),
title: None,
version: "0.1.0".into(),
description: None,
icons: None,
website_url: None,
},
capabilities: ServerCapabilities::builder()
.enable_prompts() .enable_prompts()
.enable_resources() .enable_resources()
.enable_tools() .enable_tools()
.enable_logging() .enable_logging()
.build(), .build(),
instructions: Some("Rust MCP conformance test server".into()), )
..Default::default() .with_server_info(Implementation::new("rust-conformance-server", "0.1.0"))
}) .with_instructions("Rust MCP conformance test server"))
} }
} }
@ -232,19 +224,14 @@ impl ServerHandler for ConformanceServer {
async move { async move {
let args = request.arguments.unwrap_or_default(); let args = request.arguments.unwrap_or_default();
match request.name.as_ref() { match request.name.as_ref() {
"test_simple_text" => Ok(CallToolResult { "test_simple_text" => Ok(CallToolResult::success(vec![Content::text(
content: vec![Content::text("This is a simple text response for testing.")], "This is a simple text response for testing.",
structured_content: None, )])),
is_error: None,
meta: None,
}),
"test_image_content" => Ok(CallToolResult { "test_image_content" => Ok(CallToolResult::success(vec![Content::image(
content: vec![Content::image(TEST_IMAGE_DATA, "image/png")], TEST_IMAGE_DATA,
structured_content: None, "image/png",
is_error: None, )])),
meta: None,
}),
"test_audio_content" => { "test_audio_content" => {
// No Content::audio() helper, construct manually // No Content::audio() helper, construct manually
@ -253,41 +240,28 @@ impl ServerHandler for ConformanceServer {
mime_type: "audio/wav".into(), mime_type: "audio/wav".into(),
}) })
.no_annotation(); .no_annotation();
Ok(CallToolResult { Ok(CallToolResult::success(vec![audio]))
content: vec![audio],
structured_content: None,
is_error: None,
meta: None,
})
} }
"test_embedded_resource" => Ok(CallToolResult { "test_embedded_resource" => Ok(CallToolResult::success(vec![Content::resource(
content: vec![Content::resource(ResourceContents::TextResourceContents { ResourceContents::TextResourceContents {
uri: "test://embedded-resource".into(), uri: "test://embedded-resource".into(),
mime_type: Some("text/plain".into()), mime_type: Some("text/plain".into()),
text: "This is an embedded resource content.".into(), text: "This is an embedded resource content.".into(),
meta: None, meta: None,
})], },
structured_content: None, )])),
is_error: None,
meta: None,
}),
"test_multiple_content_types" => Ok(CallToolResult { "test_multiple_content_types" => Ok(CallToolResult::success(vec![
content: vec![ Content::text("Multiple content types test:"),
Content::text("Multiple content types test:"), Content::image(TEST_IMAGE_DATA, "image/png"),
Content::image(TEST_IMAGE_DATA, "image/png"), Content::resource(ResourceContents::TextResourceContents {
Content::resource(ResourceContents::TextResourceContents { uri: "test://mixed-content-resource".into(),
uri: "test://mixed-content-resource".into(), mime_type: Some("application/json".into()),
mime_type: Some("application/json".into()), text: r#"{"test":"data","value":123}"#.into(),
text: r#"{"test":"data","value":123}"#.into(), meta: None,
meta: None, }),
}), ])),
],
structured_content: None,
is_error: None,
meta: None,
}),
"test_tool_with_logging" => { "test_tool_with_logging" => {
for msg in [ for msg in [
@ -306,22 +280,14 @@ impl ServerHandler for ConformanceServer {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
} }
Ok(CallToolResult { Ok(CallToolResult::success(vec![Content::text(
content: vec![Content::text("Logging test completed")], "Logging test completed",
structured_content: None, )]))
is_error: None,
meta: None,
})
} }
"test_error_handling" => Ok(CallToolResult { "test_error_handling" => Ok(CallToolResult::error(vec![Content::text(
content: vec![Content::text( "This tool intentionally returns an error for testing",
"This tool intentionally returns an error for testing", )])),
)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
"test_tool_with_progress" => { "test_tool_with_progress" => {
let progress_token = cx.meta.get_progress_token(); let progress_token = cx.meta.get_progress_token();
@ -343,12 +309,9 @@ impl ServerHandler for ConformanceServer {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
} }
Ok(CallToolResult { Ok(CallToolResult::success(vec![Content::text(
content: vec![Content::text("Progress test completed")], "Progress test completed",
structured_content: None, )]))
is_error: None,
meta: None,
})
} }
"test_sampling" => { "test_sampling" => {
@ -359,20 +322,10 @@ impl ServerHandler for ConformanceServer {
match cx match cx
.peer .peer
.create_message(CreateMessageRequestParams { .create_message(CreateMessageRequestParams::new(
meta: None, vec![SamplingMessage::user_text(prompt)],
task: None, 100,
messages: vec![SamplingMessage::user_text(prompt)], ))
max_tokens: 100,
model_preferences: None,
system_prompt: None,
include_context: None,
temperature: None,
stop_sequences: None,
metadata: None,
tools: None,
tool_choice: None,
})
.await .await
{ {
Ok(result) => { Ok(result) => {
@ -383,19 +336,15 @@ impl ServerHandler for ConformanceServer {
.and_then(|c| c.as_text()) .and_then(|c| c.as_text())
.map(|t| t.text.clone()) .map(|t| t.text.clone())
.unwrap_or_else(|| "No text response".into()); .unwrap_or_else(|| "No text response".into());
Ok(CallToolResult { Ok(CallToolResult::success(vec![Content::text(format!(
content: vec![Content::text(format!("LLM response: {}", text))], "LLM response: {}",
structured_content: None, text
is_error: None, ))]))
meta: None,
})
} }
Err(e) => Ok(CallToolResult { Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
content: vec![Content::text(format!("Sampling error: {}", e))], "Sampling error: {}",
structured_content: None, e
is_error: Some(true), ))])),
meta: None,
}),
} }
} }
@ -431,26 +380,19 @@ impl ServerHandler for ConformanceServer {
}) })
.await .await
{ {
Ok(result) => Ok(CallToolResult { Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
content: vec![Content::text(format!( "User response: action={}, content={:?}",
"User response: action={}, content={:?}", match result.action {
match result.action { ElicitationAction::Accept => "accept",
ElicitationAction::Accept => "accept", ElicitationAction::Decline => "decline",
ElicitationAction::Decline => "decline", ElicitationAction::Cancel => "cancel",
ElicitationAction::Cancel => "cancel", },
}, result.content
result.content ))])),
))], Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
structured_content: None, "Elicitation error: {}",
is_error: None, e
meta: None, ))])),
}),
Err(e) => Ok(CallToolResult {
content: vec![Content::text(format!("Elicitation error: {}", e))],
structured_content: None,
is_error: Some(true),
meta: None,
}),
} }
} }
@ -498,26 +440,19 @@ impl ServerHandler for ConformanceServer {
}) })
.await .await
{ {
Ok(result) => Ok(CallToolResult { Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
content: vec![Content::text(format!( "Elicitation completed: action={}, content={:?}",
"Elicitation completed: action={}, content={:?}", match result.action {
match result.action { ElicitationAction::Accept => "accept",
ElicitationAction::Accept => "accept", ElicitationAction::Decline => "decline",
ElicitationAction::Decline => "decline", ElicitationAction::Cancel => "cancel",
ElicitationAction::Cancel => "cancel", },
}, result.content
result.content ))])),
))], Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
structured_content: None, "Elicitation error: {}",
is_error: None, e
meta: None, ))])),
}),
Err(e) => Ok(CallToolResult {
content: vec![Content::text(format!("Elicitation error: {}", e))],
structured_content: None,
is_error: Some(true),
meta: None,
}),
} }
} }
@ -573,46 +508,34 @@ impl ServerHandler for ConformanceServer {
}) })
.await .await
{ {
Ok(result) => Ok(CallToolResult { Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
content: vec![Content::text(format!( "Enum elicitation completed: action={}",
"Enum elicitation completed: action={}", match result.action {
match result.action { ElicitationAction::Accept => "accept",
ElicitationAction::Accept => "accept", ElicitationAction::Decline => "decline",
ElicitationAction::Decline => "decline", ElicitationAction::Cancel => "cancel",
ElicitationAction::Cancel => "cancel", }
} ))])),
))], Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
structured_content: None, "Elicitation error: {}",
is_error: None, e
meta: None, ))])),
}),
Err(e) => Ok(CallToolResult {
content: vec![Content::text(format!("Elicitation error: {}", e))],
structured_content: None,
is_error: Some(true),
meta: None,
}),
} }
} }
"json_schema_2020_12_tool" => { "json_schema_2020_12_tool" => {
let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("world"); let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("world");
Ok(CallToolResult { Ok(CallToolResult::success(vec![Content::text(format!(
content: vec![Content::text(format!("Hello, {}!", name))], "Hello, {}!",
structured_content: None, name
is_error: None, ))]))
meta: None,
})
} }
"test_reconnection" => { "test_reconnection" => {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(CallToolResult { Ok(CallToolResult::success(vec![Content::text(
content: vec![Content::text("Reconnection test completed")], "Reconnection test completed",
structured_content: None, )]))
is_error: None,
meta: None,
})
} }
_ => Err(ErrorData::invalid_params( _ => Err(ErrorData::invalid_params(
@ -668,22 +591,22 @@ impl ServerHandler for ConformanceServer {
async move { async move {
let uri = request.uri.as_str(); let uri = request.uri.as_str();
match uri { match uri {
"test://static-text" => Ok(ReadResourceResult { "test://static-text" => Ok(ReadResourceResult::new(vec![
contents: vec![ResourceContents::TextResourceContents { ResourceContents::TextResourceContents {
uri: uri.into(), uri: uri.into(),
mime_type: Some("text/plain".into()), mime_type: Some("text/plain".into()),
text: "This is the content of the static text resource.".into(), text: "This is the content of the static text resource.".into(),
meta: None, meta: None,
}], },
}), ])),
"test://static-binary" => Ok(ReadResourceResult { "test://static-binary" => Ok(ReadResourceResult::new(vec![
contents: vec![ResourceContents::BlobResourceContents { ResourceContents::BlobResourceContents {
uri: uri.into(), uri: uri.into(),
mime_type: Some("image/png".into()), mime_type: Some("image/png".into()),
blob: TEST_IMAGE_DATA.into(), blob: TEST_IMAGE_DATA.into(),
meta: None, meta: None,
}], },
}), ])),
_ => { _ => {
// Check if it matches template: test://template/{id}/data // Check if it matches template: test://template/{id}/data
if uri.starts_with("test://template/") && uri.ends_with("/data") { if uri.starts_with("test://template/") && uri.ends_with("/data") {
@ -691,8 +614,8 @@ impl ServerHandler for ConformanceServer {
.strip_prefix("test://template/") .strip_prefix("test://template/")
.and_then(|s| s.strip_suffix("/data")) .and_then(|s| s.strip_suffix("/data"))
.unwrap_or("unknown"); .unwrap_or("unknown");
Ok(ReadResourceResult { Ok(ReadResourceResult::new(vec![
contents: vec![ResourceContents::TextResourceContents { ResourceContents::TextResourceContents {
uri: uri.into(), uri: uri.into(),
mime_type: Some("application/json".into()), mime_type: Some("application/json".into()),
text: format!( text: format!(
@ -700,8 +623,8 @@ impl ServerHandler for ConformanceServer {
id, id id, id
), ),
meta: None, meta: None,
}], },
}) ]))
} else { } else {
Err(ErrorData::resource_not_found( Err(ErrorData::resource_not_found(
format!("Resource not found: {}", uri), format!("Resource not found: {}", uri),
@ -779,18 +702,12 @@ impl ServerHandler for ConformanceServer {
"test_prompt_with_arguments", "test_prompt_with_arguments",
Some("A test prompt that accepts arguments"), Some("A test prompt that accepts arguments"),
Some(vec![ Some(vec![
PromptArgument { PromptArgument::new("name")
name: "name".into(), .with_description("The name to greet")
title: None, .with_required(true),
description: Some("The name to greet".into()), PromptArgument::new("style")
required: Some(true), .with_description("The greeting style")
}, .with_required(false),
PromptArgument {
name: "style".into(),
title: None,
description: Some("The greeting style".into()),
required: Some(false),
},
]), ]),
), ),
Prompt::new( Prompt::new(
@ -816,13 +733,11 @@ impl ServerHandler for ConformanceServer {
) -> impl Future<Output = Result<GetPromptResult, ErrorData>> + Send + '_ { ) -> impl Future<Output = Result<GetPromptResult, ErrorData>> + Send + '_ {
async move { async move {
match request.name.as_str() { match request.name.as_str() {
"test_simple_prompt" => Ok(GetPromptResult { "test_simple_prompt" => Ok(GetPromptResult::new(vec![PromptMessage::new_text(
description: Some("A simple test prompt".into()), PromptMessageRole::User,
messages: vec![PromptMessage::new_text( "This is a simple test prompt.",
PromptMessageRole::User, )])
"This is a simple test prompt.", .with_description("A simple test prompt")),
)],
}),
"test_prompt_with_arguments" => { "test_prompt_with_arguments" => {
let args = request.arguments.unwrap_or_default(); let args = request.arguments.unwrap_or_default();
let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("World"); let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("World");
@ -830,47 +745,41 @@ impl ServerHandler for ConformanceServer {
.get("style") .get("style")
.and_then(|v| v.as_str()) .and_then(|v| v.as_str())
.unwrap_or("friendly"); .unwrap_or("friendly");
Ok(GetPromptResult { Ok(GetPromptResult::new(vec![PromptMessage::new_text(
description: Some("A prompt with arguments".into()), PromptMessageRole::User,
messages: vec![PromptMessage::new_text( format!("Please greet {} in a {} style.", name, style),
PromptMessageRole::User, )])
format!("Please greet {} in a {} style.", name, style), .with_description("A prompt with arguments"))
)],
})
} }
"test_prompt_with_embedded_resource" => Ok(GetPromptResult { "test_prompt_with_embedded_resource" => Ok(GetPromptResult::new(vec![
description: Some("A prompt with an embedded resource".into()), PromptMessage::new_text(PromptMessageRole::User, "Here is a resource:"),
messages: vec![ PromptMessage::new_resource(
PromptMessage::new_text(PromptMessageRole::User, "Here is a resource:"), PromptMessageRole::User,
PromptMessage::new_resource( "test://static-text".into(),
PromptMessageRole::User, Some("text/plain".into()),
"test://static-text".into(), Some("Resource content for prompt".into()),
Some("text/plain".into()), None,
Some("Resource content for prompt".into()), None,
None, None,
None, ),
None, ])
), .with_description("A prompt with an embedded resource")),
],
}),
"test_prompt_with_image" => { "test_prompt_with_image" => {
let image_content = RawImageContent { let image_content = RawImageContent {
data: TEST_IMAGE_DATA.into(), data: TEST_IMAGE_DATA.into(),
mime_type: "image/png".into(), mime_type: "image/png".into(),
meta: None, meta: None,
}; };
Ok(GetPromptResult { Ok(GetPromptResult::new(vec![
description: Some("A prompt with an image".into()), PromptMessage::new_text(PromptMessageRole::User, "Here is an image:"),
messages: vec![ PromptMessage::new(
PromptMessage::new_text(PromptMessageRole::User, "Here is an image:"), PromptMessageRole::User,
PromptMessage { PromptMessageContent::Image {
role: PromptMessageRole::User, image: image_content.no_annotation(),
content: PromptMessageContent::Image {
image: image_content.no_annotation(),
},
}, },
], ),
}) ])
.with_description("A prompt with an image"))
} }
_ => Err(ErrorData::invalid_params( _ => Err(ErrorData::invalid_params(
format!("Unknown prompt: {}", request.name), format!("Unknown prompt: {}", request.name),
@ -904,10 +813,9 @@ impl ServerHandler for ConformanceServer {
} }
} }
}; };
Ok(CompleteResult { Ok(CompleteResult::new(
completion: CompletionInfo::new(values) CompletionInfo::new(values).map_err(|e| ErrorData::internal_error(e, None))?,
.map_err(|e| ErrorData::internal_error(e, None))?, ))
})
} }
} }