fix: conformance syntax changes (#723)
This commit is contained in:
parent
a8ea0f49b1
commit
770937a9fe
2 changed files with 176 additions and 319 deletions
|
|
@ -5,8 +5,7 @@ use rmcp::{
|
|||
model::*,
|
||||
service::RequestContext,
|
||||
transport::{
|
||||
AuthClient, AuthorizationManager, StreamableHttpClientTransport,
|
||||
auth::{OAuthClientConfig, OAuthState},
|
||||
AuthClient, AuthorizationManager, StreamableHttpClientTransport, auth::OAuthState,
|
||||
streamable_http_client::StreamableHttpClientTransportConfig,
|
||||
},
|
||||
};
|
||||
|
|
@ -17,9 +16,6 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|||
|
||||
#[derive(Debug, Default, serde::Deserialize)]
|
||||
struct ConformanceContext {
|
||||
#[serde(default)]
|
||||
name: Option<String>,
|
||||
// pre-registration / client-credentials-basic
|
||||
#[serde(default)]
|
||||
client_id: Option<String>,
|
||||
#[serde(default)]
|
||||
|
|
@ -29,15 +25,6 @@ struct ConformanceContext {
|
|||
private_key_pem: Option<String>,
|
||||
#[serde(default)]
|
||||
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 {
|
||||
|
|
@ -175,17 +162,17 @@ impl ClientHandler for FullClientHandler {
|
|||
.and_then(|c| c.as_text())
|
||||
.map(|t| t.text.clone())
|
||||
.unwrap_or_default();
|
||||
Ok(CreateMessageResult {
|
||||
message: SamplingMessage::new(
|
||||
Ok(CreateMessageResult::new(
|
||||
SamplingMessage::new(
|
||||
Role::Assistant,
|
||||
SamplingMessageContent::text(format!(
|
||||
"This is a mock LLM response to: {}",
|
||||
prompt_text
|
||||
)),
|
||||
),
|
||||
model: "mock-model".into(),
|
||||
stop_reason: Some("endTurn".into()),
|
||||
})
|
||||
"mock-model".into(),
|
||||
)
|
||||
.with_stop_reason("endTurn"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +203,7 @@ const REDIRECT_URI: &str = "http://localhost:3000/callback";
|
|||
/// 4. Return an `AuthClient` wrapping `reqwest::Client`
|
||||
async fn perform_oauth_flow(
|
||||
server_url: &str,
|
||||
ctx: &ConformanceContext,
|
||||
_ctx: &ConformanceContext,
|
||||
) -> anyhow::Result<AuthClient<reqwest::Client>> {
|
||||
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 {
|
||||
let args = build_tool_arguments(tool);
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.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.
|
||||
async fn run_auth_scope_step_up_client(
|
||||
server_url: &str,
|
||||
ctx: &ConformanceContext,
|
||||
_ctx: &ConformanceContext,
|
||||
) -> anyhow::Result<()> {
|
||||
// First auth
|
||||
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 {
|
||||
let args = build_tool_arguments(tool);
|
||||
match client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args.clone(),
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args.clone()))
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
|
|
@ -428,12 +405,7 @@ async fn run_auth_scope_step_up_client(
|
|||
);
|
||||
let client2 = BasicClientHandler.serve(transport2).await?;
|
||||
let _ = client2
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.await;
|
||||
client2.cancel().await.ok();
|
||||
return Ok(());
|
||||
|
|
@ -481,12 +453,7 @@ async fn run_auth_scope_retry_limit_client(
|
|||
for tool in &tools.tools {
|
||||
let args = build_tool_arguments(tool);
|
||||
match client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
|
|
@ -539,12 +506,7 @@ async fn run_auth_preregistered_client(
|
|||
for tool in &tools.tools {
|
||||
let args = build_tool_arguments(tool);
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.await;
|
||||
}
|
||||
client.cancel().await?;
|
||||
|
|
@ -597,12 +559,7 @@ async fn run_client_credentials_basic(
|
|||
for tool in &tools.tools {
|
||||
let args = build_tool_arguments(tool);
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.await;
|
||||
}
|
||||
client.cancel().await?;
|
||||
|
|
@ -667,12 +624,7 @@ async fn run_client_credentials_jwt(
|
|||
for tool in &tools.tools {
|
||||
let args = build_tool_arguments(tool);
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.await;
|
||||
}
|
||||
client.cancel().await?;
|
||||
|
|
@ -783,6 +735,18 @@ async fn headless_authorize(auth_url: &str) -> anyhow::Result<(String, String)>
|
|||
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.
|
||||
fn build_tool_arguments(tool: &Tool) -> Option<serde_json::Map<String, Value>> {
|
||||
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 {
|
||||
let args = build_tool_arguments(tool);
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: args,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), args))
|
||||
.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 {
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: None,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), None))
|
||||
.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")
|
||||
{
|
||||
let _ = client
|
||||
.call_tool(CallToolRequestParams {
|
||||
meta: None,
|
||||
name: tool.name.clone(),
|
||||
arguments: None,
|
||||
task: None,
|
||||
})
|
||||
.call_tool(call_tool_params(tool.name.clone(), None))
|
||||
.await?;
|
||||
}
|
||||
client.cancel().await?;
|
||||
|
|
|
|||
|
|
@ -48,24 +48,16 @@ impl ServerHandler for ConformanceServer {
|
|||
_cx: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<InitializeResult, ErrorData>> + Send + '_ {
|
||||
async {
|
||||
Ok(InitializeResult {
|
||||
server_info: Implementation {
|
||||
name: "rust-conformance-server".into(),
|
||||
title: None,
|
||||
version: "0.1.0".into(),
|
||||
description: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder()
|
||||
Ok(InitializeResult::new(
|
||||
ServerCapabilities::builder()
|
||||
.enable_prompts()
|
||||
.enable_resources()
|
||||
.enable_tools()
|
||||
.enable_logging()
|
||||
.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 {
|
||||
let args = request.arguments.unwrap_or_default();
|
||||
match request.name.as_ref() {
|
||||
"test_simple_text" => Ok(CallToolResult {
|
||||
content: vec![Content::text("This is a simple text response for testing.")],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
"test_simple_text" => Ok(CallToolResult::success(vec![Content::text(
|
||||
"This is a simple text response for testing.",
|
||||
)])),
|
||||
|
||||
"test_image_content" => Ok(CallToolResult {
|
||||
content: vec![Content::image(TEST_IMAGE_DATA, "image/png")],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
"test_image_content" => Ok(CallToolResult::success(vec![Content::image(
|
||||
TEST_IMAGE_DATA,
|
||||
"image/png",
|
||||
)])),
|
||||
|
||||
"test_audio_content" => {
|
||||
// No Content::audio() helper, construct manually
|
||||
|
|
@ -253,41 +240,28 @@ impl ServerHandler for ConformanceServer {
|
|||
mime_type: "audio/wav".into(),
|
||||
})
|
||||
.no_annotation();
|
||||
Ok(CallToolResult {
|
||||
content: vec![audio],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![audio]))
|
||||
}
|
||||
|
||||
"test_embedded_resource" => Ok(CallToolResult {
|
||||
content: vec![Content::resource(ResourceContents::TextResourceContents {
|
||||
"test_embedded_resource" => Ok(CallToolResult::success(vec![Content::resource(
|
||||
ResourceContents::TextResourceContents {
|
||||
uri: "test://embedded-resource".into(),
|
||||
mime_type: Some("text/plain".into()),
|
||||
text: "This is an embedded resource content.".into(),
|
||||
meta: None,
|
||||
})],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
},
|
||||
)])),
|
||||
|
||||
"test_multiple_content_types" => Ok(CallToolResult {
|
||||
content: vec![
|
||||
Content::text("Multiple content types test:"),
|
||||
Content::image(TEST_IMAGE_DATA, "image/png"),
|
||||
Content::resource(ResourceContents::TextResourceContents {
|
||||
uri: "test://mixed-content-resource".into(),
|
||||
mime_type: Some("application/json".into()),
|
||||
text: r#"{"test":"data","value":123}"#.into(),
|
||||
meta: None,
|
||||
}),
|
||||
],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
"test_multiple_content_types" => Ok(CallToolResult::success(vec![
|
||||
Content::text("Multiple content types test:"),
|
||||
Content::image(TEST_IMAGE_DATA, "image/png"),
|
||||
Content::resource(ResourceContents::TextResourceContents {
|
||||
uri: "test://mixed-content-resource".into(),
|
||||
mime_type: Some("application/json".into()),
|
||||
text: r#"{"test":"data","value":123}"#.into(),
|
||||
meta: None,
|
||||
}),
|
||||
])),
|
||||
|
||||
"test_tool_with_logging" => {
|
||||
for msg in [
|
||||
|
|
@ -306,22 +280,14 @@ impl ServerHandler for ConformanceServer {
|
|||
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Logging test completed")],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![Content::text(
|
||||
"Logging test completed",
|
||||
)]))
|
||||
}
|
||||
|
||||
"test_error_handling" => Ok(CallToolResult {
|
||||
content: vec![Content::text(
|
||||
"This tool intentionally returns an error for testing",
|
||||
)],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: None,
|
||||
}),
|
||||
"test_error_handling" => Ok(CallToolResult::error(vec![Content::text(
|
||||
"This tool intentionally returns an error for testing",
|
||||
)])),
|
||||
|
||||
"test_tool_with_progress" => {
|
||||
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;
|
||||
}
|
||||
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Progress test completed")],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![Content::text(
|
||||
"Progress test completed",
|
||||
)]))
|
||||
}
|
||||
|
||||
"test_sampling" => {
|
||||
|
|
@ -359,20 +322,10 @@ impl ServerHandler for ConformanceServer {
|
|||
|
||||
match cx
|
||||
.peer
|
||||
.create_message(CreateMessageRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
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,
|
||||
})
|
||||
.create_message(CreateMessageRequestParams::new(
|
||||
vec![SamplingMessage::user_text(prompt)],
|
||||
100,
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(result) => {
|
||||
|
|
@ -383,19 +336,15 @@ impl ServerHandler for ConformanceServer {
|
|||
.and_then(|c| c.as_text())
|
||||
.map(|t| t.text.clone())
|
||||
.unwrap_or_else(|| "No text response".into());
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("LLM response: {}", text))],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![Content::text(format!(
|
||||
"LLM response: {}",
|
||||
text
|
||||
))]))
|
||||
}
|
||||
Err(e) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("Sampling error: {}", e))],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: None,
|
||||
}),
|
||||
Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
|
||||
"Sampling error: {}",
|
||||
e
|
||||
))])),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -431,26 +380,19 @@ impl ServerHandler for ConformanceServer {
|
|||
})
|
||||
.await
|
||||
{
|
||||
Ok(result) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!(
|
||||
"User response: action={}, content={:?}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
},
|
||||
result.content
|
||||
))],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
Err(e) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("Elicitation error: {}", e))],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
|
||||
"User response: action={}, content={:?}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
},
|
||||
result.content
|
||||
))])),
|
||||
Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
|
||||
"Elicitation error: {}",
|
||||
e
|
||||
))])),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -498,26 +440,19 @@ impl ServerHandler for ConformanceServer {
|
|||
})
|
||||
.await
|
||||
{
|
||||
Ok(result) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!(
|
||||
"Elicitation completed: action={}, content={:?}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
},
|
||||
result.content
|
||||
))],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
Err(e) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("Elicitation error: {}", e))],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
|
||||
"Elicitation completed: action={}, content={:?}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
},
|
||||
result.content
|
||||
))])),
|
||||
Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
|
||||
"Elicitation error: {}",
|
||||
e
|
||||
))])),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -573,46 +508,34 @@ impl ServerHandler for ConformanceServer {
|
|||
})
|
||||
.await
|
||||
{
|
||||
Ok(result) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!(
|
||||
"Enum elicitation completed: action={}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
}
|
||||
))],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
}),
|
||||
Err(e) => Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("Elicitation error: {}", e))],
|
||||
structured_content: None,
|
||||
is_error: Some(true),
|
||||
meta: None,
|
||||
}),
|
||||
Ok(result) => Ok(CallToolResult::success(vec![Content::text(format!(
|
||||
"Enum elicitation completed: action={}",
|
||||
match result.action {
|
||||
ElicitationAction::Accept => "accept",
|
||||
ElicitationAction::Decline => "decline",
|
||||
ElicitationAction::Cancel => "cancel",
|
||||
}
|
||||
))])),
|
||||
Err(e) => Ok(CallToolResult::error(vec![Content::text(format!(
|
||||
"Elicitation error: {}",
|
||||
e
|
||||
))])),
|
||||
}
|
||||
}
|
||||
|
||||
"json_schema_2020_12_tool" => {
|
||||
let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("world");
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text(format!("Hello, {}!", name))],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![Content::text(format!(
|
||||
"Hello, {}!",
|
||||
name
|
||||
))]))
|
||||
}
|
||||
|
||||
"test_reconnection" => {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
Ok(CallToolResult {
|
||||
content: vec![Content::text("Reconnection test completed")],
|
||||
structured_content: None,
|
||||
is_error: None,
|
||||
meta: None,
|
||||
})
|
||||
Ok(CallToolResult::success(vec![Content::text(
|
||||
"Reconnection test completed",
|
||||
)]))
|
||||
}
|
||||
|
||||
_ => Err(ErrorData::invalid_params(
|
||||
|
|
@ -668,22 +591,22 @@ impl ServerHandler for ConformanceServer {
|
|||
async move {
|
||||
let uri = request.uri.as_str();
|
||||
match uri {
|
||||
"test://static-text" => Ok(ReadResourceResult {
|
||||
contents: vec![ResourceContents::TextResourceContents {
|
||||
"test://static-text" => Ok(ReadResourceResult::new(vec![
|
||||
ResourceContents::TextResourceContents {
|
||||
uri: uri.into(),
|
||||
mime_type: Some("text/plain".into()),
|
||||
text: "This is the content of the static text resource.".into(),
|
||||
meta: None,
|
||||
}],
|
||||
}),
|
||||
"test://static-binary" => Ok(ReadResourceResult {
|
||||
contents: vec![ResourceContents::BlobResourceContents {
|
||||
},
|
||||
])),
|
||||
"test://static-binary" => Ok(ReadResourceResult::new(vec![
|
||||
ResourceContents::BlobResourceContents {
|
||||
uri: uri.into(),
|
||||
mime_type: Some("image/png".into()),
|
||||
blob: TEST_IMAGE_DATA.into(),
|
||||
meta: None,
|
||||
}],
|
||||
}),
|
||||
},
|
||||
])),
|
||||
_ => {
|
||||
// Check if it matches template: test://template/{id}/data
|
||||
if uri.starts_with("test://template/") && uri.ends_with("/data") {
|
||||
|
|
@ -691,8 +614,8 @@ impl ServerHandler for ConformanceServer {
|
|||
.strip_prefix("test://template/")
|
||||
.and_then(|s| s.strip_suffix("/data"))
|
||||
.unwrap_or("unknown");
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![ResourceContents::TextResourceContents {
|
||||
Ok(ReadResourceResult::new(vec![
|
||||
ResourceContents::TextResourceContents {
|
||||
uri: uri.into(),
|
||||
mime_type: Some("application/json".into()),
|
||||
text: format!(
|
||||
|
|
@ -700,8 +623,8 @@ impl ServerHandler for ConformanceServer {
|
|||
id, id
|
||||
),
|
||||
meta: None,
|
||||
}],
|
||||
})
|
||||
},
|
||||
]))
|
||||
} else {
|
||||
Err(ErrorData::resource_not_found(
|
||||
format!("Resource not found: {}", uri),
|
||||
|
|
@ -779,18 +702,12 @@ impl ServerHandler for ConformanceServer {
|
|||
"test_prompt_with_arguments",
|
||||
Some("A test prompt that accepts arguments"),
|
||||
Some(vec![
|
||||
PromptArgument {
|
||||
name: "name".into(),
|
||||
title: None,
|
||||
description: Some("The name to greet".into()),
|
||||
required: Some(true),
|
||||
},
|
||||
PromptArgument {
|
||||
name: "style".into(),
|
||||
title: None,
|
||||
description: Some("The greeting style".into()),
|
||||
required: Some(false),
|
||||
},
|
||||
PromptArgument::new("name")
|
||||
.with_description("The name to greet")
|
||||
.with_required(true),
|
||||
PromptArgument::new("style")
|
||||
.with_description("The greeting style")
|
||||
.with_required(false),
|
||||
]),
|
||||
),
|
||||
Prompt::new(
|
||||
|
|
@ -816,13 +733,11 @@ impl ServerHandler for ConformanceServer {
|
|||
) -> impl Future<Output = Result<GetPromptResult, ErrorData>> + Send + '_ {
|
||||
async move {
|
||||
match request.name.as_str() {
|
||||
"test_simple_prompt" => Ok(GetPromptResult {
|
||||
description: Some("A simple test prompt".into()),
|
||||
messages: vec![PromptMessage::new_text(
|
||||
PromptMessageRole::User,
|
||||
"This is a simple test prompt.",
|
||||
)],
|
||||
}),
|
||||
"test_simple_prompt" => Ok(GetPromptResult::new(vec![PromptMessage::new_text(
|
||||
PromptMessageRole::User,
|
||||
"This is a simple test prompt.",
|
||||
)])
|
||||
.with_description("A simple test prompt")),
|
||||
"test_prompt_with_arguments" => {
|
||||
let args = request.arguments.unwrap_or_default();
|
||||
let name = args.get("name").and_then(|v| v.as_str()).unwrap_or("World");
|
||||
|
|
@ -830,47 +745,41 @@ impl ServerHandler for ConformanceServer {
|
|||
.get("style")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("friendly");
|
||||
Ok(GetPromptResult {
|
||||
description: Some("A prompt with arguments".into()),
|
||||
messages: vec![PromptMessage::new_text(
|
||||
PromptMessageRole::User,
|
||||
format!("Please greet {} in a {} style.", name, style),
|
||||
)],
|
||||
})
|
||||
Ok(GetPromptResult::new(vec![PromptMessage::new_text(
|
||||
PromptMessageRole::User,
|
||||
format!("Please greet {} in a {} style.", name, style),
|
||||
)])
|
||||
.with_description("A prompt with arguments"))
|
||||
}
|
||||
"test_prompt_with_embedded_resource" => Ok(GetPromptResult {
|
||||
description: Some("A prompt with an embedded resource".into()),
|
||||
messages: vec![
|
||||
PromptMessage::new_text(PromptMessageRole::User, "Here is a resource:"),
|
||||
PromptMessage::new_resource(
|
||||
PromptMessageRole::User,
|
||||
"test://static-text".into(),
|
||||
Some("text/plain".into()),
|
||||
Some("Resource content for prompt".into()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
],
|
||||
}),
|
||||
"test_prompt_with_embedded_resource" => Ok(GetPromptResult::new(vec![
|
||||
PromptMessage::new_text(PromptMessageRole::User, "Here is a resource:"),
|
||||
PromptMessage::new_resource(
|
||||
PromptMessageRole::User,
|
||||
"test://static-text".into(),
|
||||
Some("text/plain".into()),
|
||||
Some("Resource content for prompt".into()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
])
|
||||
.with_description("A prompt with an embedded resource")),
|
||||
"test_prompt_with_image" => {
|
||||
let image_content = RawImageContent {
|
||||
data: TEST_IMAGE_DATA.into(),
|
||||
mime_type: "image/png".into(),
|
||||
meta: None,
|
||||
};
|
||||
Ok(GetPromptResult {
|
||||
description: Some("A prompt with an image".into()),
|
||||
messages: vec![
|
||||
PromptMessage::new_text(PromptMessageRole::User, "Here is an image:"),
|
||||
PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: PromptMessageContent::Image {
|
||||
image: image_content.no_annotation(),
|
||||
},
|
||||
Ok(GetPromptResult::new(vec![
|
||||
PromptMessage::new_text(PromptMessageRole::User, "Here is an image:"),
|
||||
PromptMessage::new(
|
||||
PromptMessageRole::User,
|
||||
PromptMessageContent::Image {
|
||||
image: image_content.no_annotation(),
|
||||
},
|
||||
],
|
||||
})
|
||||
),
|
||||
])
|
||||
.with_description("A prompt with an image"))
|
||||
}
|
||||
_ => Err(ErrorData::invalid_params(
|
||||
format!("Unknown prompt: {}", request.name),
|
||||
|
|
@ -904,10 +813,9 @@ impl ServerHandler for ConformanceServer {
|
|||
}
|
||||
}
|
||||
};
|
||||
Ok(CompleteResult {
|
||||
completion: CompletionInfo::new(values)
|
||||
.map_err(|e| ErrorData::internal_error(e, None))?,
|
||||
})
|
||||
Ok(CompleteResult::new(
|
||||
CompletionInfo::new(values).map_err(|e| ErrorData::internal_error(e, None))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue