chore: add #[non_exhaustive] and mutation methods to improve compatibility (#715)
* chore: add #[non_exhaustive] to reduce backwards-incompatible changes going forward * fix: remove ProtocolVersion import * fix: add a few more with_ mutator methods --------- Co-authored-by: Alex Hancock <alexhancock@block.xyz>
This commit is contained in:
parent
79834b6211
commit
f63718d202
9 changed files with 1568 additions and 1110 deletions
|
|
@ -49,10 +49,7 @@ impl Calculator {
|
|||
#[tool_handler]
|
||||
impl ServerHandler for Calculator {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
instructions: Some("A simple calculator".into()),
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
..Default::default()
|
||||
}
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_instructions("A simple calculator".to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,10 +171,10 @@ impl Counter {
|
|||
"This is an example prompt with your message here: '{}'",
|
||||
args.message
|
||||
);
|
||||
Ok(vec![PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: PromptMessageContent::text(prompt),
|
||||
}])
|
||||
Ok(vec![PromptMessage::new_text(
|
||||
PromptMessageRole::User,
|
||||
prompt,
|
||||
)])
|
||||
}
|
||||
|
||||
/// Analyze the current counter value and suggest next steps
|
||||
|
|
@ -202,13 +202,10 @@ impl Counter {
|
|||
),
|
||||
];
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some(format!(
|
||||
"Counter analysis for reaching {} from {}",
|
||||
args.goal, current_value
|
||||
)),
|
||||
messages,
|
||||
})
|
||||
Ok(GetPromptResult::new(messages).with_description(format!(
|
||||
"Counter analysis for reaching {} from {}",
|
||||
args.goal, current_value
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,16 +214,16 @@ impl Counter {
|
|||
#[task_handler]
|
||||
impl ServerHandler for Counter {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
protocol_version: ProtocolVersion::V_2024_11_05,
|
||||
capabilities: ServerCapabilities::builder()
|
||||
ServerInfo::new(
|
||||
ServerCapabilities::builder()
|
||||
.enable_prompts()
|
||||
.enable_resources()
|
||||
.enable_tools()
|
||||
.build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some("This server provides counter tools and prompts. Tools: increment, decrement, get_value, say_hello, echo, sum. Prompts: example_prompt (takes a message), counter_analysis (analyzes counter state with a goal).".to_string()),
|
||||
}
|
||||
)
|
||||
.with_server_info(Implementation::from_build_env())
|
||||
.with_protocol_version(ProtocolVersion::V_2024_11_05)
|
||||
.with_instructions("This server provides counter tools and prompts. Tools: increment, decrement, get_value, say_hello, echo, sum. Prompts: example_prompt (takes a message), counter_analysis (analyzes counter state with a goal).".to_string())
|
||||
}
|
||||
|
||||
async fn list_resources(
|
||||
|
|
@ -246,21 +243,24 @@ impl ServerHandler for Counter {
|
|||
|
||||
async fn read_resource(
|
||||
&self,
|
||||
ReadResourceRequestParams { meta: _, uri }: ReadResourceRequestParams,
|
||||
request: ReadResourceRequestParams,
|
||||
_: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, McpError> {
|
||||
let uri = &request.uri;
|
||||
match uri.as_str() {
|
||||
"str:////Users/to/some/path/" => {
|
||||
let cwd = "/Users/to/some/path/";
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![ResourceContents::text(cwd, uri)],
|
||||
})
|
||||
Ok(ReadResourceResult::new(vec![ResourceContents::text(
|
||||
cwd,
|
||||
uri.clone(),
|
||||
)]))
|
||||
}
|
||||
"memo://insights" => {
|
||||
let memo = "Business Intelligence Memo\n\nAnalysis has revealed 5 key insights ...";
|
||||
Ok(ReadResourceResult {
|
||||
contents: vec![ResourceContents::text(memo, uri)],
|
||||
})
|
||||
Ok(ReadResourceResult::new(vec![ResourceContents::text(
|
||||
memo,
|
||||
uri.clone(),
|
||||
)]))
|
||||
}
|
||||
_ => Err(McpError::resource_not_found(
|
||||
"resource_not_found",
|
||||
|
|
@ -364,12 +364,7 @@ mod tests {
|
|||
"source".into(),
|
||||
serde_json::Value::String("integration-test".into()),
|
||||
);
|
||||
let params = CallToolRequestParams {
|
||||
meta: None,
|
||||
name: "long_task".into(),
|
||||
arguments: None,
|
||||
task: Some(task_meta),
|
||||
};
|
||||
let params = CallToolRequestParams::new("long_task").with_task(Some(task_meta));
|
||||
let response = client_service
|
||||
.send_request(ClientRequest::CallToolRequest(Request::new(params.clone())))
|
||||
.await?;
|
||||
|
|
|
|||
|
|
@ -77,10 +77,7 @@ impl<DS: DataService> GenericService<DS> {
|
|||
#[tool_handler]
|
||||
impl<DS: DataService> ServerHandler for GenericService<DS> {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
instructions: Some("generic data service".into()),
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
..Default::default()
|
||||
}
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_instructions("generic data service".to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,15 +118,13 @@ impl ProgressDemo {
|
|||
#[tool_handler]
|
||||
impl ServerHandler for ProgressDemo {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
protocol_version: ProtocolVersion::V_2024_11_05,
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some(
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_protocol_version(ProtocolVersion::V_2024_11_05)
|
||||
.with_server_info(Implementation::from_build_env())
|
||||
.with_instructions(
|
||||
"This server demonstrates progress notifications during long-running operations. \
|
||||
Use the tools to see real-time progress updates for batch processing"
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -292,47 +292,41 @@ impl SqlQueryServer {
|
|||
]
|
||||
};
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some(format!(
|
||||
"SQL Query: {} on {}",
|
||||
if args.operation.is_empty() {
|
||||
"Unknown"
|
||||
} else {
|
||||
&args.operation
|
||||
},
|
||||
if args.table.is_empty() {
|
||||
"table"
|
||||
} else {
|
||||
&args.table
|
||||
}
|
||||
)),
|
||||
messages,
|
||||
})
|
||||
Ok(GetPromptResult::new(messages).with_description(format!(
|
||||
"SQL Query: {} on {}",
|
||||
if args.operation.is_empty() {
|
||||
"Unknown"
|
||||
} else {
|
||||
&args.operation
|
||||
},
|
||||
if args.table.is_empty() {
|
||||
"table"
|
||||
} else {
|
||||
&args.table
|
||||
}
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[prompt_handler]
|
||||
impl ServerHandler for SqlQueryServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
capabilities: ServerCapabilities::builder()
|
||||
ServerInfo::new(
|
||||
ServerCapabilities::builder()
|
||||
.enable_completions()
|
||||
.enable_prompts()
|
||||
.build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some(
|
||||
"Smart SQL query builder with progressive completion that adapts based on your choices:\n\n\
|
||||
Step 1: Choose operation type ('sel' → SELECT, 'ins' → INSERT, 'upd' → UPDATE, 'del' → DELETE)\n\
|
||||
Step 2: Specify table name ('users', 'orders', 'products')\n\
|
||||
Step 3: Add relevant fields based on operation type:\n\
|
||||
• SELECT/UPDATE: columns ('name', 'email', 'id')\n\
|
||||
• INSERT: values to insert\n\
|
||||
• All: optional WHERE clause\n\n\
|
||||
The completion adapts - only relevant fields appear based on your SQL operation!"
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
.with_instructions(
|
||||
"Smart SQL query builder with progressive completion that adapts based on your choices:\n\n\
|
||||
Step 1: Choose operation type ('sel' → SELECT, 'ins' → INSERT, 'upd' → UPDATE, 'del' → DELETE)\n\
|
||||
Step 2: Specify table name ('users', 'orders', 'products')\n\
|
||||
Step 3: Add relevant fields based on operation type:\n\
|
||||
• SELECT/UPDATE: columns ('name', 'email', 'id')\n\
|
||||
• INSERT: values to insert\n\
|
||||
• All: optional WHERE clause\n\n\
|
||||
The completion adapts - only relevant fields appear based on your SQL operation!",
|
||||
)
|
||||
}
|
||||
|
||||
async fn complete(
|
||||
|
|
@ -417,7 +411,7 @@ impl ServerHandler for SqlQueryServer {
|
|||
has_more: Some(false),
|
||||
};
|
||||
|
||||
Ok(CompleteResult { completion })
|
||||
Ok(CompleteResult::new(completion))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,14 +156,11 @@ impl ElicitationEnumFormServer {
|
|||
#[tool_handler]
|
||||
impl ServerHandler for ElicitationEnumFormServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some(
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_server_info(Implementation::from_build_env())
|
||||
.with_instructions(
|
||||
"Simple server demonstrating elicitation for enum selection".to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,14 +154,11 @@ impl ElicitationServer {
|
|||
#[tool_handler]
|
||||
impl ServerHandler for ElicitationServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some(
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_server_info(Implementation::from_build_env())
|
||||
.with_instructions(
|
||||
"Simple server demonstrating elicitation for user name collection".to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,14 +174,11 @@ impl PromptServer {
|
|||
),
|
||||
];
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some(format!(
|
||||
"Code review for {} file focusing on {}",
|
||||
args.language,
|
||||
focus_areas.join(", ")
|
||||
)),
|
||||
messages,
|
||||
})
|
||||
Ok(GetPromptResult::new(messages).with_description(format!(
|
||||
"Code review for {} file focusing on {}",
|
||||
args.language,
|
||||
focus_areas.join(", ")
|
||||
)))
|
||||
}
|
||||
|
||||
/// Data analysis prompt demonstrating context usage
|
||||
|
|
@ -270,13 +267,10 @@ impl PromptServer {
|
|||
));
|
||||
}
|
||||
|
||||
GetPromptResult {
|
||||
description: Some(format!(
|
||||
"Writing {} for {} audience with {} tone",
|
||||
args.content_type, args.audience, tone
|
||||
)),
|
||||
messages,
|
||||
}
|
||||
GetPromptResult::new(messages).with_description(format!(
|
||||
"Writing {} for {} audience with {} tone",
|
||||
args.content_type, args.audience, tone
|
||||
))
|
||||
}
|
||||
|
||||
/// Debug assistant demonstrating error handling patterns
|
||||
|
|
@ -332,14 +326,11 @@ impl PromptServer {
|
|||
"Let's debug this systematically. First, let me understand the error context better.",
|
||||
));
|
||||
|
||||
Ok(GetPromptResult {
|
||||
description: Some(format!(
|
||||
"Debugging {} error in {}",
|
||||
args.error_message.chars().take(50).collect::<String>(),
|
||||
args.stack.first().map(|s| s.as_str()).unwrap_or("unknown")
|
||||
)),
|
||||
messages,
|
||||
})
|
||||
Ok(GetPromptResult::new(messages).with_description(format!(
|
||||
"Debugging {} error in {}",
|
||||
args.error_message.chars().take(50).collect::<String>(),
|
||||
args.stack.first().map(|s| s.as_str()).unwrap_or("unknown")
|
||||
)))
|
||||
}
|
||||
|
||||
/// Learning path prompt that uses server state
|
||||
|
|
@ -376,17 +367,11 @@ impl PromptServer {
|
|||
#[prompt_handler]
|
||||
impl ServerHandler for PromptServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
capabilities: ServerCapabilities::builder().enable_prompts().build(),
|
||||
server_info: Implementation::from_build_env(),
|
||||
instructions: Some(
|
||||
"This server provides various prompt templates for code review, data analysis, \
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_prompts().build()).with_instructions(
|
||||
"This server provides various prompt templates for code review, data analysis, \
|
||||
writing assistance, debugging help, and personalized learning paths. \
|
||||
All prompts are designed to provide structured, context-aware assistance."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
All prompts are designed to provide structured, context-aware assistance.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,17 +18,12 @@ pub struct SamplingDemoServer;
|
|||
|
||||
impl ServerHandler for SamplingDemoServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo {
|
||||
instructions: Some(concat!(
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_instructions(concat!(
|
||||
"This is a demo server that requests sampling from clients. It provides tools that use LLM capabilities.\n\n",
|
||||
"IMPORTANT: This server requires a client that supports the 'sampling/createMessage' method. ",
|
||||
"Without sampling support, the tools will return errors."
|
||||
).into()),
|
||||
capabilities: ServerCapabilities::builder()
|
||||
.enable_tools()
|
||||
.build(),
|
||||
..Default::default()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
async fn call_tool(
|
||||
|
|
@ -48,27 +43,22 @@ impl ServerHandler for SamplingDemoServer {
|
|||
|
||||
let response = context
|
||||
.peer
|
||||
.create_message(CreateMessageRequestParams {
|
||||
meta: None,
|
||||
task: None,
|
||||
messages: vec![SamplingMessage::user_text(question)],
|
||||
model_preferences: Some(ModelPreferences {
|
||||
hints: Some(vec![ModelHint {
|
||||
name: Some("claude".to_string()),
|
||||
}]),
|
||||
cost_priority: Some(0.3),
|
||||
speed_priority: Some(0.8),
|
||||
intelligence_priority: Some(0.7),
|
||||
}),
|
||||
system_prompt: Some("You are a helpful assistant.".to_string()),
|
||||
include_context: Some(ContextInclusion::None),
|
||||
temperature: Some(0.7),
|
||||
max_tokens: 150,
|
||||
stop_sequences: None,
|
||||
metadata: None,
|
||||
tools: None,
|
||||
tool_choice: None,
|
||||
})
|
||||
.create_message(
|
||||
CreateMessageRequestParams::new(
|
||||
vec![SamplingMessage::user_text(question)],
|
||||
150,
|
||||
)
|
||||
.with_model_preferences(
|
||||
ModelPreferences::new()
|
||||
.with_hints(vec![ModelHint::new("claude")])
|
||||
.with_cost_priority(0.3)
|
||||
.with_speed_priority(0.8)
|
||||
.with_intelligence_priority(0.7),
|
||||
)
|
||||
.with_system_prompt("You are a helpful assistant.")
|
||||
.with_include_context(ContextInclusion::None)
|
||||
.with_temperature(0.7),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
ErrorData::new(
|
||||
|
|
@ -105,11 +95,10 @@ impl ServerHandler for SamplingDemoServer {
|
|||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListToolsResult, ErrorData> {
|
||||
Ok(ListToolsResult {
|
||||
tools: vec![Tool {
|
||||
name: "ask_llm".into(),
|
||||
title: None,
|
||||
description: Some("Ask a question to the LLM through sampling".into()),
|
||||
input_schema: Arc::new(
|
||||
tools: vec![Tool::new(
|
||||
"ask_llm",
|
||||
"Ask a question to the LLM through sampling",
|
||||
Arc::new(
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -122,12 +111,7 @@ impl ServerHandler for SamplingDemoServer {
|
|||
}))
|
||||
.unwrap(),
|
||||
),
|
||||
output_schema: None,
|
||||
annotations: None,
|
||||
execution: None,
|
||||
icons: None,
|
||||
meta: None,
|
||||
}],
|
||||
)],
|
||||
meta: None,
|
||||
next_cursor: None,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue