Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions crates/rmcp-macros/src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ impl ResolvedPromptAttribute {
meta,
} = self;
let description = if let Some(description) = description {
quote! { Some(#description.into()) }
quote! { Some::<String>(#description.into()) }
} else {
quote! { None }
quote! { None::<String> }
};
let title = if let Some(title) = title {
quote! { Some(#title.into()) }
Expand All @@ -63,14 +63,14 @@ impl ResolvedPromptAttribute {
};
let tokens = quote! {
pub fn #fn_ident() -> rmcp::model::Prompt {
rmcp::model::Prompt {
name: #name.into(),
description: #description,
arguments: #arguments,
title: #title,
icons: #icons,
meta: #meta,
}
rmcp::model::Prompt::from_raw(
#name,
#description,
#arguments,
)
.with_title(#title)
.with_icons(#icons)
.with_meta(#meta)
}
};
syn::parse2::<ImplItemFn>(tokens)
Expand Down
76 changes: 30 additions & 46 deletions crates/rmcp-macros/src/task_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,16 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
.into_iter()
.map(|task_id| {
let timestamp = rmcp::task_manager::current_timestamp();
rmcp::model::Task {
rmcp::model::Task::new(
task_id,
status: rmcp::model::TaskStatus::Working,
status_message: None,
created_at: timestamp.clone(),
last_updated_at: timestamp,
ttl: None,
poll_interval: None,
}
rmcp::model::TaskStatus::Working,
timestamp.clone(),
timestamp,
)
})
.collect::<Vec<_>>();

Ok(rmcp::model::ListTasksResult {
tasks,
next_cursor: None,
total: Some(total),
})
Ok(rmcp::model::ListTasksResult::new(tasks))
}
};
item_impl.items.push(syn::parse2::<ImplItem>(list_fn)?);
Expand Down Expand Up @@ -106,17 +99,14 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
))?;

let timestamp = current_timestamp();
let task = rmcp::model::Task {
let task = rmcp::model::Task::new(
task_id,
status: rmcp::model::TaskStatus::Working,
status_message: Some("Task accepted".to_string()),
created_at: timestamp.clone(),
last_updated_at: timestamp,
ttl: None,
poll_interval: None,
};
rmcp::model::TaskStatus::Working,
timestamp.clone(),
timestamp,
).with_status_message("Task accepted");

Ok(rmcp::model::CreateTaskResult { task })
Ok(rmcp::model::CreateTaskResult::new(task))
}
};
item_impl.items.push(syn::parse2::<ImplItem>(enqueue_fn)?);
Expand Down Expand Up @@ -151,31 +141,28 @@ pub fn task_handler(attr: TokenStream, input: TokenStream) -> syn::Result<TokenS
Err(_) => rmcp::model::TaskStatus::Failed,
};
let timestamp = current_timestamp();
let task = rmcp::model::Task {
let mut task = rmcp::model::Task::new(
task_id,
status,
status_message: None,
created_at: timestamp.clone(),
last_updated_at: timestamp,
ttl: completed_result.descriptor.ttl,
poll_interval: None,
};
timestamp.clone(),
timestamp,
);
if let Some(ttl) = completed_result.descriptor.ttl {
task = task.with_ttl(ttl);
}
return Ok(rmcp::model::GetTaskResult { meta: None, task });
}

// If not completed, check running
let running = processor.list_running();
if running.into_iter().any(|id| id == task_id) {
let timestamp = current_timestamp();
let task = rmcp::model::Task {
let task = rmcp::model::Task::new(
task_id,
status: rmcp::model::TaskStatus::Working,
status_message: None,
created_at: timestamp.clone(),
last_updated_at: timestamp,
ttl: None,
poll_interval: None,
};
rmcp::model::TaskStatus::Working,
timestamp.clone(),
timestamp,
);
return Ok(rmcp::model::GetTaskResult { meta: None, task });
}

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

if processor.cancel_task(&task_id) {
let timestamp = current_timestamp();
let task = rmcp::model::Task {
let task = rmcp::model::Task::new(
task_id,
status: rmcp::model::TaskStatus::Cancelled,
status_message: None,
created_at: timestamp.clone(),
last_updated_at: timestamp,
ttl: None,
poll_interval: None,
};
rmcp::model::TaskStatus::Cancelled,
timestamp.clone(),
timestamp,
);
return Ok(rmcp::model::CancelTaskResult { meta: None, task });
}

Expand Down
42 changes: 21 additions & 21 deletions crates/rmcp-macros/src/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,17 @@ impl ResolvedToolAttribute {
let tokens = quote! {
#doc_attr
pub fn #fn_ident() -> rmcp::model::Tool {
rmcp::model::Tool {
name: #name.into(),
title: #title,
description: #description,
input_schema: #input_schema,
output_schema: #output_schema,
annotations: #annotations,
execution: #execution,
icons: #icons,
meta: #meta,
}
rmcp::model::Tool::new_with_raw(
#name,
#description,
#input_schema,
)
.with_title(#title)
.with_raw_output_schema(#output_schema)
.with_annotations(#annotations)
.with_execution(#execution)
.with_icons(#icons)
.with_meta(#meta)
}
};
syn::parse2::<ImplItemFn>(tokens)
Expand Down Expand Up @@ -260,13 +260,13 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
let idempotent_hint = wrap_option(idempotent_hint);
let open_world_hint = wrap_option(open_world_hint);
let token_stream = quote! {
Some(rmcp::model::ToolAnnotations {
title: #title,
read_only_hint: #read_only_hint,
destructive_hint: #destructive_hint,
idempotent_hint: #idempotent_hint,
open_world_hint: #open_world_hint,
})
Some(rmcp::model::ToolAnnotations::from_raw(
#title,
#read_only_hint,
#destructive_hint,
#idempotent_hint,
#open_world_hint,
))
};
syn::parse2::<Expr>(token_stream)?
} else {
Expand Down Expand Up @@ -296,9 +296,9 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
};

let token_stream = quote! {
Some(rmcp::model::ToolExecution {
task_support: #task_support_expr,
})
Some(rmcp::model::ToolExecution::from_raw(
#task_support_expr,
))
};
syn::parse2::<Expr>(token_stream)?
} else {
Expand Down
21 changes: 8 additions & 13 deletions crates/rmcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

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

```rust,no_run
```rust,ignore
use rmcp::{
ServerHandler, ServiceExt,
handler::server::tool::ToolRouter,
Expand Down Expand Up @@ -68,11 +68,8 @@ impl Counter {
#[tool_handler]
impl ServerHandler for Counter {
fn get_info(&self) -> ServerInfo {
ServerInfo {
instructions: Some("A simple counter that tallies the number of times the increment tool has been used".into()),
capabilities: ServerCapabilities::builder().enable_tools().build(),
..Default::default()
}
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_instructions("A simple counter that tallies the number of times the increment tool has been used")
}
}

Expand Down Expand Up @@ -147,7 +144,7 @@ To expose task support, enable the `tasks` capability when building `ServerCapab

Creating a client to interact with a server:

```rust,no_run
```rust,ignore
use rmcp::{
ServiceExt,
model::CallToolRequestParams,
Expand Down Expand Up @@ -176,12 +173,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

// Call a tool
let result = service
.call_tool(CallToolRequestParams {
meta: None,
name: "git_status".into(),
arguments: serde_json::json!({ "repo_path": "." }).as_object().cloned(),
task: None,
})
.call_tool(
CallToolRequestParams::new("git_status")
.with_arguments(serde_json::json!({ "repo_path": "." }).as_object().cloned().unwrap_or_default())
)
.await?;
println!("Result: {result:#?}");

Expand Down
1 change: 1 addition & 0 deletions crates/rmcp/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ impl std::error::Error for ErrorData {}
/// This is an unified error type for the errors could be returned by the service.
#[derive(Debug, thiserror::Error)]
#[allow(clippy::large_enum_variant)]
#[non_exhaustive]
pub enum RmcpError {
#[cfg(any(feature = "client", feature = "server"))]
#[error("Service error: {0}")]
Expand Down
Loading