Skip to content

Commit 9a870ee

Browse files
committed
feat(model): add title field to various structs
1 parent cb5e708 commit 9a870ee

13 files changed

Lines changed: 69 additions & 7 deletions

File tree

crates/rmcp-macros/src/prompt.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::common::{extract_doc_line, none_expr};
1010
pub struct PromptAttribute {
1111
/// The name of the prompt
1212
pub name: Option<String>,
13+
/// Human readable title of prompt
14+
pub title: Option<String>,
1315
/// Optional description of what the prompt does
1416
pub description: Option<String>,
1517
/// Arguments that can be passed to the prompt
@@ -18,6 +20,7 @@ pub struct PromptAttribute {
1820

1921
pub struct ResolvedPromptAttribute {
2022
pub name: String,
23+
pub title: Option<String>,
2124
pub description: Option<String>,
2225
pub arguments: Expr,
2326
}
@@ -28,18 +31,25 @@ impl ResolvedPromptAttribute {
2831
name,
2932
description,
3033
arguments,
34+
title,
3135
} = self;
3236
let description = if let Some(description) = description {
3337
quote! { Some(#description.into()) }
3438
} else {
3539
quote! { None }
3640
};
41+
let title = if let Some(title) = title {
42+
quote! { Some(#title.into()) }
43+
} else {
44+
quote! { None }
45+
};
3746
let tokens = quote! {
3847
pub fn #fn_ident() -> rmcp::model::Prompt {
3948
rmcp::model::Prompt {
4049
name: #name.into(),
4150
description: #description,
4251
arguments: #arguments,
52+
title: #title,
4353
}
4454
}
4555
};
@@ -87,6 +97,7 @@ pub fn prompt(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream>
8797
name: name.clone(),
8898
description: description.clone(),
8999
arguments: arguments.clone(),
100+
title: attribute.title,
90101
};
91102
let prompt_attr_fn = resolved_prompt_attr.into_fn(prompt_attr_fn_ident.clone())?;
92103

crates/rmcp-macros/src/tool.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ fn extract_schema_from_return_type(ret_type: &syn::Type) -> Option<Expr> {
6666
pub struct ToolAttribute {
6767
/// The name of the tool
6868
pub name: Option<String>,
69+
/// Human readable title of tool
70+
pub title: Option<String>,
6971
pub description: Option<String>,
7072
/// A JSON Schema object defining the expected parameters for the tool
7173
pub input_schema: Option<Expr>,
@@ -77,6 +79,7 @@ pub struct ToolAttribute {
7779

7880
pub struct ResolvedToolAttribute {
7981
pub name: String,
82+
pub title: Option<String>,
8083
pub description: Option<String>,
8184
pub input_schema: Expr,
8285
pub output_schema: Option<Expr>,
@@ -88,6 +91,7 @@ impl ResolvedToolAttribute {
8891
let Self {
8992
name,
9093
description,
94+
title,
9195
input_schema,
9296
output_schema,
9397
annotations,
@@ -102,10 +106,16 @@ impl ResolvedToolAttribute {
102106
} else {
103107
quote! { None }
104108
};
109+
let title = if let Some(title) = title {
110+
quote! { Some(#title.into()) }
111+
} else {
112+
quote! { None }
113+
};
105114
let tokens = quote! {
106115
pub fn #fn_ident() -> rmcp::model::Tool {
107116
rmcp::model::Tool {
108117
name: #name.into(),
118+
title: #title,
109119
description: #description,
110120
input_schema: #input_schema,
111121
output_schema: #output_schema,
@@ -229,6 +239,7 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
229239
input_schema: input_schema_expr,
230240
output_schema: output_schema_expr,
231241
annotations: annotations_expr,
242+
title: attribute.title,
232243
};
233244
let tool_attr_fn = resolved_tool_attr.into_fn(tool_attr_fn_ident)?;
234245
// modify the the input function

crates/rmcp/src/handler/server/prompt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ pub fn cached_arguments_from_schema<T: schemars::JsonSchema + std::any::Any>()
350350

351351
arguments.push(crate::model::PromptArgument {
352352
name: name.clone(),
353+
title: None,
353354
description,
354355
required: Some(required.contains(name.as_str())),
355356
});

crates/rmcp/src/model.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,8 @@ impl Default for ClientInfo {
686686
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
687687
pub struct Implementation {
688688
pub name: String,
689+
#[serde(skip_serializing_if = "Option::is_none")]
690+
pub title: Option<String>,
689691
pub version: String,
690692
}
691693

@@ -699,6 +701,7 @@ impl Implementation {
699701
pub fn from_build_env() -> Self {
700702
Implementation {
701703
name: env!("CARGO_CRATE_NAME").to_owned(),
704+
title: None,
702705
version: env!("CARGO_PKG_VERSION").to_owned(),
703706
}
704707
}
@@ -1104,6 +1107,8 @@ pub struct ResourceReference {
11041107
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
11051108
pub struct PromptReference {
11061109
pub name: String,
1110+
#[serde(skip_serializing_if = "Option::is_none")]
1111+
pub title: Option<String>,
11071112
}
11081113

11091114
const_string!(CompleteRequestMethod = "completion/complete");
@@ -1430,6 +1435,7 @@ macro_rules! ts_union {
14301435
export type $U: ident =
14311436
$(|)?$($V: ident)|*;
14321437
) => {
1438+
#[allow(clippy::large_enum_variant)]
14331439
#[derive(Debug, Serialize, Deserialize, Clone)]
14341440
#[serde(untagged)]
14351441
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

crates/rmcp/src/model/content.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub enum RawContent {
5757
Text(RawTextContent),
5858
Image(RawImageContent),
5959
Resource(RawEmbeddedResource),
60-
Audio(AudioContent),
60+
Audio(RawAudioContent),
6161
ResourceLink(super::resource::RawResource),
6262
}
6363

@@ -234,6 +234,7 @@ mod tests {
234234
let resource_link = RawContent::ResourceLink(RawResource {
235235
uri: "file:///test.txt".to_string(),
236236
name: "test.txt".to_string(),
237+
title: None,
237238
description: Some("A test file".to_string()),
238239
mime_type: Some("text/plain".to_string()),
239240
size: Some(100),

crates/rmcp/src/model/meta.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
use std::ops::{Deref, DerefMut};
1+
use std::{
2+
borrow::Cow,
3+
collections::HashMap,
4+
ops::{Deref, DerefMut, Range},
5+
};
26

37
use serde::{Deserialize, Serialize};
48
use serde_json::Value;
59

610
use super::{
7-
ClientNotification, ClientRequest, Extensions, JsonObject, JsonRpcMessage, NumberOrString,
8-
ProgressToken, ServerNotification, ServerRequest,
11+
ClientNotification, ClientRequest, Extensions, JsonRpcMessage, NumberOrString, ProgressToken,
12+
ServerNotification, ServerRequest,
913
};
1014

15+
pub struct MetaKey {
16+
pub raw: Cow<'static, [u8]>,
17+
pub prefix: Range<usize>,
18+
}
19+
1120
pub trait GetMeta {
1221
fn get_meta_mut(&mut self) -> &mut Meta;
1322
fn get_meta(&self) -> &Meta;
@@ -100,11 +109,11 @@ variant_extension! {
100109
}
101110
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
102111
#[serde(transparent)]
103-
pub struct Meta(pub JsonObject);
112+
pub struct Meta(pub HashMap<String, Value>);
104113
const PROGRESS_TOKEN_FIELD: &str = "progressToken";
105114
impl Meta {
106115
pub fn new() -> Self {
107-
Self(JsonObject::new())
116+
Self(HashMap::new())
108117
}
109118

110119
pub(crate) fn static_empty() -> &'static Self {
@@ -142,7 +151,7 @@ impl Meta {
142151
}
143152

144153
impl Deref for Meta {
145-
type Target = JsonObject;
154+
type Target = HashMap<String, Value>;
146155

147156
fn deref(&self) -> &Self::Target {
148157
&self.0

crates/rmcp/src/model/prompt.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use super::{
1414
pub struct Prompt {
1515
/// The name of the prompt
1616
pub name: String,
17+
#[serde(skip_serializing_if = "Option::is_none")]
18+
pub title: Option<String>,
1719
/// Optional description of what the prompt does
1820
#[serde(skip_serializing_if = "Option::is_none")]
1921
pub description: Option<String>,
@@ -35,6 +37,7 @@ impl Prompt {
3537
{
3638
Prompt {
3739
name: name.into(),
40+
title: None,
3841
description: description.map(Into::into),
3942
arguments,
4043
}
@@ -47,6 +50,9 @@ impl Prompt {
4750
pub struct PromptArgument {
4851
/// The name of the argument
4952
pub name: String,
53+
/// A human-readable title for the argument
54+
#[serde(skip_serializing_if = "Option::is_none")]
55+
pub title: Option<String>,
5056
/// A description of what the argument is used for
5157
#[serde(skip_serializing_if = "Option::is_none")]
5258
pub description: Option<String>,

crates/rmcp/src/model/resource.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ pub struct RawResource {
1111
pub uri: String,
1212
/// Name of the resource
1313
pub name: String,
14+
/// Human-readable title of the resource
15+
#[serde(skip_serializing_if = "Option::is_none")]
16+
pub title: Option<String>,
1417
/// Optional description of the resource
1518
#[serde(skip_serializing_if = "Option::is_none")]
1619
pub description: Option<String>,
@@ -34,6 +37,8 @@ pub struct RawResourceTemplate {
3437
pub uri_template: String,
3538
pub name: String,
3639
#[serde(skip_serializing_if = "Option::is_none")]
40+
pub title: Option<String>,
41+
#[serde(skip_serializing_if = "Option::is_none")]
3742
pub description: Option<String>,
3843
#[serde(skip_serializing_if = "Option::is_none")]
3944
pub mime_type: Option<String>,
@@ -77,6 +82,7 @@ impl RawResource {
7782
Self {
7883
uri: uri.into(),
7984
name: name.into(),
85+
title: None,
8086
description: None,
8187
mime_type: None,
8288
size: None,
@@ -94,6 +100,7 @@ mod tests {
94100
fn test_resource_serialization() {
95101
let resource = RawResource {
96102
uri: "file:///test.txt".to_string(),
103+
title: None,
97104
name: "test".to_string(),
98105
description: Some("Test resource".to_string()),
99106
mime_type: Some("text/plain".to_string()),

crates/rmcp/src/model/tool.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use super::JsonObject;
1515
pub struct Tool {
1616
/// The name of the tool
1717
pub name: Cow<'static, str>,
18+
/// A human-readable title for the tool
19+
#[serde(skip_serializing_if = "Option::is_none")]
20+
pub title: Option<String>,
1821
/// A description of what the tool does
1922
#[serde(skip_serializing_if = "Option::is_none")]
2023
pub description: Option<Cow<'static, str>>,
@@ -138,6 +141,7 @@ impl Tool {
138141
{
139142
Tool {
140143
name: name.into(),
144+
title: None,
141145
description: Some(description.into()),
142146
input_schema: input_schema.into(),
143147
output_schema: None,

crates/rmcp/tests/test_elicitation.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ async fn test_initialize_request_with_elicitation() {
892892
client_info: Implementation {
893893
name: "test-client".to_string(),
894894
version: "1.0.0".to_string(),
895+
title: None,
895896
},
896897
};
897898

@@ -933,6 +934,7 @@ async fn test_capability_checking_logic() {
933934
client_info: Implementation {
934935
name: "test-client".to_string(),
935936
version: "1.0.0".to_string(),
937+
title: None,
936938
},
937939
};
938940

@@ -950,6 +952,7 @@ async fn test_capability_checking_logic() {
950952
client_info: Implementation {
951953
name: "test-client".to_string(),
952954
version: "1.0.0".to_string(),
955+
title: None,
953956
},
954957
};
955958

0 commit comments

Comments
 (0)