Hmbown/CodeWhale · error · anyhow::Error
Failed to find MCP server
Error message
Failed to find MCP server: {server} What it means
require_server() checks the requested MCP server name against the allow rules (server_allowed) and fails with this message when the name is not permitted. The wording deliberately mirrors "server not found" so the model cannot probe which servers exist; the real cause is a disallow rule, not a missing server.
Solutions
- Check the server name spelling against the configured MCP servers.
- Confirm the server is in the allow rules (not in disallowed_tools) in the MCP config.
- List available servers via the MCP listing tooling before dispatching a call.
- Reconnect or reload config if the server was recently enabled.
Example fix
// before
conn.call("db-server", "query", input)?
// after: guard with the same allow rule the pool uses
if !pool.server_allowed("db-server") { return Err(...); }
conn.call("db-server", "query", input)? Defensive patterns
Strategy: validation
Validate before calling
// Resolve the server list and check before calling
let servers: Vec<String> = pool.list_server_names();
assert!(servers.iter().any(|s| s == server), "unknown MCP server: {server}"); Type guard
fn server_in_list(servers: &[String], name: &str) -> bool {
servers.iter().any(|s| s == name)
} Try / catch
match pool.call_tool(server, tool, input) {
Err(e) if e.to_string().contains("Failed to find MCP server") => {
eprintln!("server '{server}' is not available; check spelling and config");
}
other => other?,
} Prevention
- Pick server names from the configured list rather than typing them.
- Reload config in long-lived sessions after editing MCP servers.
- Keep server names stable across renames or provide aliases.
- Surface the allowed server list to users/UI before dispatch.
When it happens
Trigger: Invoking an MCP tool whose input names a server that does not match any allowed server rule — i.e. tool_matches_any_rule matched the disallowed_tools/server-deny configuration for that server name.
Common situations: Typo in the server name in a tools/call; calling an MCP server that was removed from or never added to the allowlist; stale session referencing a server disabled in config.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- A managed, project or plugin connector already uses this…
- Absolute path should not warn
- active plugin registry is missing its pre-dotenv…
- app_denied
- {backoff.last_error}
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/21ff8a4ad0677659.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp.rs:2917
pub(crate) fn server_denied_by(rules: &[String], server: &str) -> bool {
let namespace = format!("mcp_{server}_").to_ascii_lowercase();
rules.iter().any(|rule| {
rule.to_ascii_lowercase()
.strip_suffix('*')
.is_some_and(|prefix| namespace.starts_with(prefix))
})
}
fn server_allowed(&self, server: &str) -> bool {
!Self::server_denied_by(&self.disallowed_tools, server)
}
pub(crate) fn tool_allowed(&self, name: &str) -> bool {
!crate::core::engine::tool_catalog::tool_matches_any_rule(&self.disallowed_tools, name)
}
fn require_server(&self, server: &str) -> Result<()> {
anyhow::ensure!(
self.server_allowed(server),
"Failed to find MCP server: {server}"
);
Ok(())
}
pub(crate) fn authorize_call(
rules: &[String],
name: &str,
input: &serde_json::Value,
) -> Result<()> {
anyhow::ensure!(
!crate::core::engine::tool_catalog::tool_matches_any_rule(rules, name),
"Unknown MCP tool name: {name}"
);
if matches!(
name,
"list_mcp_resources"View on GitHub (pinned to 73e0f67d83)