Skip to content

Commit f63718d

Browse files
jamadeoalexhancock
andauthored
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>
1 parent 79834b6 commit f63718d

63 files changed

Lines changed: 1568 additions & 1110 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/rmcp-macros/src/prompt.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ impl ResolvedPromptAttribute {
4242
meta,
4343
} = self;
4444
let description = if let Some(description) = description {
45-
quote! { Some(#description.into()) }
45+
quote! { Some::<String>(#description.into()) }
4646
} else {
47-
quote! { None }
47+
quote! { None::<String> }
4848
};
4949
let title = if let Some(title) = title {
5050
quote! { Some(#title.into()) }
@@ -63,14 +63,14 @@ impl ResolvedPromptAttribute {
6363
};
6464
let tokens = quote! {
6565
pub fn #fn_ident() -> rmcp::model::Prompt {
66-
rmcp::model::Prompt {
67-
name: #name.into(),
68-
description: #description,
69-
arguments: #arguments,
70-
title: #title,
71-
icons: #icons,
72-
meta: #meta,
73-
}
66+
rmcp::model::Prompt::from_raw(
67+
#name,
68+
#description,
69+
#arguments,
70+
)
71+
.with_title(#title)
72+
.with_icons(#icons)
73+
.with_meta(#meta)
7474
}
7575
};
7676
syn::parse2::<ImplItemFn>(tokens)

crates/rmcp-macros/src/task_handler.rs

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,16 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
4242
.into_iter()
4343
.map(|task_id| {
4444
let timestamp = rmcp::task_manager::current_timestamp();
45-
rmcp::model::Task {
45+
rmcp::model::Task::new(
4646
task_id,
47-
status: rmcp::model::TaskStatus::Working,
48-
status_message: None,
49-
created_at: timestamp.clone(),
50-
last_updated_at: timestamp,
51-
ttl: None,
52-
poll_interval: None,
53-
}
47+
rmcp::model::TaskStatus::Working,
48+
timestamp.clone(),
49+
timestamp,
50+
)
5451
})
5552
.collect::<Vec<_>>();
5653

57-
Ok(rmcp::model::ListTasksResult {
58-
tasks,
59-
next_cursor: None,
60-
total: Some(total),
61-
})
54+
Ok(rmcp::model::ListTasksResult::new(tasks))
6255
}
6356
};
6457
item_impl.items.push(syn::parse2::<ImplItem>(list_fn)?);
@@ -106,17 +99,14 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
10699
))?;
107100

108101
let timestamp = current_timestamp();
109-
let task = rmcp::model::Task {
102+
let task = rmcp::model::Task::new(
110103
task_id,
111-
status: rmcp::model::TaskStatus::Working,
112-
status_message: Some("Task accepted".to_string()),
113-
created_at: timestamp.clone(),
114-
last_updated_at: timestamp,
115-
ttl: None,
116-
poll_interval: None,
117-
};
104+
rmcp::model::TaskStatus::Working,
105+
timestamp.clone(),
106+
timestamp,
107+
).with_status_message("Task accepted");
118108

119-
Ok(rmcp::model::CreateTaskResult { task })
109+
Ok(rmcp::model::CreateTaskResult::new(task))
120110
}
121111
};
122112
item_impl.items.push(syn::parse2::<ImplItem>(enqueue_fn)?);
@@ -151,31 +141,28 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
151141
Err(_) => rmcp::model::TaskStatus::Failed,
152142
};
153143
let timestamp = current_timestamp();
154-
let task = rmcp::model::Task {
144+
let mut task = rmcp::model::Task::new(
155145
task_id,
156146
status,
157-
status_message: None,
158-
created_at: timestamp.clone(),
159-
last_updated_at: timestamp,
160-
ttl: completed_result.descriptor.ttl,
161-
poll_interval: None,
162-
};
147+
timestamp.clone(),
148+
timestamp,
149+
);
150+
if let Some(ttl) = completed_result.descriptor.ttl {
151+
task = task.with_ttl(ttl);
152+
}
163153
return Ok(rmcp::model::GetTaskResult { meta: None, task });
164154
}
165155

166156
// If not completed, check running
167157
let running = processor.list_running();
168158
if running.into_iter().any(|id| id == task_id) {
169159
let timestamp = current_timestamp();
170-
let task = rmcp::model::Task {
160+
let task = rmcp::model::Task::new(
171161
task_id,
172-
status: rmcp::model::TaskStatus::Working,
173-
status_message: None,
174-
created_at: timestamp.clone(),
175-
last_updated_at: timestamp,
176-
ttl: None,
177-
poll_interval: None,
178-
};
162+
rmcp::model::TaskStatus::Working,
163+
timestamp.clone(),
164+
timestamp,
165+
);
179166
return Ok(rmcp::model::GetTaskResult { meta: None, task });
180167
}
181168

@@ -207,7 +194,7 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
207194
match &tool.result {
208195
Ok(call_tool) => {
209196
let value = ::serde_json::to_value(call_tool).unwrap_or(::serde_json::Value::Null);
210-
return Ok(rmcp::model::GetTaskPayloadResult(value));
197+
return Ok(rmcp::model::GetTaskPayloadResult::new(value));
211198
}
212199
Err(err) => return Err(McpError::internal_error(
213200
format!("task failed: {}", err),
@@ -254,15 +241,12 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
254241

255242
if processor.cancel_task(&task_id) {
256243
let timestamp = current_timestamp();
257-
let task = rmcp::model::Task {
244+
let task = rmcp::model::Task::new(
258245
task_id,
259-
status: rmcp::model::TaskStatus::Cancelled,
260-
status_message: None,
261-
created_at: timestamp.clone(),
262-
last_updated_at: timestamp,
263-
ttl: None,
264-
poll_interval: None,
265-
};
246+
rmcp::model::TaskStatus::Cancelled,
247+
timestamp.clone(),
248+
timestamp,
249+
);
266250
return Ok(rmcp::model::CancelTaskResult { meta: None, task });
267251
}
268252

crates/rmcp-macros/src/tool.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,17 @@ impl ResolvedToolAttribute {
159159
let tokens = quote! {
160160
#doc_attr
161161
pub fn #fn_ident() -> rmcp::model::Tool {
162-
rmcp::model::Tool {
163-
name: #name.into(),
164-
title: #title,
165-
description: #description,
166-
input_schema: #input_schema,
167-
output_schema: #output_schema,
168-
annotations: #annotations,
169-
execution: #execution,
170-
icons: #icons,
171-
meta: #meta,
172-
}
162+
rmcp::model::Tool::new_with_raw(
163+
#name,
164+
#description,
165+
#input_schema,
166+
)
167+
.with_title(#title)
168+
.with_raw_output_schema(#output_schema)
169+
.with_annotations(#annotations)
170+
.with_execution(#execution)
171+
.with_icons(#icons)
172+
.with_meta(#meta)
173173
}
174174
};
175175
syn::parse2::<ImplItemFn>(tokens)
@@ -260,13 +260,13 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
260260
let idempotent_hint = wrap_option(idempotent_hint);
261261
let open_world_hint = wrap_option(open_world_hint);
262262
let token_stream = quote! {
263-
Some(rmcp::model::ToolAnnotations {
264-
title: #title,
265-
read_only_hint: #read_only_hint,
266-
destructive_hint: #destructive_hint,
267-
idempotent_hint: #idempotent_hint,
268-
open_world_hint: #open_world_hint,
269-
})
263+
Some(rmcp::model::ToolAnnotations::from_raw(
264+
#title,
265+
#read_only_hint,
266+
#destructive_hint,
267+
#idempotent_hint,
268+
#open_world_hint,
269+
))
270270
};
271271
syn::parse2::<Expr>(token_stream)?
272272
} else {
@@ -296,9 +296,9 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
296296
};
297297

298298
let token_stream = quote! {
299-
Some(rmcp::model::ToolExecution {
300-
task_support: #task_support_expr,
301-
})
299+
Some(rmcp::model::ToolExecution::from_raw(
300+
#task_support_expr,
301+
))
302302
};
303303
syn::parse2::<Expr>(token_stream)?
304304
} else {

crates/rmcp/README.md

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
Creating a server with tools is simple using the `#[tool]` macro:
2121

22-
```rust,no_run
22+
```rust,ignore
2323
use rmcp::{
2424
ServerHandler, ServiceExt,
2525
handler::server::tool::ToolRouter,
@@ -68,11 +68,8 @@ impl Counter {
6868
#[tool_handler]
6969
impl ServerHandler for Counter {
7070
fn get_info(&self) -> ServerInfo {
71-
ServerInfo {
72-
instructions: Some("A simple counter that tallies the number of times the increment tool has been used".into()),
73-
capabilities: ServerCapabilities::builder().enable_tools().build(),
74-
..Default::default()
75-
}
71+
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
72+
.with_instructions("A simple counter that tallies the number of times the increment tool has been used")
7673
}
7774
}
7875
@@ -147,7 +144,7 @@ To expose task support, enable the `tasks` capability when building `ServerCapab
147144

148145
Creating a client to interact with a server:
149146

150-
```rust,no_run
147+
```rust,ignore
151148
use rmcp::{
152149
ServiceExt,
153150
model::CallToolRequestParams,
@@ -176,12 +173,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
176173
177174
// Call a tool
178175
let result = service
179-
.call_tool(CallToolRequestParams {
180-
meta: None,
181-
name: "git_status".into(),
182-
arguments: serde_json::json!({ "repo_path": "." }).as_object().cloned(),
183-
task: None,
184-
})
176+
.call_tool(
177+
CallToolRequestParams::new("git_status")
178+
.with_arguments(serde_json::json!({ "repo_path": "." }).as_object().cloned().unwrap_or_default())
179+
)
185180
.await?;
186181
println!("Result: {result:#?}");
187182

crates/rmcp/src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ impl std::error::Error for ErrorData {}
2020
/// This is an unified error type for the errors could be returned by the service.
2121
#[derive(Debug, thiserror::Error)]
2222
#[allow(clippy::large_enum_variant)]
23+
#[non_exhaustive]
2324
pub enum RmcpError {
2425
#[cfg(any(feature = "client", feature = "server"))]
2526
#[error("Service error: {0}")]

0 commit comments

Comments
 (0)