X
NAME
MCP::Server::Tool::Web::X - the typed failures thrown by the web tool pack
SYNOPSIS
use MCP::Server::Tool::Web::X;
# The tool surface catches the base class and reports it as a tool error;
# everything else is a bug and should keep travelling.
{
CATCH {
when X::MCP::Server::Tool::Web {
return %( isError => True, content => [ { type => 'text', text => .message } ] );
}
}
# ... a fetch, a crawl, a grep ...
}
Reacting to particular failures rather than to all of them:
CATCH {
when X::MCP::Server::Tool::Web::Blocked {
# .address-class is 'loopback', 'private', 'metadata', ... ā the
# refusal is teachable without re-reading the message.
note "SSRF floor said no: {.address-class} at {.address}";
note "the operator could permit it with {.config-key}";
}
when X::MCP::Server::Tool::Web::BadUrl {
note "bad URL ({.reason}): {.url}";
}
}
DESCRIPTION
Every failure this distribution raises on its own behalf is an
X::MCP::Server::Tool::Web, so one CATCH arm separates "the web request
went wrong" from "the code around it went wrong". The tool handlers catch the
base class and turn it into an MCP isError result; nothing else is caught,
so a genuine bug still surfaces as a bug.
Two properties are enforced across the whole family, because the consumer of these messages is a language model that has to decide what to do next:
Every
.messagenames the rule that refused the request ā not "denied" but "private address (RFC 1918)", "port out of range", "robots.txt Disallow: /private".Every
.messagenames the configuration key that would change the answer, or says plainly that no key would. A refusal an operator cannot act on is a support ticket; a refusal that namesallow-loopbackis a one-line fix.
The classes are declared as plain global classes ā no unit module, no
is export ā exactly the way Rakudo core declares X::AdHoc. is export
on a nested-name class exports its leaf name too, which makes two
distributions that both ship, say, an X::ā¦::Transport impossible to import
together. Consumers use this file and refer to the full names.
The failures
X::MCP::Server::Tool::Web::BadUrlā the URL was refused on its shape alone (scheme, credentials, obfuscated host, port range). No allow-list rescues these; they are refused before anything is resolved or connected.X::MCP::Server::Tool::Web::Blockedā the address behind the URL is one the SSRF floor will not connect to. Carries the host, the address, the classification and the permitting config key.X::MCP::Server::Tool::Web::TooManyRedirectsā the hop budget ran out. Carries the whole chain, in order.X::MCP::Server::Tool::Web::RedirectLoopā a redirect came back to a URL already visited on this fetch.X::MCP::Server::Tool::Web::Deadlineā a time budget expired. Carries the phase ('connecting', 'reading headers', 'reading the body') so the caller can tell a dead host from a slow one.X::MCP::Server::Tool::Web::Transportā the connection itself failed: refused, reset, DNS miss, TLS rejection.X::MCP::Server::Tool::Web::RobotsRefusedā robots.txt disallows the URL for our user agent.