searchts
This server acts as a bridge between AI agents and the web, enabling them to read, search, and download internet content by bypassing common bot-walls.
read_url– Fetch any URL as clean Markdown using an escalating unlocker ladder (Chrome-fingerprint fetch → JS-rendering relay → stealth browser) that bypasses Cloudflare, DataDome, PerimeterX, and similar systems. Includes prompt-injection detection and scrubbing.web_search– Perform multi-provider web searches returning ranked, de-duplicated results (title, URL, snippet). Works keylessly via DuckDuckGo by default, with optional support for SearXNG, Exa, Brave, and Tavily.fetch_asset– Download a single asset file (image, PDF, font, CSS, etc.) through the same unlock ladder, saving it to disk and returning its path, content type, and size.grab_site– Fetch a page and bulk-download all its assets (images, icons, CSS, fonts, SVGs), extract its color palette and fonts in use, and return a full manifest. Useful for design inspiration or offline archiving.get_status– Get a diagnostic report of which unlocker tiers, search providers, and optional integrations are installed, configured, and working — with fix hints for anything broken.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@searchtsread https://example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
searchts
The missing layer between AI and the web. A Python CLI and library that lets an AI agent read and search the internet, fronted by a fully open-source "unlocker" that gets through common bot-walls with no paid proxy and no API key.
Why searchts?
Reads pages behind common bot walls
Reads complete ChatGPT / Claude / Gemini / Grok / Poe / DeepSeek / Perplexity / Copilot shared conversations
Works with Claude, Codex, and MCP agents
Extracts clean Markdown, ready to feed a model
Searches the web without API keys
Downloads a page's assets (images, fonts, palette)
Transcribes videos, subtitles-first
Related MCP server: Webustler
Why it's free
AI agents constantly need to read web pages, but the naive way they fetch is trivially blocked by modern anti-bot systems (Cloudflare, PerimeterX, DataDome). Paid unlocker services solve this, but the thing they really charge for is a large pool of clean residential IP addresses. searchts runs on your own machine, from your own connection, at personal volume, so it sidesteps that cost and gets through most of those walls for free.
The unlocker
searchts reads any URL through an escalating ladder and stops at the first tier that returns real content:
curl_cffi: a fetch that impersonates a real Chrome's TLS/JA3 and HTTP2 fingerprint. Beats user-agent and fingerprint filters. Fast, local, private.
Jina Reader: a JavaScript-rendering relay (
r.jina.ai), for pages that only fill in content after running JS. Default on — the target URL is sent to Jina's servers on this rung. Opt out withSEARCHTS_NO_JINA=1or configjina: false(local curl + stealth only).stealth browser: an undetected headless Chromium (patchright), launched lazily only when the cheaper tiers fail, for live JS / Cloudflare managed challenges.
If no tier comes back with real content, an optional human-in-the-loop step opens a real browser so you can clear the page once and continue. That covers interactive CAPTCHAs and soft walls alike: a login page served as HTTP 200 is not a challenge, but it is still a page only a human gets past. Block detection is phrase-based (not vendor-name based), so legitimate pages that merely embed a bot-sensor script are not falsely rejected. Content is extracted to clean Markdown with trafilatura.
Walls (F12 playbook, not a bypass): fail loud on login/challenge/thin. Do not cut a release that claims Reddit/LinkedIn now read (N7). Order: stealth already retries page.content after a navigation race (P3.11) → next is a persistent Chromium profile so clearance can survive across reads (F1, not shipped) → then --human / device session for extras only (F7, never silent, never inside read_url). Never paid residential as default (N1). Never a keyed commercial unlocker as default (N3).
AI-chat share links
Share links from AI chat apps are a special kind of hard: the conversation never appears in the page HTML as extractable text, so generic readers (and most AI agents' built-in fetch) return an empty shell or a fragment cut off mid-chat. searchts read recognizes these URLs and decodes each provider's own data channel instead, returning the complete conversation as role-labeled Markdown — keyless, no login:
Provider | Share URL | How it's read |
ChatGPT |
| turbo-stream payload embedded in the page |
Claude |
| keyless snapshot API (behind Cloudflare) |
Gemini |
| keyless batchexecute RPC |
Grok |
| keyless share-links API |
Poe |
|
|
DeepSeek |
| stealth render, scrolled to the end |
Perplexity |
| stealth render, scrolled to the end |
Copilot |
| stealth render, scrolled to the end |
The first five need no browser. The last three are JavaScript shells with nothing in the initial HTML, so those reuse the stealth tier: wait for the conversation to render, auto-scroll until the page height stops changing (list virtualization will otherwise truncate a long chat), then expand the collapsed sections before reading. The benchmark currently covers the five that read without a browser and passes all five; the three that need one are not in it yet.
ChatGPT issues two shapes: /share/<uuid> for a whole conversation, and the
newer /s/<prefix>_<id> short links for a single shared turn (t_ thread,
m_ message, dr_ deep research, cd_ Codex). Both are read.
Each provider is a drop-in plugin module (searchts/share_extractors/); if a provider changes its format, extraction falls back to the normal unlocker ladder instead of failing.
Install
Keep it (global isolated CLI, MCP extra included):
pipx install "searchts[mcp]"Try it without installing (one-shot, copy-paste):
uvx --from "searchts[mcp]" searchts <verb>venv / packaging only (not the recommended path for the CLI):
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "searchts[mcp]"
# optional stealth-browser extra, still venv-only:
# pip install "searchts[browser]" && patchright install chromiumQuickstart
searchts read https://en.wikipedia.org/wiki/Ada_Lovelace # fetch a page as clean Markdown
searchts search "open source vector db" # multi-provider web search (keyless by default)
searchts transcribe https://youtu.be/... # transcript of a YouTube/TikTok/Instagram/Reddit video
searchts grab https://example.com # download a page's assets + extract palette/fonts
searchts get https://example.com/logo.png # download one asset (image/PDF/font/file)
searchts doctor # see what is configured and workingread flags: --json, --backend <tier>, --human (hand off a CAPTCHA or login wall to a real browser), --scrub (redact injection).
search flags: -n <count>, --json, --provider <name>. Content goes to stdout (pipeable); status to stderr.
grab flags: --out <dir>, --kinds <images,icons,css,fonts,svg>, --read (also save page.md), --max <n>, --json.
Use it from your AI agent
Add searchts to your agent in one line - as an MCP server, or as a Claude Code slash command:
Two ways, both one command:
# 1) MCP: always-on read_url + web_search + fetch_asset + grab_site + get_status
# Try / no install / Claude cannot see PATH:
claude mcp add searchts -- uvx --from "searchts[mcp]" searchts mcp serve
# Keep (after pipx install "searchts[mcp]"):
# claude mcp add searchts -- searchts mcp serve
# Desktop / Cursor JSON: `searchts mcp install` (or uvx the same serve command)
# First read: Wikipedia — example.com is thinner than _MIN_CHARS and looks like a failed install.
# 2) Slash command: type /searchts <url-or-query> in Claude Code
searchts skill install # writes ~/.claude/commands/searchts.mdSee the MCP server reference for all five tools (read_url, web_search, fetch_asset, grab_site, get_status), their inputs and outputs, and when to use each.
Features
Escalating open-source unlocker: curl_cffi, then Jina Reader, then a stealth browser.
Multi-provider search with rank fusion: DuckDuckGo (keyless default), plus SearXNG, Exa, Brave, and Tavily when configured; results merged with reciprocal rank fusion and de-duplicated.
Video transcription: yt-dlp audio plus Whisper for YouTube, TikTok, Instagram, and Reddit videos.
Asset + design grabber:
searchts grab <url>downloads a page's images/icons/css/fonts and extracts a color palette plus the fonts in use;searchts get <url>pulls a single asset. Both go through the same escalating unlock ladder, so they work on fingerprint-gated CDNs, not just open ones.Prompt-injection scrubbing: strips invisible/bidi characters, flags injection indicators, optional redaction, so untrusted page content is safer to feed a model.
Per-domain backend memory: remembers which tier worked per domain and tries it first (
SEARCHTS_NO_MEMORY=1to disable).Jina opt-out: the JS-render relay is on by default;
SEARCHTS_NO_JINA=1(orjina: falsein~/.searchtsconfig) skips it so URLs never hitr.jina.ai.Surfaces: a CLI, an MCP server (
read_url,web_search,fetch_asset,grab_site,get_status), and a Python library.
Use as a library
from searchts import unlocker
r = unlocker.fetch("https://example.com")
print(r.backend, r.status, r.text)
from searchts.search import search
for hit in search("open source vector db", max_results=5):
print(hit.title, hit.url)Does it actually work?
Rather than take our word for it, searchts ships a reproducible two-suite benchmark: it runs the unlocker over two page sets and reports how many it read — keyless — and which tier carried each.
Smoke — a small public page set (control, open docs, AI-chat share links). A regression canary, not evidence about hard bot-walls.
Walled — real vendors that restrict bots (Reddit, LinkedIn login wall, a Cloudflare/DataDome-class site, X, Booking). A short body under the unlocker's minimum-content threshold is a fail, not a pass; expected walled failures are reported honestly, not papered over with 100%.
These two are reported separately on purpose — the smoke number is not "does it work on walls." See benchmarks/README.md.
python -m benchmarks.run # both suites, print a scorecard
python -m benchmarks.run --suite walled # the real walled pass rate only
python -m benchmarks.run --out docs/ # write docs/scorecard.md + results.jsonLatest run: docs/scorecard.md. Add your own targets — see benchmarks/README.md.
The numbers only mean something from a residential connection: a datacenter IP (or a VPN that reshapes your TLS fingerprint) blocks the fast curl_cffi tier more than a real user sees.
How it works, and its limits
It runs from your own residential IP at personal volume, which is why it needs no paid proxy pool. It is a personal-grade research tool, not a mass-scraping system.
Interactive CAPTCHAs (DataDome / Turnstile press-and-hold) and login walls are the honest ceiling. Use
--humanfor those.Some platforms (notably Instagram, and YouTube in 2026) may need your browser cookies or fail intermittently; that is platform-side.
Anti-bot systems evolve; this is an arms race and the techniques may need occasional updates. Respect each site's terms of service and use responsibly.
Configuration
Search works with no keys (DuckDuckGo). Everything else is optional, via searchts configure or a .env (see .env.example):
Search providers: Exa, Brave, Tavily API keys, or a self-hosted
SEARXNG_URL, for more and better results.Transcription: a Groq or OpenAI (Whisper) key, plus
ffmpegandyt-dlp.GitHub token for higher rate limits.
Run searchts doctor to check what is configured and working.
Optional integrations
The core is read / search / transcribe. Every searchts read goes through
unlocker.fetch — there is no per-platform router. searchts doctor only probes
whether optional CLIs (gh, twitter-cli, opencli, mcporter) are on PATH
and authenticated. Presence is not a claim that searchts reads those sites
through those CLIs.
Roadmap
See ROADMAP.md for where searchts is headed — and what's deliberately out of scope.
Credits
searchts builds on and extends Agent-Reach (MIT), reusing its channel, installer, and diagnostics architecture. The escalating open-source unlocker, multi-provider search with rank fusion, prompt-injection scrubbing, per-domain backend memory, the human-in-the-loop CAPTCHA flow, the video transcript channels, the read_url / web_search MCP tools, and the read / search CLI commands are additions in searchts. Thanks to the original authors.
License
MIT. See LICENSE. Original portions Copyright (c) 2025 Agent Eyes; modifications and additions Copyright (c) 2026 capad-xyz.
Built by capad. Questions or feedback: open an issue or email oss@capad.fyi.
Fun fact: "searchts" doesn't officially abbreviate anything. Off the record, it stands for "search this shit".
Available Tools
5 toolsfetch_assetA
Download a single asset file (image, PDF, font, CSS, any file) from its URL through the same unlock ladder as read_url, save it to disk, and return {path, content_type, bytes} as JSON. Use this for one specific file by its direct URL; to pull a whole page's assets at once use grab_site instead. Saves into out_dir when given, otherwise the current directory. Returns an 'Error: ...' string on failure.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| out_dir | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral disclosure burden. It discloses that the tool saves to disk, respects out_dir with a default fallback, returns {path, content_type, bytes} as JSON, and returns an Error string on failure. It does not explain the 'unlock ladder' mechanism in detail, but the reference to read_url provides enough context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three tightly packed sentences with no filler. The core action and return shape come first, usage guidance follows, and failure behavior ends the description. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with an output schema, this description provides enough context to select and invoke the tool correctly: what it downloads, where it saves, what it returns, and how it differs from grab_site. The only mild gap is that 'same unlock ladder as read_url' presumes familiarity with read_url's behavior, but siblings are available for that context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains url as a direct URL to one asset file and out_dir as the save destination with current-directory fallback. It does not specify filename derivation or accepted URL schemes, but it adds meaning beyond the bare parameter names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific action (download), a specific resource (a single asset file by direct URL), and the concrete outcomes (save to disk, return JSON). It also distinguishes itself from grab_site by explicitly stating it is for one file rather than a whole page's assets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use this tool ('for one specific file by its direct URL') and names the alternative for the opposite case ('to pull a whole page's assets at once use grab_site instead'). This gives an agent clear routing guidance without requiring schema inspection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_statusA
Report the health of this searchts install: which unlocker tiers, search providers, and optional platform integrations are installed, configured, and working. Use this first when another searchts tool fails or before relying on an optional capability (e.g. keyed search providers, transcription). Takes no arguments and performs no web requests; returns a human-readable text report, one line per channel with an ok/warn/error status and a fix hint.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does well: it discloses zero arguments, no web requests, and the return format including per-line ok/warn/error status and fix hints. It could also explicitly mention that it makes no changes to the system, but the health-report framing makes that reasonably clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler: purpose, usage guidance, and behavioral details are each packed efficiently. Critical information is front-loaded in the first sentence and the usage guidance is precise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-argument diagnostic tool with an output schema and no annotations, the description covers everything needed: what is checked, when to invoke it, that it is read-only, and what the response looks like. No meaningful gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema already fully communicates the input contract. The description reinforces this with 'Takes no arguments,' removing any ambiguity, which justifies a strong score despite the lack of parameter detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Report the health of this searchts install' and enumerates exactly what is covered (unlocker tiers, search providers, platform integrations). It is immediately distinguishable from sibling tools like read_url and web_search, which perform web actions rather than local diagnostics.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use the tool: 'Use this first when another searchts tool fails or before relying on an optional capability.' It also clarifies what the tool does not do ('performs no web requests'), which prevents misuse relative to the sibling web tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
grab_siteA
Grab a page for design inspiration: fetch it through the unlock ladder, download its assets (images/icons/css/fonts/svg), extract the color palette and the fonts in use, and return a manifest (with local file paths) as JSON. Use this for a whole page's design/assets at once; for a single known file use fetch_asset. Saves into out_dir when given, otherwise a 'searchts-grab-' folder. Set read=true to also save the page text as page.md. Returns an 'Error: ...' string on failure.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| read | No | ||
| out_dir | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full behavioral burden. It discloses the fetch mechanism, the disk-saving behavior (out_dir or default folder), the optional page.md side effect, and the 'Error: ...' failure return. This is strong transparency for a download/capture operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet complete: it front-loads the core purpose, then gives routing guidance, parameter behavior, and error handling. Every sentence earns its place, with no repetition of schema defaults or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a multi-step tool with no annotations and no schema parameter descriptions, this description covers what, when, how, parameter side effects, save location, and failure mode. The output schema handles the manifest structure, so the prose does not need to enumerate return fields.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains read=true (saves page text as page.md), out_dir semantics including the default 'searchts-grab-<host>' folder, and the url parameter is self-evident from 'Grab a page'. An agent can set all three parameters correctly from this text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource (a page), the specific actions (fetch through the unlock ladder, download assets, extract palette/fonts, return a manifest JSON), and explicitly contrasts with fetch_asset for single files. The phrase 'a whole page's design/assets at once' clearly anchors its scope and differentiates it from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance ('Use this for a whole page's design/assets at once') and an explicit alternative for a different case ('for a single known file use fetch_asset'). The read flag condition is also explained, so an agent can decide correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_urlA
Read one web page as clean Markdown, escalating through an unlocker ladder (Chrome-fingerprint fetch -> JS-rendering relay -> stealth browser) that stops at the first tier returning real content. Use this when a plain HTTP fetch is blocked (403/429, a Cloudflare/DataDome/PerimeterX bot-wall, or an 'enable JavaScript' page), the content is rendered client-side, or a previous web_search snippet was blocked, thin, or empty. Do not answer from a blocked snippet — call this tool on that URL. Returns Markdown ready to feed a model, always strips invisible/control characters, and if prompt-injection indicators are detected it fences the body as untrusted and prepends a one-line warning. Returns an 'Error: ...' string (not an exception) when every tier fails.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly. It discloses the unlocker ladder, the stop-at-first-success behavior, stripping of invisible/control characters, prompt-injection fencing with a warning line, and the fact that failures return an 'Error: ...' string rather than an exception.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: purpose, when to use, return format, and failure behavior. It is front-loaded with the core action and then organizes supporting details logically without repetition or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description is complete: it covers the decision to call it, the internal escalation behavior, output format, sanitation, security handling, and error behavior. There are no major gaps an agent would need filled to select or invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is only one parameter, url, and the schema provides no property description. The description compensates by explaining the URL is for a web page and by pointing the agent to 'call this tool on that URL' from a blocked snippet. It does not spell out URL formats, but the intent is clear enough for a single self-describing parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: 'Read one web page as clean Markdown,' and clearly distinguishes itself from plain HTTP fetch and web_search snippets. However, it does not explicitly differentiate itself from sibling tools like fetch_asset or grab_site, relying on the reader to infer those boundaries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use conditions: blocked HTTP fetch, bot-walls, client-side rendering, or a blocked/thin/empty web_search snippet. It even states a direct directive: 'Do not answer from a blocked snippet — call this tool on that URL.' This is clear, actionable routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
web_searchA
Search the web across multiple providers and return a ranked, de-duplicated list of results (title + URL + snippet), fusion-merged with reciprocal-rank fusion. Keyless by default (DuckDuckGo); also uses SearXNG/Exa/Brave/Tavily when their keys are configured. Use this to discover URLs or answer open-ended questions before reading pages. Snippets are not the page: if you need the content, or a hit is 403/429/challenge/thin, call read_url on that URL. Do not answer from the snippet. Returns a formatted text block, or an 'Error: ...' string when every provider fails.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even though no annotations are provided, the description carries the full burden and does so well. It discloses provider behavior (keyless DuckDuckGo default, optional SearXNG/Exa/Brave/Tavily), deduplication with reciprocal-rank fusion, and the exact failure return format: "an 'Error: ...' string when every provider fails." It also warns that snippets are not the page, a meaningful behavioral caveat.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core behavior, then provider details, then usage guidance, then return format. Every sentence carries operational value, including the direct "Do not answer from the snippet" rule. It is detailed without being bloated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (2 parameters, 1 required) and the description covers provider fallback, usage boundaries, and error behavior. It explicitly names the sibling to call for follow-up reading, and because an output schema exists, the description does not need to detail the return format further.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for missing parameter documentation. It implicitly covers query by describing the search use case, but it never explicitly explains max_results or how it shapes the returned list. The parameter names and the default value of 5 carry most of the meaning, so the description adds only marginal value here.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "Search the web across multiple providers and return a ranked, de-duplicated list of results." It clearly distinguishes this tool from read_url and the other siblings by defining its output as a search result list rather than page content. The phrase "Use this to discover URLs" reinforces the intended purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: "Use this to discover URLs or answer open-ended questions before reading pages." It also provides a clear exclusion: "if you need the content, or a hit is 403/429/challenge/thin, call read_url on that URL." This tells the agent exactly when to prefer a sibling tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.8.0- Changed
fetch_asset7 fields changed- added
Input schema / properties / out_dir / defaultAdded value: +"" - removed
Input schema / properties / out_dir / descriptionRemoved value: -"Directory to save into (optional; defaults to the current directory)." - added
Input schema / properties / out_dir / titleAdded value: +"Out Dir" - removed
Input schema / properties / url / descriptionRemoved value: -"Direct URL of the asset file to download." - added
Input schema / properties / url / titleAdded value: +"Url" - added
Input schema / titleAdded value: +"fetch_asset_toolArguments" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "title": "Result", + "type": "string" + } + }, + "required": [ + "result" + ], + "title": "fetch_asset_toolOutput", + "type": "object" +}
- Changed
get_status2 fields changed- added
Input schema / titleAdded value: +"get_status_toolArguments" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "title": "Result", + "type": "string" + } + }, + "required": [ + "result" + ], + "title": "get_status_toolOutput", + "type": "object" +}
- Changed
grab_site10 fields changed- added
Input schema / properties / out_dir / defaultAdded value: +"" - removed
Input schema / properties / out_dir / descriptionRemoved value: -"Directory to save into (optional; defaults to 'searchts-grab-<host>')." - added
Input schema / properties / out_dir / titleAdded value: +"Out Dir" - added
Input schema / properties / read / defaultAdded value: +false - removed
Input schema / properties / read / descriptionRemoved value: -"If true, also save the page text as page.md (default false)." - added
Input schema / properties / read / titleAdded value: +"Read" - removed
Input schema / properties / url / descriptionRemoved value: -"URL of the page to grab." - added
Input schema / properties / url / titleAdded value: +"Url" - added
Input schema / titleAdded value: +"grab_site_toolArguments" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "title": "Result", + "type": "string" + } + }, + "required": [ + "result" + ], + "title": "grab_site_toolOutput", + "type": "object" +}
- Changed
read_url4 fields changed- removed
Input schema / properties / url / descriptionRemoved value: -"Absolute http(s) URL of the page to read." - added
Input schema / properties / url / titleAdded value: +"Url" - added
Input schema / titleAdded value: +"read_url_toolArguments" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "title": "Result", + "type": "string" + } + }, + "required": [ + "result" + ], + "title": "read_url_toolOutput", + "type": "object" +}
- Changed
web_search7 fields changed- added
Input schema / properties / max_results / defaultAdded value: +5 - removed
Input schema / properties / max_results / descriptionRemoved value: -"How many results to return (default 5; clamped to 1-25)." - added
Input schema / properties / max_results / titleAdded value: +"Max Results" - removed
Input schema / properties / query / descriptionRemoved value: -"The search query." - added
Input schema / properties / query / titleAdded value: +"Query" - added
Input schema / titleAdded value: +"web_search_toolArguments" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "properties": { + "result": { + "title": "Result", + "type": "string" + } + }, + "required": [ + "result" + ], + "title": "web_search_toolOutput", + "type": "object" +}
5 tool updates
v0.5.1- First observed
fetch_asset - First observed
get_status - First observed
grab_site - First observed
read_url - First observed
web_search
TDQS
Scored across 5 tools
Each tool serves a clearly distinct purpose: status check, read a single page, perform a web search, download a single asset, and grab an entire page's assets/design. There is no overlap in usage—read_url vs fetch_asset are differentiated by 'readable content' vs 'raw file', and grab_site is explicitly for whole-page design extraction.
All five tools follow a consistent snake_case verb_noun naming pattern (get_status, read_url, web_search, fetch_asset, grab_site). The verbs clearly indicate the action and the nouns the target, making the tool surface predictable and easy to navigate.
The tool count of 5 is well-scoped for a search-and-retrieval server. Each tool addresses a distinct user need—searching, reading, fetching, grabbing, and status—without redundancy or bloat. It feels neither thin nor overloaded.
The tool surface covers the full retrieval workflow: discover via web_search, read a page via read_url, download a single file via fetch_asset, capture a full design via grab_site, and check the system state via get_status. There are no obvious dead ends, and the tools complement each other well (e.g., search snippets lead to read_url).
Maintenance
Related MCP Connectors
Web scraping for AI agents. Converts URLs to clean, LLM-ready Markdown with anti-bot bypass.
Clean Markdown and AI-readability scoring for any URL. Built for AI agents.
Read a URL as clean markdown, screenshot a website, url to PDF. Web access for agents, no signup.
Fetch any URL and get clean Markdown. Web scraping for AI agents.
Related MCP Servers
- AlicenseDqualityDmaintenanceEnables AI assistants to reliably fetch web content as markdown and search the web by bypassing bot detection and rendering JavaScript. Provides tools to unblock URLs and search the web with results converted to markdown format.216MIT
- AlicenseNot gradedqualityCmaintenanceEnables clean, LLM-ready markdown extraction from any URL with automatic anti-bot bypass.3MIT
- AlicenseAqualityBmaintenanceEnables AI agents to read web pages reliably, returning clean markdown content, hyperlinks, and metadata without navigation or ad noise.37MIT
- AlicenseAqualityCmaintenanceEnables AI agents to fetch any web page as clean markdown or screenshot it, turning URLs into LLM-ready context.211MIT