SearchProvider
NAME
MCP::Server::Tool::Web::SearchProvider - the pluggable seam behind web_search
SYNOPSIS
use MCP::Server::Tool::Web::SearchProvider;
class MyProvider does MCP::Server::Tool::Web::SearchProvider {
method name(--> Str:D) { 'my-provider' }
method search(Str:D $query, Int:D $count --> List:D) {
# ... call out to whatever search backend, then ...
(
%( title => 'Example', url => 'https://example.com/', snippet => '...' ),
);
}
}
DESCRIPTION
web_search does not talk to any particular search engine directly ā
it asks whatever object composes this role. MCP::Server::Tool::Web
picks a concrete implementation (MCP::Server::Tool::Web::Provider::Brave
by default) via its provider attribute; tests compose a tiny fake
that never touches the network. A provider is exactly two methods.
METHODS
method name
method name(--> Str:D) { ... }
A short, stable, lowercase identifier for the provider ('brave',
'my-provider', ...). web_search's JSON result carries this under
its provider key, so callers ā and the model reading the tool
output ā can tell which engine actually answered.
method search
method search(Str:D $query, Int:D $count --> List:D) { ... }
Run one search and return up to $count results as a List of
plain Hashes, each shaped:
titleāStr:D, the result's headline text.urlāStr:D, the result's absolute URL.snippetāStr:D, a short excerpt of the page (empty string when the provider has none, never omitted).ageāStr, OPTIONAL. A human-readable freshness hint ("3 days ago", "2026-08-01", ...) when the provider supplies one. Omitted from the Hash entirely when unknown ā callers must check:exists, not merely definedness.
An empty List is a legitimate, non-error answer (the query matched
nothing, or the provider only had non-web results to offer) ā it is
NOT how failure is reported.
Failure is reported by throwing. Implementations die with a
plain Str message (never a typed exception the caller would have to
know about) whose first line is a teaching sentence ā one a person
(or the model that triggered the call) can act on without reading
source: it names what went wrong, and, wherever one exists, the
specific config key or environment variable that would change the
outcome. MCP::Server::Tool::Web's web_search tool catches
whatever a provider throws and surfaces .message verbatim as the
tool's is_error text, so a vague "search failed" here becomes the
whole story the model gets. Multi-line messages are fine (extra
context on later lines); the first line alone must stand on its own.
$query is guaranteed non-empty and already length-checked by the
tool layer against the provider's advertised limit before this method
is ever called; $count is guaranteed to be a positive integer
within whatever range the tool layer negotiated. A provider is free to
apply its own tighter limits (and should die with a teaching message,
not silently clamp, if it does) but does not need to re-validate the
basics.
AUTHOR
Matt Doughty
COPYRIGHT AND LICENSE
Copyright 2026 Matt Doughty
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.