refactor(mcp): centralize meta-function prefix predicates and fix list_tools pagination
Implements T1 of plans/mcp-resources-prompts-design.md (§4.1, §4.6): - Replace list_tools(None) with cursor-following list_all_tools() at the three call sites (start_server catalog build, catalog_items, describe) so paginating servers no longer silently lose tools past page one. - Add MCP_READ/MCP_PROMPT prefix constants (declared nowhere yet; wired in T4/T7) plus centralized helpers MCP_META_FUNCTION_PREFIXES, is_mcp_meta_function, and mcp_meta_function_names. - Mechanically replace every hand-rolled 3-prefix starts_with triple (partition in eval_tool_calls, 3 exclusion triples in select_enabled_functions, 3 inclusion triples + per-server name construction in select_enabled_mcp_servers) with the helpers, preserving the existing lax starts_with matching semantics and the mcp_invoke_* enablement sentinel (sentinel moves to search in T5). - Behavior-neutral: dispatch chains keep their 3 arms, emission stays at exactly 3 meta-functions per server, existing tests unmodified. - Add unit tests: helper classification, prefix-soundness property, lax-matching pin, ordered candidate-name construction.
This commit is contained in:
@@ -23,8 +23,8 @@ use crate::function::{
|
||||
user_interaction::USER_FUNCTION_PREFIX,
|
||||
};
|
||||
use crate::mcp::{
|
||||
MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX, MCP_INVOKE_META_FUNCTION_NAME_PREFIX,
|
||||
MCP_SEARCH_META_FUNCTION_NAME_PREFIX, McpAuthReason, McpAuthRequired, is_auth_required_error,
|
||||
MCP_INVOKE_META_FUNCTION_NAME_PREFIX, McpAuthReason, McpAuthRequired, is_auth_required_error,
|
||||
is_mcp_meta_function, mcp_meta_function_names,
|
||||
};
|
||||
use crate::rag::Rag;
|
||||
use crate::supervisor::Supervisor;
|
||||
@@ -2011,11 +2011,7 @@ impl RequestContext {
|
||||
.functions
|
||||
.declarations()
|
||||
.iter()
|
||||
.filter(|v| {
|
||||
!v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| !is_mcp_meta_function(&v.name))
|
||||
.map(|v| v.name.to_string())
|
||||
.collect();
|
||||
|
||||
@@ -2025,11 +2021,7 @@ impl RequestContext {
|
||||
.functions()
|
||||
.declarations()
|
||||
.iter()
|
||||
.filter(|v| {
|
||||
!v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| !is_mcp_meta_function(&v.name))
|
||||
.map(|v| v.name.to_string()),
|
||||
);
|
||||
}
|
||||
@@ -2102,11 +2094,7 @@ impl RequestContext {
|
||||
.declarations()
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.filter(|v| {
|
||||
!v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
&& !v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| !is_mcp_meta_function(&v.name))
|
||||
.collect();
|
||||
|
||||
if let Some(ref tool_names) = role_filter {
|
||||
@@ -2155,11 +2143,7 @@ impl RequestContext {
|
||||
.functions
|
||||
.declarations()
|
||||
.iter()
|
||||
.filter(|v| {
|
||||
v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| is_mcp_meta_function(&v.name))
|
||||
.map(|v| v.name.to_string())
|
||||
.collect();
|
||||
if let Some(agent) = &self.agent {
|
||||
@@ -2168,12 +2152,7 @@ impl RequestContext {
|
||||
.functions()
|
||||
.declarations()
|
||||
.iter()
|
||||
.filter(|v| {
|
||||
v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name
|
||||
.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| is_mcp_meta_function(&v.name))
|
||||
.map(|v| v.name.to_string()),
|
||||
);
|
||||
}
|
||||
@@ -2190,39 +2169,15 @@ impl RequestContext {
|
||||
|
||||
let item_invoke_name =
|
||||
format!("{}_{item}", MCP_INVOKE_META_FUNCTION_NAME_PREFIX);
|
||||
let item_search_name =
|
||||
format!("{}_{item}", MCP_SEARCH_META_FUNCTION_NAME_PREFIX);
|
||||
let item_describe_name =
|
||||
format!("{}_{item}", MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX);
|
||||
if let Some(values) = app.mapping_mcp_servers.get(item) {
|
||||
server_names.extend(
|
||||
values
|
||||
.split(',')
|
||||
.flat_map(|v| {
|
||||
vec![
|
||||
format!(
|
||||
"{}_{}",
|
||||
MCP_INVOKE_META_FUNCTION_NAME_PREFIX,
|
||||
v.to_string()
|
||||
),
|
||||
format!(
|
||||
"{}_{}",
|
||||
MCP_SEARCH_META_FUNCTION_NAME_PREFIX,
|
||||
v.to_string()
|
||||
),
|
||||
format!(
|
||||
"{}_{}",
|
||||
MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX,
|
||||
v.to_string()
|
||||
),
|
||||
]
|
||||
})
|
||||
.flat_map(mcp_meta_function_names)
|
||||
.filter(|v| mcp_declaration_names.contains(v)),
|
||||
)
|
||||
} else if mcp_declaration_names.contains(&item_invoke_name) {
|
||||
server_names.insert(item_invoke_name);
|
||||
server_names.insert(item_search_name);
|
||||
server_names.insert(item_describe_name);
|
||||
server_names.extend(mcp_meta_function_names(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2251,11 +2206,7 @@ impl RequestContext {
|
||||
.declarations()
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.filter(|v| {
|
||||
v.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|
||||
|| v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||
})
|
||||
.filter(|v| is_mcp_meta_function(&v.name))
|
||||
.collect();
|
||||
|
||||
if let Some(ref server_names) = role_filter {
|
||||
|
||||
@@ -63,10 +63,10 @@ impl McpRuntime {
|
||||
.get(server)
|
||||
.cloned()
|
||||
.with_context(|| format!("{server} MCP server not found in runtime"))?;
|
||||
let tools = server_handle.list_tools(None).await?;
|
||||
let tools = server_handle.list_all_tools().await?;
|
||||
let mut items = HashMap::new();
|
||||
|
||||
for tool in tools.tools {
|
||||
for tool in tools {
|
||||
let item = CatalogItem {
|
||||
name: tool.name.to_string(),
|
||||
server: server.to_string(),
|
||||
@@ -110,9 +110,8 @@ impl McpRuntime {
|
||||
.with_context(|| format!("{server} MCP server not found in runtime"))?;
|
||||
|
||||
let tool_schema = server_handle
|
||||
.list_tools(None)
|
||||
.list_all_tools()
|
||||
.await?
|
||||
.tools
|
||||
.into_iter()
|
||||
.find(|item| item.name == tool)
|
||||
.ok_or_else(|| anyhow!("{tool} not found in {server} MCP server catalog"))?
|
||||
|
||||
Reference in New Issue
Block a user