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:
Jack Amadeo 2026-03-03 10:38:01 -05:00 • committed by GitHub
commit f63718d202
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1568 additions and 1110 deletions

View file

@ -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())
}
}

View file

@ -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?;

View file

@ -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())
}
}

View file

@ -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(),
),
}
)
}
}

View file

@ -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))
}
}

View file

@ -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()
}
)
}
}

View file

@ -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()
}
)
}
}

View file

@ -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.",
)
}
}

View file

@ -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,
})