fix(mcp): omit declared-but-empty prompt/resource capabilities from .info mcp-server
This commit is contained in:
@@ -805,23 +805,23 @@ impl RequestContext {
|
||||
if features.resources {
|
||||
let resources = handle.list_all_resources().await;
|
||||
let templates = handle.list_all_resource_templates().await;
|
||||
capabilities.push(if resources.is_err() && templates.is_err() {
|
||||
"resources (declared, list failed)".to_string()
|
||||
} else {
|
||||
let count = resources.map(|r| r.len()).unwrap_or_default()
|
||||
+ templates.map(|t| t.len()).unwrap_or_default();
|
||||
match count {
|
||||
0 => "resources (declared, none served)".to_string(),
|
||||
n => format!("resources ({n})"),
|
||||
}
|
||||
});
|
||||
let any_failed = resources.is_err() || templates.is_err();
|
||||
let count = resources.map(|r| r.len()).unwrap_or_default()
|
||||
+ templates.map(|t| t.len()).unwrap_or_default();
|
||||
if count > 0 {
|
||||
capabilities.push(format!("resources ({count})"));
|
||||
} else if any_failed {
|
||||
capabilities.push("resources (declared, list failed)".to_string());
|
||||
}
|
||||
}
|
||||
if features.prompts {
|
||||
capabilities.push(match handle.list_all_prompts().await {
|
||||
Ok(prompts) if prompts.is_empty() => "prompts (declared, none served)".to_string(),
|
||||
Ok(prompts) => format!("prompts ({})", prompts.len()),
|
||||
Err(_) => "prompts (declared, list failed)".to_string(),
|
||||
});
|
||||
match handle.list_all_prompts().await {
|
||||
Ok(prompts) if prompts.is_empty() => {}
|
||||
Ok(prompts) => capabilities.push(format!("prompts ({})", prompts.len())),
|
||||
Err(_) => {
|
||||
capabilities.push("prompts (declared, list failed)".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const INFO_LABEL_WIDTH: usize = 15;
|
||||
@@ -8746,7 +8746,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn info_mcp_server_annotates_declared_but_empty_capabilities() {
|
||||
fn info_mcp_server_omits_declared_but_empty_capabilities() {
|
||||
let mut ctx = RequestContext::new(mcp_app_state(&["fixture"]), WorkingMode::Cmd);
|
||||
|
||||
let info = run_async(async {
|
||||
@@ -8762,12 +8762,11 @@ mod tests {
|
||||
ctx.mcp_server_info("fixture").await.unwrap()
|
||||
});
|
||||
|
||||
assert!(
|
||||
info.contains(
|
||||
"capabilities tools, resources (declared, none served), prompts (declared, none served)"
|
||||
),
|
||||
"got:\n{info}"
|
||||
);
|
||||
let cap_line = info
|
||||
.lines()
|
||||
.find(|line| line.starts_with("capabilities"))
|
||||
.unwrap();
|
||||
assert_eq!(cap_line, "capabilities tools", "got:\n{info}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -8795,6 +8794,32 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn info_mcp_server_annotates_partial_resource_listing_failure() {
|
||||
let mut ctx = RequestContext::new(mcp_app_state(&["fixture"]), WorkingMode::Cmd);
|
||||
|
||||
let info = run_async(async {
|
||||
let (runtime, _server) = fixture_runtime(FixtureServer {
|
||||
resources_capability: true,
|
||||
empty_resource_listings: true,
|
||||
fail_template_listings: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
ctx.tool_scope.mcp_runtime = runtime;
|
||||
ctx.mcp_server_info("fixture").await.unwrap()
|
||||
});
|
||||
|
||||
let cap_line = info
|
||||
.lines()
|
||||
.find(|line| line.starts_with("capabilities"))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cap_line, "capabilities tools, resources (declared, list failed)",
|
||||
"got:\n{info}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn info_mcp_server_errors_when_unconfigured_or_not_running() {
|
||||
let ctx = RequestContext::new(mcp_app_state(&["gh"]), WorkingMode::Cmd);
|
||||
|
||||
@@ -645,6 +645,7 @@ pub(crate) mod test_fixtures {
|
||||
pub(crate) prompts_capability: bool,
|
||||
pub(crate) hostile_prompt: bool,
|
||||
pub(crate) fail_resource_listings: bool,
|
||||
pub(crate) fail_template_listings: bool,
|
||||
pub(crate) fail_prompt_listings: bool,
|
||||
pub(crate) empty_resource_listings: bool,
|
||||
pub(crate) empty_prompt_listings: bool,
|
||||
@@ -666,6 +667,7 @@ pub(crate) mod test_fixtures {
|
||||
prompts_capability: false,
|
||||
hostile_prompt: false,
|
||||
fail_resource_listings: false,
|
||||
fail_template_listings: false,
|
||||
fail_prompt_listings: false,
|
||||
empty_resource_listings: false,
|
||||
empty_prompt_listings: false,
|
||||
@@ -756,7 +758,7 @@ pub(crate) mod test_fixtures {
|
||||
_request: Option<PaginatedRequestParams>,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ListResourceTemplatesResult, ErrorData> {
|
||||
if self.fail_resource_listings {
|
||||
if self.fail_resource_listings || self.fail_template_listings {
|
||||
return Err(ErrorData::internal_error("template listing exploded", None));
|
||||
}
|
||||
if self.empty_resource_listings {
|
||||
|
||||
Reference in New Issue
Block a user