Skip to content

Commit adb8787

Browse files
authored
feat: add support for advanced JSON Schema types such as anyOf and allOf, oneOf (#16)
* feat: support anyOf, oneOf, allOf types * feat: support representation of enum values * cleanup
1 parent cd42a90 commit adb8787

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

src/schema.rs

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,113 @@ pub fn param_object(object_map: &Map<String, Value>) -> DiscoveryResult<Vec<McpT
5151

5252
/// Determines the parameter type from a schema definition.
5353
pub fn param_type(type_info: &Map<String, Value>) -> DiscoveryResult<ParamTypes> {
54+
// Check for 'enum' keyword
55+
if let Some(enum_values) = type_info.get("enum") {
56+
let values = enum_values.as_array().ok_or(DiscoveryError::InvalidSchema(
57+
"'enum' field must be an array".to_string(),
58+
))?;
59+
if values.is_empty() {
60+
return Err(DiscoveryError::InvalidSchema(
61+
"'enum' array cannot be empty".to_string(),
62+
));
63+
}
64+
let mut param_types = Vec::new();
65+
for value in values {
66+
let param_type = match value {
67+
Value::String(s) => ParamTypes::Primitive(s.clone()),
68+
Value::Number(n) => ParamTypes::Primitive(n.to_string()),
69+
Value::Bool(b) => ParamTypes::Primitive(b.to_string()),
70+
Value::Null => ParamTypes::Primitive("null".to_string()),
71+
_ => {
72+
return Err(DiscoveryError::InvalidSchema(format!(
73+
"Unsupported enum value type: {}",
74+
serde_json::to_string(value).unwrap_or_default()
75+
)))
76+
}
77+
};
78+
param_types.push(param_type);
79+
}
80+
return Ok(ParamTypes::EnumValues(param_types));
81+
}
82+
83+
// Check for 'const' keyword
84+
if let Some(const_value) = type_info.get("const") {
85+
return Ok(ParamTypes::Primitive(
86+
serde_json::to_string(const_value)
87+
.unwrap_or_default()
88+
.trim_matches('"')
89+
.to_string(),
90+
));
91+
}
92+
93+
if let Some(any_of) = type_info.get("anyOf") {
94+
let any_of_array = any_of.as_array().ok_or(DiscoveryError::InvalidSchema(
95+
"'anyOf' field must be an array".to_string(),
96+
))?;
97+
if any_of_array.is_empty() {
98+
return Err(DiscoveryError::InvalidSchema(
99+
"'anyOf' array cannot be empty".to_string(),
100+
));
101+
}
102+
let mut enum_types = Vec::new();
103+
for item in any_of_array {
104+
let item_map = item.as_object().ok_or(DiscoveryError::InvalidSchema(
105+
"Items in 'anyOf' must be objects".to_string(),
106+
))?;
107+
enum_types.push(param_type(item_map)?);
108+
}
109+
return Ok(ParamTypes::Anyof(enum_types));
110+
}
111+
112+
// Check for 'oneOf'
113+
if let Some(one_of) = type_info.get("oneOf") {
114+
let one_of_array = one_of.as_array().ok_or(DiscoveryError::InvalidSchema(
115+
"'oneOf' field must be an array".to_string(),
116+
))?;
117+
if one_of_array.is_empty() {
118+
return Err(DiscoveryError::InvalidSchema(
119+
"'oneOf' array cannot be empty".to_string(),
120+
));
121+
}
122+
let mut one_of_types = Vec::new();
123+
for item in one_of_array {
124+
let item_map = item.as_object().ok_or(DiscoveryError::InvalidSchema(
125+
"Items in 'oneOf' must be objects".to_string(),
126+
))?;
127+
one_of_types.push(param_type(item_map)?);
128+
}
129+
return Ok(ParamTypes::OneOf(one_of_types));
130+
}
131+
132+
// Check for 'allOf'
133+
if let Some(all_of) = type_info.get("allOf") {
134+
let all_of_array = all_of.as_array().ok_or(DiscoveryError::InvalidSchema(
135+
"'allOf' field must be an array".to_string(),
136+
))?;
137+
if all_of_array.is_empty() {
138+
return Err(DiscoveryError::InvalidSchema(
139+
"'allOf' array cannot be empty".to_string(),
140+
));
141+
}
142+
let mut all_of_types = Vec::new();
143+
for item in all_of_array {
144+
let item_map = item.as_object().ok_or(DiscoveryError::InvalidSchema(
145+
"Items in 'allOf' must be objects".to_string(),
146+
))?;
147+
all_of_types.push(param_type(item_map)?);
148+
}
149+
return Ok(ParamTypes::AllOf(all_of_types));
150+
}
151+
152+
// other types
54153
let type_name =
55154
type_info
56155
.get("type")
57156
.and_then(|v| v.as_str())
58-
.ok_or(DiscoveryError::InvalidSchema(
59-
"Missing or invalid 'type' field".to_string(),
60-
))?;
157+
.ok_or(DiscoveryError::InvalidSchema(format!(
158+
"Missing or invalid 'type' field: {}",
159+
serde_json::to_string(&type_info).unwrap_or_default()
160+
)))?;
61161

62162
match type_name {
63163
"array" => {

src/types/capabilities.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ pub enum ParamTypes {
2727
Primitive(String),
2828
Object(Vec<McpToolSParams>),
2929
Array(Vec<ParamTypes>),
30+
Anyof(Vec<ParamTypes>), // anyOf
31+
OneOf(Vec<ParamTypes>), // oneOf
32+
AllOf(Vec<ParamTypes>), // allOf
33+
EnumValues(Vec<ParamTypes>), // JSON Schema enum
3034
}
3135

3236
impl Display for ParamTypes {
@@ -44,6 +48,26 @@ impl Display for ParamTypes {
4448
)
4549
}
4650
ParamTypes::Array(items) => format!("{} [ ]", items[0]),
51+
ParamTypes::Anyof(types) => types
52+
.iter()
53+
.map(|t| t.to_string())
54+
.collect::<Vec<String>>()
55+
.join(" | "),
56+
ParamTypes::OneOf(types) => types
57+
.iter()
58+
.map(|t| t.to_string())
59+
.collect::<Vec<String>>()
60+
.join(" | "),
61+
ParamTypes::AllOf(types) => types
62+
.iter()
63+
.map(|t| t.to_string())
64+
.collect::<Vec<String>>()
65+
.join(" & "),
66+
ParamTypes::EnumValues(types) => types
67+
.iter()
68+
.map(|t| t.to_string())
69+
.collect::<Vec<String>>()
70+
.join("|"),
4771
};
4872
write!(f, "{}", type_name)
4973
}

0 commit comments

Comments
 (0)