Skip to content

Commit 836e765

Browse files
authored
feat: improve schema version configuration using Cargo features (#51)
* feat: improve-schema-version-selection * improve-schema-version-selection * cleanup * revert default mcp version
1 parent 7479c5b commit 836e765

Some content is hidden

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

44 files changed

+250
-250
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ rust-mcp-sdk = { path = "crates/rust-mcp-sdk", default-features = false }
2121
rust-mcp-macros = { version = "0.4.1", path = "crates/rust-mcp-macros", default-features = false }
2222

2323
# External crates
24-
rust-mcp-schema = { version = "0.5", default-features = false }
24+
rust-mcp-schema = { version = "0.6", default-features = false }
25+
2526
futures = { version = "0.3" }
2627
tokio = { version = "1.4", features = ["full"] }
2728
serde = { version = "1.0", features = ["derive", "serde_derive"] }

Makefile.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ args = ["nextest", "run", "--no-tests=pass"]
2727
[tasks.doc-test]
2828
workspace = false
2929
command = "cargo"
30-
args = ["test", "--doc", "-p", "rust-mcp-sdk", "-p", "rust-mcp-transport"]
31-
dependencies = ["doc-test-macros"]
30+
args = ["test", "--doc", "-p", "rust-mcp-sdk"]
31+
dependencies = ["doc-test-macros", "doc-test-transport"]
32+
33+
[tasks.doc-test-transport]
34+
workspace = false
35+
command = "cargo"
36+
args = ["test", "--doc", "-p", "rust-mcp-sdk"]
3237

3338
[tasks.doc-test-macros]
3439
workspace = false

crates/rust-mcp-macros/src/lib.rs

Lines changed: 63 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,25 @@ use utils::{is_option, renamed_field, type_to_json_schema};
1919
/// * `name` - An optional string representing the tool's name.
2020
/// * `description` - An optional string describing the tool.
2121
///
22-
#[cfg(feature = "2024_11_05")]
23-
struct McpToolMacroAttributes {
24-
name: Option<String>,
25-
description: Option<String>,
26-
}
27-
28-
/// Represents the attributes for the `mcp_tool` procedural macro.
29-
///
30-
/// This struct parses and validates the `name` and `description` attributes provided
31-
/// to the `mcp_tool` macro. Both attributes are required and must not be empty strings.
32-
///
33-
/// # Fields
34-
/// * `name` - An optional string representing the tool's name.
35-
/// * `description` - An optional string describing the tool.
22+
/// The following fields are available only with the `2025_03_26` feature:
3623
/// * `destructive_hint` - Optional boolean for `ToolAnnotations::destructive_hint`.
3724
/// * `idempotent_hint` - Optional boolean for `ToolAnnotations::idempotent_hint`.
3825
/// * `open_world_hint` - Optional boolean for `ToolAnnotations::open_world_hint`.
3926
/// * `read_only_hint` - Optional boolean for `ToolAnnotations::read_only_hint`.
4027
/// * `title` - Optional string for `ToolAnnotations::title`.
4128
///
42-
#[cfg(feature = "2025_03_26")]
4329
struct McpToolMacroAttributes {
4430
name: Option<String>,
4531
description: Option<String>,
32+
#[cfg(feature = "2025_03_26")]
4633
destructive_hint: Option<bool>,
34+
#[cfg(feature = "2025_03_26")]
4735
idempotent_hint: Option<bool>,
36+
#[cfg(feature = "2025_03_26")]
4837
open_world_hint: Option<bool>,
38+
#[cfg(feature = "2025_03_26")]
4939
read_only_hint: Option<bool>,
40+
#[cfg(feature = "2025_03_26")]
5041
title: Option<String>,
5142
}
5243

@@ -75,13 +66,20 @@ impl Parse for McpToolMacroAttributes {
7566
/// - The `name` attribute is missing or empty.
7667
/// - The `description` attribute is missing or empty.
7768
fn parse(attributes: syn::parse::ParseStream) -> syn::Result<Self> {
78-
let mut name = None;
79-
let mut description = None;
80-
let mut destructive_hint = None;
81-
let mut idempotent_hint = None;
82-
let mut open_world_hint = None;
83-
let mut read_only_hint = None;
84-
let mut title = None;
69+
let mut instance = Self {
70+
name: None,
71+
description: None,
72+
#[cfg(feature = "2025_03_26")]
73+
destructive_hint: None,
74+
#[cfg(feature = "2025_03_26")]
75+
idempotent_hint: None,
76+
#[cfg(feature = "2025_03_26")]
77+
open_world_hint: None,
78+
#[cfg(feature = "2025_03_26")]
79+
read_only_hint: None,
80+
#[cfg(feature = "2025_03_26")]
81+
title: None,
82+
};
8583

8684
let meta_list: Punctuated<Meta, Token![,]> = Punctuated::parse_terminated(attributes)?;
8785
for meta in meta_list {
@@ -131,33 +129,38 @@ impl Parse for McpToolMacroAttributes {
131129
}
132130
};
133131
match ident_str.as_str() {
134-
"name" => name = Some(value),
135-
"description" => description = Some(value),
132+
"name" => instance.name = Some(value),
133+
"description" => instance.description = Some(value),
136134
_ => {}
137135
}
138136
}
139137
"destructive_hint" | "idempotent_hint" | "open_world_hint"
140138
| "read_only_hint" => {
141-
let value = match &meta_name_value.value {
142-
Expr::Lit(ExprLit {
143-
lit: Lit::Bool(lit_bool),
144-
..
145-
}) => lit_bool.value,
146-
_ => {
147-
return Err(Error::new_spanned(
148-
&meta_name_value.value,
149-
"Expected a boolean literal",
150-
));
139+
#[cfg(feature = "2025_03_26")]
140+
{
141+
let value = match &meta_name_value.value {
142+
Expr::Lit(ExprLit {
143+
lit: Lit::Bool(lit_bool),
144+
..
145+
}) => lit_bool.value,
146+
_ => {
147+
return Err(Error::new_spanned(
148+
&meta_name_value.value,
149+
"Expected a boolean literal",
150+
));
151+
}
152+
};
153+
154+
match ident_str.as_str() {
155+
"destructive_hint" => instance.destructive_hint = Some(value),
156+
"idempotent_hint" => instance.idempotent_hint = Some(value),
157+
"open_world_hint" => instance.open_world_hint = Some(value),
158+
"read_only_hint" => instance.read_only_hint = Some(value),
159+
_ => {}
151160
}
152-
};
153-
match ident_str.as_str() {
154-
"destructive_hint" => destructive_hint = Some(value),
155-
"idempotent_hint" => idempotent_hint = Some(value),
156-
"open_world_hint" => open_world_hint = Some(value),
157-
"read_only_hint" => read_only_hint = Some(value),
158-
_ => {}
159161
}
160162
}
163+
#[cfg(feature = "2025_03_26")]
161164
"title" => {
162165
let value = match &meta_name_value.value {
163166
Expr::Lit(ExprLit {
@@ -171,21 +174,27 @@ impl Parse for McpToolMacroAttributes {
171174
));
172175
}
173176
};
174-
title = Some(value);
177+
instance.title = Some(value);
175178
}
176179
_ => {}
177180
}
178181
}
179182
}
180183

181184
// Validate presence and non-emptiness
182-
if name.as_ref().map(|s| s.trim().is_empty()).unwrap_or(true) {
185+
if instance
186+
.name
187+
.as_ref()
188+
.map(|s| s.trim().is_empty())
189+
.unwrap_or(true)
190+
{
183191
return Err(Error::new(
184192
attributes.span(),
185193
"The 'name' attribute is required and must not be empty.",
186194
));
187195
}
188-
if description
196+
if instance
197+
.description
189198
.as_ref()
190199
.map(|s| s.trim().is_empty())
191200
.unwrap_or(true)
@@ -196,20 +205,6 @@ impl Parse for McpToolMacroAttributes {
196205
));
197206
}
198207

