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:
2026-08-25 11:37:41 -06:00
parent 7caa24d090
commit 01ada1da18
4 changed files with 92 additions and 74 deletions
+9 -9
View File
@@ -16,7 +16,7 @@ use crate::config::ensure_parent_exists;
use crate::config::paths;
use crate::mcp::{
MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX, MCP_INVOKE_META_FUNCTION_NAME_PREFIX,
MCP_SEARCH_META_FUNCTION_NAME_PREFIX, McpServersConfig,
MCP_SEARCH_META_FUNCTION_NAME_PREFIX, McpServersConfig, is_mcp_meta_function,
};
use crate::parsers::{bash, python, typescript};
use anyhow::{Context, Result, anyhow, bail};
@@ -284,14 +284,9 @@ pub async fn eval_tool_calls(
}
}
let (mcp_calls, sequential_calls): (Vec<_>, Vec<_>) =
to_execute.into_iter().partition(|(_, call)| {
call.name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX)
|| call.name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX)
|| call
.name
.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
});
let (mcp_calls, sequential_calls): (Vec<_>, Vec<_>) = to_execute
.into_iter()
.partition(|(_, call)| is_mcp_meta_function(&call.name));
if !mcp_calls.is_empty() {
let ctx_ref: &RequestContext = ctx;
@@ -2128,6 +2123,11 @@ mod tests {
assert_eq!(MCP_INVOKE_META_FUNCTION_NAME_PREFIX, "mcp_invoke");
assert_eq!(MCP_SEARCH_META_FUNCTION_NAME_PREFIX, "mcp_search");
assert_eq!(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX, "mcp_describe");
assert_eq!(crate::mcp::MCP_READ_META_FUNCTION_NAME_PREFIX, "mcp_read");
assert_eq!(
crate::mcp::MCP_PROMPT_META_FUNCTION_NAME_PREFIX,
"mcp_prompt"
);
}
#[test]