Skip to content

refactor!: naming & less constrained dependencies #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 16, 2025
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
4 changes: 2 additions & 2 deletions crates/rust-mcp-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A procedural macro, part of the [rust-mcp-sdk](https://github.com/rust-mcp-stack
The `mcp_tool` macro generates an implementation for the annotated struct that includes:

- A `tool_name()` method returning the tool's name as a string.
- A `get_tool()` method returning a `rust_mcp_schema::Tool` instance with the tool's name,
- A `tool()` method returning a `rust_mcp_schema::Tool` instance with the tool's name,
description, and input schema derived from the struct's fields.

## Attributes
Expand All @@ -32,7 +32,7 @@ fn main() {

assert_eq!(WriteFileTool::tool_name(), "write_file");

let tool: rust_mcp_schema::Tool = WriteFileTool::get_tool();
let tool: rust_mcp_schema::Tool = WriteFileTool::tool();
assert_eq!(tool.name, "write_file");
assert_eq!( tool.description.unwrap(),"Create a new file or completely overwrite an existing file with new content.");

Expand Down
22 changes: 11 additions & 11 deletions crates/rust-mcp-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ use utils::{is_option, renamed_field, type_to_json_schema};
/// * `name` - An optional string representing the tool's name.
/// * `description` - An optional string describing the tool.
///
struct MCPToolMacroAttributes {
struct McpToolMacroAttributes {
name: Option<String>,
description: Option<String>,
}

impl Parse for MCPToolMacroAttributes {
impl Parse for McpToolMacroAttributes {
/// Parses the macro attributes from a `ParseStream`.
///
/// This implementation extracts `name` and `description` from the attribute input,
Expand Down Expand Up @@ -96,7 +96,7 @@ impl Parse for MCPToolMacroAttributes {
///
/// The `mcp_tool` macro generates an implementation for the annotated struct that includes:
/// - A `tool_name()` method returning the tool's name as a string.
/// - A `get_tool()` method returning a `rust_mcp_schema::Tool` instance with the tool's name,
/// - A `tool()` method returning a `rust_mcp_schema::Tool` instance with the tool's name,
/// description, and input schema derived from the struct's fields.
///
/// # Attributes
Expand All @@ -116,7 +116,7 @@ impl Parse for MCPToolMacroAttributes {
/// }
///
/// assert_eq!(ExampleTool::tool_name() , "example_tool");
/// let tool : rust_mcp_schema::Tool = ExampleTool::get_tool();
/// let tool : rust_mcp_schema::Tool = ExampleTool::tool();
/// assert_eq!(tool.name , "example_tool");
/// assert_eq!(tool.description.unwrap() , "An example tool");
///
Expand All @@ -131,7 +131,7 @@ pub fn mcp_tool(attributes: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); // Parse the input as a function
let input_ident = &input.ident;

let macro_attributes = parse_macro_input!(attributes as MCPToolMacroAttributes);
let macro_attributes = parse_macro_input!(attributes as McpToolMacroAttributes);

let tool_name = macro_attributes.name.unwrap_or_default();
let tool_description = macro_attributes.description.unwrap_or_default();
Expand All @@ -147,7 +147,7 @@ pub fn mcp_tool(attributes: TokenStream, input: TokenStream) -> TokenStream {
///
/// The tool includes the name, description, and input schema derived from
/// the struct's attributes.
pub fn get_tool()-> rust_mcp_schema::Tool
pub fn tool()-> rust_mcp_schema::Tool
{
let json_schema = &#input_ident::json_schema();

Expand Down Expand Up @@ -304,7 +304,7 @@ mod tests {
#[test]
fn test_valid_macro_attributes() {
let input = r#"name = "test_tool", description = "A test tool.""#;
let parsed: MCPToolMacroAttributes = parse_str(input).unwrap();
let parsed: McpToolMacroAttributes = parse_str(input).unwrap();

assert_eq!(parsed.name.unwrap(), "test_tool");
assert_eq!(parsed.description.unwrap(), "A test tool.");
Expand All @@ -313,7 +313,7 @@ mod tests {
#[test]
fn test_missing_name() {
let input = r#"description = "Only description""#;
let result: Result<MCPToolMacroAttributes, Error> = parse_str(input);
let result: Result<McpToolMacroAttributes, Error> = parse_str(input);
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
Expand All @@ -324,7 +324,7 @@ mod tests {
#[test]
fn test_missing_description() {
let input = r#"name = "OnlyName""#;
let result: Result<MCPToolMacroAttributes, Error> = parse_str(input);
let result: Result<McpToolMacroAttributes, Error> = parse_str(input);
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
Expand All @@ -335,7 +335,7 @@ mod tests {
#[test]
fn test_empty_name_field() {
let input = r#"name = "", description = "something""#;
let result: Result<MCPToolMacroAttributes, Error> = parse_str(input);
let result: Result<McpToolMacroAttributes, Error> = parse_str(input);
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
Expand All @@ -345,7 +345,7 @@ mod tests {
#[test]
fn test_empty_description_field() {
let input = r#"name = "my-tool", description = """#;
let result: Result<MCPToolMacroAttributes, Error> = parse_str(input);
let result: Result<McpToolMacroAttributes, Error> = parse_str(input);
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
Expand Down
22 changes: 11 additions & 11 deletions crates/rust-mcp-macros/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn is_vec(ty: &Type) -> bool {

// Extract the inner type from Vec<T> or Option<T>
#[allow(unused)]
pub fn get_inner_type(ty: &Type) -> Option<&Type> {
pub fn inner_type(ty: &Type) -> Option<&Type> {
if let Type::Path(type_path) = ty {
if type_path.path.segments.len() == 1 {
let segment = &type_path.path.segments[0];
Expand All @@ -46,7 +46,7 @@ pub fn get_inner_type(ty: &Type) -> Option<&Type> {
None
}

fn get_doc_comment(attrs: &[Attribute]) -> Option<String> {
fn doc_comment(attrs: &[Attribute]) -> Option<String> {
let mut docs = Vec::new();
for attr in attrs {
if attr.path().is_ident("doc") {
Expand Down Expand Up @@ -86,7 +86,7 @@ pub fn type_to_json_schema(ty: &Type, attrs: &[Attribute]) -> proc_macro2::Token
let number_types = [
"i8", "i16", "i32", "i64", "i128", "u8", "u16", "u32", "u64", "u128", "f32", "f64",
];
let doc_comment = get_doc_comment(attrs);
let doc_comment = doc_comment(attrs);
let description = doc_comment.as_ref().map(|desc| {
quote! {
map.insert("description".to_string(), serde_json::Value::String(#desc.to_string()));
Expand Down Expand Up @@ -266,21 +266,21 @@ mod tests {
}

#[test]
fn test_get_inner_type() {
fn test_inner_type() {
let ty: Type = parse_quote!(Option<String>);
let inner = get_inner_type(&ty);
let inner = inner_type(&ty);
assert!(inner.is_some());
let inner = inner.unwrap();
assert_eq!(quote!(#inner).to_string(), quote!(String).to_string());

let ty: Type = parse_quote!(Vec<i32>);
let inner = get_inner_type(&ty);
let inner = inner_type(&ty);
assert!(inner.is_some());
let inner = inner.unwrap();
assert_eq!(quote!(#inner).to_string(), quote!(i32).to_string());

let ty: Type = parse_quote!(i32);
assert!(get_inner_type(&ty).is_none());
assert!(inner_type(&ty).is_none());
}

#[test]
Expand Down Expand Up @@ -338,7 +338,7 @@ mod tests {
#[test]
fn test_get_doc_comment_single_line() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[doc = "This is a test comment."])];
let result = super::get_doc_comment(&attrs);
let result = super::doc_comment(&attrs);
assert_eq!(result, Some("This is a test comment.".to_string()));
}

Expand All @@ -349,7 +349,7 @@ mod tests {
parse_quote!(#[doc = "Line two."]),
parse_quote!(#[doc = "Line three."]),
];
let result = super::get_doc_comment(&attrs);
let result = super::doc_comment(&attrs);
assert_eq!(
result,
Some("Line one.\nLine two.\nLine three.".to_string())
Expand All @@ -359,14 +359,14 @@ mod tests {
#[test]
fn test_get_doc_comment_no_doc() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[allow(dead_code)])];
let result = super::get_doc_comment(&attrs);
let result = super::doc_comment(&attrs);
assert_eq!(result, None);
}

#[test]
fn test_get_doc_comment_trim_whitespace() {
let attrs: Vec<Attribute> = vec![parse_quote!(#[doc = " Trimmed line. "])];
let result = super::get_doc_comment(&attrs);
let result = super::doc_comment(&attrs);
assert_eq!(result, Some("Trimmed line.".to_string()));
}

Expand Down
20 changes: 10 additions & 10 deletions crates/rust-mcp-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

# Rust MCP SDK

A high-performance, asynchronous toolkit for building MCP servers and clients.
A high-performance, asynchronous toolkit for building MCP servers and clients.
Focus on your app's logic while **rust-mcp-sdk** takes care of the rest!

**rust-mcp-sdk** provides the necessary components for developing both servers and clients in the MCP ecosystem.
**rust-mcp-sdk** provides the necessary components for developing both servers and clients in the MCP ecosystem.
Leveraging the [rust-mcp-schema](https://github.com/rust-mcp-stack/rust-mcp-schema) crate for type safe MCP schema objects and MCP type utilities simplifies the process of building robust and reliable MCP servers and clients, ensuring consistency and minimizing errors in data handling and message processing.

**⚠️WARNING**: This project only supports Standard Input/Output (stdio) transport at this time, with support for SSE (Server-Sent Events) transport still in progress and not yet available. Project is currently under development and should be used at your own risk.
Expand Down Expand Up @@ -70,18 +70,18 @@ pub struct MyServerHandler;
#[async_trait]
impl ServerHandler for MyServerHandler {
// Handle ListToolsRequest, return list of available tools as ListToolsResult
async fn handle_list_tools_request(&self, request: ListToolsRequest, runtime: &dyn MCPServer) -> Result<ListToolsResult, RpcError> {
async fn handle_list_tools_request(&self, request: ListToolsRequest, runtime: &dyn McpServer) -> Result<ListToolsResult, RpcError> {

Ok(ListToolsResult {
tools: vec![SayHelloTool::get_tool()],
tools: vec![SayHelloTool::tool()],
meta: None,
next_cursor: None,
})

}

/// Handles requests to call a specific tool.
async fn handle_call_tool_request( &self, request: CallToolRequest, runtime: &dyn MCPServer, ) -> Result<CallToolResult, CallToolError> {
async fn handle_call_tool_request( &self, request: CallToolRequest, runtime: &dyn McpServer, ) -> Result<CallToolResult, CallToolError> {

if request.tool_name() == SayHelloTool::tool_name() {
Ok(CallToolResult::text_content(
Expand Down Expand Up @@ -153,7 +153,7 @@ async fn main() -> SdkResult<()> {
// STEP 7: use client methods to communicate with the MCP Server as you wish

// Retrieve and display the list of tools available on the server
let server_version = client.get_server_version().unwrap();
let server_version = client.server_version().unwrap();
let tools = client.list_tools(None).await?.tools;

println!("List of tools for {}@{}", server_version.name, server_version.version);
Expand Down Expand Up @@ -195,10 +195,10 @@ If you are looking for a step-by-step tutorial on how to get started with `rust-

[rust-mcp-sdk](https://github.com/rust-mcp-stack/rust-mcp-sdk) provides two type of handler traits that you can chose from:

- **mcp_server_handler**: This is the recommended trait for your MCP project, offering a default implementation for all types of MCP messages. It includes predefined implementations within the trait, such as handling initialization or responding to ping requests, so you only need to override and customize the handler functions relevant to your specific needs.
- **mcp_server_handler**: This is the recommended trait for your MCP project, offering a default implementation for all types of MCP messages. It includes predefined implementations within the trait, such as handling initialization or responding to ping requests, so you only need to override and customize the handler functions relevant to your specific needs.
Refer to [examples/hello-world-mcp-server/src/handler.rs](https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/examples/hello-world-mcp-server/src/handler.rs) for an example.

- **mcp_server_handler_core**: If you need more control over MCP messages, consider using `mcp_server_handler_core`. It offers three primary methods to manage the three MCP message types: `request`, `notification`, and `error`. While still providing type-safe objects in these methods, it allows you to determine how to handle each message based on its type and parameters.
- **mcp_server_handler_core**: If you need more control over MCP messages, consider using `mcp_server_handler_core`. It offers three primary methods to manage the three MCP message types: `request`, `notification`, and `error`. While still providing type-safe objects in these methods, it allows you to determine how to handle each message based on its type and parameters.
Refer to [examples/hello-world-mcp-server-core/src/handler.rs](https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/examples/hello-world-mcp-server-core/src/handler.rs) for an example.

---
Expand All @@ -209,8 +209,8 @@ If you are looking for a step-by-step tutorial on how to get started with `rust-

### Choosing Between `mcp_client_handler` and `mcp_client_handler_core`

The same principles outlined above apply to the client-side handlers, `mcp_client_handler` and `mcp_client_handler_core`.
Use `client_runtime::create_client()` or `client_runtime_core::create_client()` , respectively.
The same principles outlined above apply to the client-side handlers, `mcp_client_handler` and `mcp_client_handler_core`.
Use `client_runtime::create_client()` or `client_runtime_core::create_client()` , respectively.
Check out the corresponding examples at: [examples/simple-mcp-client](https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/examples/simple-mcp-client) and [examples/simple-mcp-client-core](https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/examples/simple-mcp-client-core).

## License
Expand Down
6 changes: 3 additions & 3 deletions crates/rust-mcp-sdk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use rust_mcp_schema::RpcError;
use rust_mcp_transport::error::TransportError;
use thiserror::Error;

pub type SdkResult<T> = core::result::Result<T, MCPSdkError>;
pub type SdkResult<T> = core::result::Result<T, McpSdkError>;

#[derive(Debug, Error)]
pub enum MCPSdkError {
pub enum McpSdkError {
#[error("{0}")]
RpcError(#[from] RpcError),
#[error("{0}")]
Expand All @@ -15,7 +15,7 @@ pub enum MCPSdkError {
#[error("{0}")]
AnyErrorStatic(Box<(dyn std::error::Error + Send + Sync + 'static)>),
#[error("{0}")]
AnyError(Box<(dyn std::error::Error + Send + Sync + 'static)>),
AnyError(Box<(dyn std::error::Error + Send + Sync)>),
#[error("{0}")]
SdkError(#[from] rust_mcp_schema::schema_utils::SdkError),
}
Loading
Loading