mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
fix(mcp): publish properties in tool input schemas
This commit is contained in:
@@ -55,6 +55,7 @@ enum DbxResourceRequest {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
#[schemars(extend("properties" = {}))]
|
||||
pub struct ListConnectionsRequest {}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
@@ -3371,6 +3372,9 @@ impl DbxMcpServer {
|
||||
if self.plugin_tools_mode.advertise_flat() {
|
||||
tools.extend(self.plugin_tools_view(policy.as_ref()).await);
|
||||
}
|
||||
for tool in &mut tools {
|
||||
normalize_tool_input_schema(tool);
|
||||
}
|
||||
tools
|
||||
}
|
||||
|
||||
@@ -4003,6 +4007,23 @@ impl ServerHandler for DbxMcpServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure object-typed input schemas publish a `properties` object.
|
||||
/// OpenAI-compatible gateways (e.g. DashScope / 通义千问, DeepSeek Harness)
|
||||
/// strictly validate tool schemas and reject all tool calls if any tool
|
||||
/// with `type: "object"` lacks a `properties` map (issue #12464).
|
||||
fn normalize_tool_input_schema(tool: &mut rmcp::model::Tool) {
|
||||
let is_object = match tool.input_schema.get("type") {
|
||||
Some(serde_json::Value::String(t)) => t == "object",
|
||||
None => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_object && !tool.input_schema.contains_key("properties") {
|
||||
let mut object = (*tool.input_schema).clone();
|
||||
object.insert("properties".to_string(), serde_json::json!({}));
|
||||
tool.input_schema = std::sync::Arc::new(object);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_dbx_resource_uri(uri: &str) -> Result<DbxResourceRequest, ErrorData> {
|
||||
let parsed = Url::parse(uri).map_err(|_| ErrorData::invalid_params("Invalid DBX resource URI.", None))?;
|
||||
if parsed.scheme() != "dbx"
|
||||
@@ -6628,6 +6649,58 @@ mod tests {
|
||||
.is_some_and(|required| required.iter().any(|field| field == "tables")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_advertised_tools_publish_properties_in_input_schema() {
|
||||
let server = DbxMcpServer::with_runtime_options(Arc::new(FakeBackend::default()), McpScope::default(), false);
|
||||
let tools = server.tool_router.list_all();
|
||||
|
||||
for tool in &tools {
|
||||
let is_object = match tool.input_schema.get("type") {
|
||||
Some(serde_json::Value::String(t)) => t == "object",
|
||||
None => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_object {
|
||||
assert!(
|
||||
tool.input_schema.get("properties").and_then(serde_json::Value::as_object).is_some(),
|
||||
"tool {} inputSchema must publish a 'properties' object for OpenAI compatibility (issue #12464)",
|
||||
tool.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let list_connections = tools
|
||||
.iter()
|
||||
.find(|tool| tool.name == "dbx_list_connections")
|
||||
.expect("dbx_list_connections should be registered");
|
||||
assert_eq!(
|
||||
list_connections.input_schema.get("properties"),
|
||||
Some(&serde_json::json!({})),
|
||||
"dbx_list_connections must have empty properties object"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn policy_filtered_tools_guarantee_properties_for_all_object_schemas() {
|
||||
let server = DbxMcpServer::with_runtime_options(Arc::new(FakeBackend::default()), McpScope::default(), false);
|
||||
let tools = server.policy_filtered_tools().await;
|
||||
|
||||
for tool in &tools {
|
||||
let is_object = match tool.input_schema.get("type") {
|
||||
Some(serde_json::Value::String(t)) => t == "object",
|
||||
None => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_object {
|
||||
assert!(
|
||||
tool.input_schema.get("properties").and_then(serde_json::Value::as_object).is_some(),
|
||||
"advertised tool {} inputSchema must publish 'properties'",
|
||||
tool.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connection_selector_schema_uses_optional_strings() {
|
||||
let server = DbxMcpServer::with_runtime_options(Arc::new(FakeBackend::default()), McpScope::default(), false);
|
||||
|
||||
Reference in New Issue
Block a user