199-
#[cfg(feature = "2024_11_05")]
200-
let instance = Self { name, description };
201-
202-
#[cfg(feature = "2025_03_26")]
203-
let instance = Self {
204-
name,
205-
description,
206-
destructive_hint,
207-
idempotent_hint,
208-
open_world_hint,
209-
read_only_hint,
210-
title,
211-
};
212-
213208
Ok(instance)
214209
}
215210
}
@@ -304,21 +299,23 @@ pub fn mcp_tool(attributes: TokenStream, input: TokenStream) -> TokenStream {
304299
quote! {None}
305300
};
306301

307-
#[cfg(feature = "2025_03_26")]
308-
let tool_token = quote! {
309-
#base_crate::Tool {
310-
name: #tool_name.to_string(),
311-
description: Some(#tool_description.to_string()),
312-
input_schema: #base_crate::ToolInputSchema::new(required, properties),
313-
annotations: #annotations
302+
let annotations_token = {
303+
#[cfg(feature = "2025_03_26")]
304+
{
305+
quote! { annotations: #annotations }
306+
}
307+
#[cfg(not(feature = "2025_03_26"))]
308+
{
309+
quote! {}
314310
}
315311
};
316-
#[cfg(feature = "2024_11_05")]
312+
317313
let tool_token = quote! {
318314
#base_crate::Tool {
319315
name: #tool_name.to_string(),
320316
description: Some(#tool_description.to_string()),
321317
input_schema: #base_crate::ToolInputSchema::new(required, properties),
318+
#annotations_token
322319
}
323320
};
324321

crates/rust-mcp-sdk/Cargo.toml

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ license = "MIT"
1111
edition = "2021"
1212

1313
[dependencies]
14-
rust-mcp-schema = { workspace = true, default-features=false }
14+
rust-mcp-schema = { workspace = true, default-features = false }
1515
rust-mcp-transport = { workspace = true, default-features = false, optional = true }
16-
rust-mcp-macros = { workspace = true, optional = true, default-features=false, feature=["sdk"] }
16+
rust-mcp-macros = { workspace = true, optional = true, default-features = false }
1717

1818
tokio.workspace = true
1919
serde = { workspace = true }
@@ -46,7 +46,7 @@ default = [
4646
"macros",
4747
"hyper-server",
4848
"ssl",
49-
"2025_03_26"
49+
"2025_03_26",
5050
] # All features enabled by default
5151
server = ["rust-mcp-transport/stdio"] # Server feature
5252
client = ["rust-mcp-transport/stdio", "rust-mcp-transport/sse"] # Client feature
@@ -61,16 +61,20 @@ ssl = ["axum-server/tls-rustls"]
6161
macros = ["rust-mcp-macros/sdk"]
6262

6363
# enabled mcp protocol version 2025_03_26
64-
2025_03_26 = ["rust-mcp-schema/2025_03_26",
65-
"rust-mcp-macros/2025_03_26",
66-
"rust-mcp-transport/2025_03_26",
67-
"rust-mcp-schema/schema_utils"]
64+
2025_03_26 = [
65+
"rust-mcp-schema/2025_03_26",
66+
"rust-mcp-macros/2025_03_26",
67+
"rust-mcp-transport/2025_03_26",
68+
"rust-mcp-schema/schema_utils",
69+
]
6870

6971
# enabled mcp protocol version 2024_11_05
70-
2024_11_05 = ["rust-mcp-schema/2024_11_05",
71-
"rust-mcp-macros/2024_11_05",
72-
"rust-mcp-transport/2024_11_05",
73-
"rust-mcp-schema/schema_utils"]
72+
2024_11_05 = [
73+
"rust-mcp-schema/2024_11_05",
74+
"rust-mcp-macros/2024_11_05",
75+
"rust-mcp-transport/2024_11_05",
76+
"rust-mcp-schema/schema_utils",
77+
]
7478

7579
[lints]
7680
workspace = true

crates/rust-mcp-sdk/src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rust_mcp_schema::RpcError;
1+
use crate::schema::RpcError;
22
use rust_mcp_transport::error::TransportError;
33
use thiserror::Error;
44

@@ -18,7 +18,7 @@ pub enum McpSdkError {
1818
#[error("{0}")]
1919
AnyError(Box<(dyn std::error::Error + Send + Sync)>),
2020
#[error("{0}")]
21-
SdkError(#[from] rust_mcp_schema::schema_utils::SdkError),
21+
SdkError(#[from] crate::schema::schema_utils::SdkError),
2222
#[cfg(feature = "hyper-server")]
2323
#[error("{0}")]
2424
TransportServerError(#[from] TransportServerError),

crates/rust-mcp-sdk/src/hyper_servers/app_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{sync::Arc, time::Duration};
22

3-
use rust_mcp_schema::InitializeResult;
3+
use crate::schema::InitializeResult;
44
use rust_mcp_transport::TransportOptions;
55

66
use crate::mcp_traits::mcp_handler::McpServerHandler;

crates/rust-mcp-sdk/src/hyper_servers/hyper_server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::sync::Arc;
22

3-
use rust_mcp_schema::InitializeResult;
3+
use crate::schema::InitializeResult;
44

55
use crate::mcp_server::{server_runtime::ServerRuntimeInternalHandler, ServerHandler};
66

crates/rust-mcp-sdk/src/hyper_servers/hyper_server_core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{HyperServer, HyperServerOptions};
22
use crate::mcp_server::{server_runtime_core::RuntimeCoreInternalHandler, ServerHandlerCore};
3-
use rust_mcp_schema::InitializeResult;
3+
use crate::schema::InitializeResult;
44
use std::sync::Arc;
55

66
/// Creates a new HyperServer instance with the provided handler and options

crates/rust-mcp-sdk/src/hyper_servers/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use super::{
1616
routes::app_routes,
1717
IdGenerator, InMemorySessionStore, UuidGenerator,
1818
};
19+
use crate::schema::InitializeResult;
1920
use axum::Router;
20-
use rust_mcp_schema::InitializeResult;
2121
use rust_mcp_transport::TransportOptions;
2222

2323
// Default client ping interval (12 seconds)

0 commit comments

Comments
 (0)