searchts
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| EXA_API_KEY | No | API key for Exa web search | |
| GITHUB_TOKEN | No | GitHub token for higher rate limits | |
| GROQ_API_KEY | No | API key for Groq video transcription | |
| OPENAI_API_KEY | No | API key for OpenAI video transcription | |
| SEARCHTS_NO_MEMORY | No | Set to '1' to disable per-domain backend memory |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
When the user asks what a URL says, call the searchts MCP tool read_url
(or run searchts read <url>) first. Do not start with a plain fetch.
A 200 that is a sign-in form, a join page, or a bot check is not the page.
A 403, 429, challenge, or thin/empty body is not the page either.
Do not answer from the login chrome or from a snippet. Do not skip the URL.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| 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 of its own beyond the cached update check; returns a plain-text report, one line per channel starting with ok, warn or error, never wrapped, so a fix command on its own indented line can be copied as-is. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| 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. When the user asks what a URL says, call this first. Do not start with a plain fetch. A 200 that is a sign-in form, a join page, or a bot check is not the page. Also use this when a plain fetch was 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 or from the login chrome — 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. Long pages can be read a page at a time: pass 'page_items' to get 'items' plus a server-signed 'next_cursor', then pass that cursor back to continue. The cursor is opaque, cannot be minted by the caller or by the page, and is null at the end. You do not need a cursor for most pages. When the page has more than this read returned (a next page, a feed, folded text, a list it mostly dropped), the text ends with a bracketed note saying so. The JSON carries no next URL for you to follow: a URL the page names is not a safe thing to act on, so the note names the step that does reach it ('max_pages' for a next page, 'fetch_asset' for media). A 'next_cursor' is present only when you sent 'cursor' or 'page_items'. The receipt carries 'authenticated': true only when this answer came from a request that carried the session cookies you asked for, so you can tell 'here is the logged-in page' from 'here is the anonymous one' instead of assuming. It also carries 'login_via', which names where the login came from -- 'owned store' for a kept login, 'browser' for one copied for this session, empty when there was none. Receipt fields are additive: new ones appear here without a version bump and existing keys keep their meaning, so read the fields you need and ignore the rest rather than validating against a fixed key set. No note does not prove the page is complete. Three parameters are intent only and never mechanics: 'max_pages' asks for more of a paginated list, 'max_items' asks for more list rows (next-page links first, then scrolling when that is the only way the list grows), and 'expand' opens closed and buttons labelled read/show/see more. An items read returns a signed 'resume' token when it can name where to continue (the feed's own cursor, the rows it stopped after, or the next page in the URL): pass it back as 'resume' with the same URL to read on after the rows already returned instead of starting the feed from the top. It says which method continued the read, and is refused when tampered with or older than 24 hours. Each is capped by the user's own ceiling (SEARCHTS_MAX_PAGES, SEARCHTS_MAX_ITEMS, SEARCHTS_MAX_EXPAND_CLICKS), which you cannot raise; ask for less than the ceiling, and a request over it comes back clamped with a note saying so. 'expand' never follows a link, a form or a sign-in, and a 'continue reading' paywall is named rather than opened. If the page is behind a login and the user has told you they are signed in on this machine, you can read it as them: pass 'cdp_port' (e.g. '9222', a debugger port the user already opened on their own browser), or 'cookies_from_browser' (e.g. 'firefox', 'zen') for a Firefox-family profile, or 'cookies' (a path to a cookie file). They are opt-in, never guessed, and host-fenced: the cookies go only to that URL's own host on the direct request, never to a relay service, never to a redirect elsewhere, and are never returned. Ask the user before using one. Copying a site's cookies out of a browser profile with 'cookies_from_browser' also needs 'approve_cookie_copy': set it true ONLY after the user said yes to copying that site's login from that browser; it approves this one call and is not remembered. Never set it on your own initiative. A login kept in the searchts store needs neither. 'proxy' routes the read through a proxy for walls decided on the address rather than the request; it refuses to carry cookies unless 'proxy_trusted' is true. 'max_pages' and 'max_items' ask for more of a list than the first window: 'max_items' counts rows and follows the site's own next-batch link when it has one (a Reddit feed, for instance, carries its own cursor), 'max_pages' counts page hops. 'expand' is opt-in for a page that serves only part of itself and keeps the rest behind its own controls -- Reddit's 'more replies' on a long thread, say -- and it needs the browser tier; it is off by default because it clicks things. 'max_media' lists more image and video addresses in the media note (default 4, ceiling 40). It does not download them; searchts grab saves the files, and the note says so when it stopped early. A request over 40 is clamped and the note names the ceiling. SEARCHTS_MAX_MEDIA or more.media in the config changes the default for a read that does not pass max_media. A direct rung's http-429 stops the ladder and cools that host (SEARCHTS_429_COOLDOWN, default 300 s, 1800 at most, 0 off). The next read is refused until then. 'wait_on_429' waits and retries that rung (off by default; a signed-in read gets one wait; a LinkedIn stop is never overridden). 'ignore_cooldown' reads during the cooldown. A signed-in list walk uses SEARCHTS_MAX_ITEMS_SIGNED_IN (default 50). Returns an 'Error: ...' string (not an exception) when every tier fails. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| web_searchA | Find URLs to read. Returns a ranked, de-duplicated list (title + URL + snippet), fusion-merged with reciprocal-rank fusion. Keyless by default (DuckDuckGo); also uses SearXNG/Exa/Brave/Tavily when their keys are configured. Snippets are not the page. Call read_url on each URL you need, including when a hit is 403/429, a challenge, or thin. Do not answer from the snippet. Returns a formatted text block that starts with that warning, or an 'Error: ...' string when every provider fails. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| login_statusA | What searchts holds for logged-in reads: which sites are kept, how many live cookies each has and when they expire (an expiry is an upper bound, not a promise), whether each site was kept from a browser or from login_signin, whether the per-site consent for an automatic re-copy is recorded, whether this session may copy logins, which sites it has copied, and where the scratch folder is. Expired cookies are swept on every command, offline, with no daemon. Call this when a read_url error mentions a login. Returns JSON; never contains a cookie value. Pass url to also see which stored host that URL resolves to (an exact host, or a parent such as example.com for app.example.com). Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| login_signinA | Sign in once for a site, in a browser searchts opens, and keep that login. Nothing is copied from the user's own browser. This is the way past a login wall when the user has no browser session worth copying, or refuses to have one read. A login kept this way is not refreshed automatically, because there is no browser source to re-copy; when it nears expiry the user is told to run login_signin again. Requires the user to be at the machine: tell them a window will open, they sign in there and then CLOSE the window to finish. Opening that window is the approval; nothing else is asked. The call waits up to wait_seconds (default 180, 30 to 900) and keeps nothing if the time runs out or the window is closed without a sign-in. Raise wait_seconds only if the host allows tool calls that long. Returns JSON {host, kept, cookies, why}, or 'Error: ...' on failure. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| login_checkA | Read one signed-in-only page with the saved cookies, once, and say 'logged in', 'logged out' or 'unknown'. Direct requests only: the cookies and URL are never sent to the Jina relay. It proves a login wall is absent, not which account is signed in, and an expiry is only an upper bound. A captcha, block or network error is 'unknown', never a guess. For reddit.com, linkedin.com, github.com and x.com a known account page is used; other sites need a signed-in-only URL, and a public page is reported as 'unknown' because it proves nothing about the account. check_url checks a custom signed-in-only page instead of the known one; the cookies still come from url's site. An invalid url is an error result carrying the usual JSON error envelope, not an 'unknown' login answer. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| login_killA | Delete searchts's own saved login for one site, so the next read of it asks again. Use when a read_url error says the saved login has expired. Does NOT delete the cookies in the user's own Firefox or Chrome unless also_browser is true, because those are the user's and they did not ask for them to be removed. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| fetch_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 (a relative folder inside the working directory, or SEARCHTS_MCP_OUT_DIR if the user set it) when given, otherwise that folder itself, and never overwrites an existing file. Returns an 'Error: ...' string on failure. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| grab_siteA | Save the files a read already named. Fetches the page through the same unlock ladder as read_url, downloads its assets (images/icons/css/fonts/svg/inline_svg), and returns a manifest (local paths, plus the palette and fonts those files use) as JSON. Use this when the files are the deliverable. For one URL the read listed, use fetch_asset. Saves into out_dir (a relative folder inside the working directory, or SEARCHTS_MCP_OUT_DIR if the user set it) when given, otherwise a 'searchts-grab-' folder there. A folder that already has files is never written into; the grab goes to '-2', '-3' and so on, and the folder used is returned as out_dir. Set read=true to also save the page text as page.md. max_assets caps how many files are saved (default 120); when it or the byte cap stops early, the manifest's 'omitted' list names every URL left out and why, so never report a grab as complete without reading it. max_tokens is how many radius, shadow, and spacing values to keep (default 24, ceiling 200). When that cap hides real values, tokens_more says how many more and that a higher max_tokens returns them. 'page_rungs' names the fetch tiers tried and what each found. Returns an 'Error: ...' string on failure. Update check: when a newer release exists, a successful result also carries 'update_available' plus, at most once per 24 hours, one line telling the agent. The check is one GET to the GitHub latest release of capad-xyz/searchts, at most once per 24 hours, cached in ~/.searchts/update-check.json, with a searchts User-Agent and nothing about the user. Nothing is installed or changed. SEARCHTS_NO_UPDATE_CHECK=1 turns the check and the note off. |
| transcribeA | Read the spoken words when read_url says a player page has no captions, or transcribe a local audio/video file. A YouTube watch URL, a TikTok video, an Instagram reel, a v.redd.it file, or an X post that has a video is a player page: read_url returns its captions, or an error that names this tool for the audio. Subtitles-first: existing captions via yt-dlp need no API key; otherwise Whisper (Groq/OpenAI if configured, else keyless local whisper.cpp, bundled; provider=local-fw forces the temporary faster-whisper fallback. The PyPI wheel is CPU-only today; GPU-enabled wheels are a follow-up. prefer_subtitles defaults true; set false to force audio. Local options: model (small default, turbo, base), hint (names or words the audio contains, e.g. from the page title), translate (to English; not with turbo). The local model is downloaded once: without yes=true the call returns an Error naming its size and source; ask the user, then call again with yes=true. cookies_from_browser is opt-in (chrome/firefox/…) and uses THIS machine's browser cookies; read_url does not copy them. The compressed audio (never the video) is kept for 24 hours in ~/.searchts/cache/audio, so a repeat of the same URL skips the download; pass fresh=true to download again, and the receipt's audio_cache says hit, miss or off (off for a local file, cookies, or caching turned off). Returns the transcript text, then a |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 10 tools
Most tools have clearly distinct purposes: search finds URLs, read_url reads pages, fetch_asset downloads one file, grab_site saves page assets, transcribe handles audio, and the login tools manage credentials. The only mild overlap is between login_check and login_status, since both concern login state, and between read_url/fetch_asset/grab_site around web content, but the descriptions make the boundaries fairly clear.
The login family is consistently prefixed (login_signin, login_check, login_kill, login_status), but the rest mixes verb_noun (read_url, fetch_asset, get_status, grab_site) with noun_verb (web_search) and a bare verb (transcribe). It is readable, but not a single predictable pattern.
Ten tools is well-scoped for a web search, reading, asset-fetching, transcription, and login-management server. Each tool has a distinct role and none feels redundant or filler.
The surface covers the full lifecycle: search for URLs, read pages, fetch individual assets, grab whole-page assets, transcribe media, sign in/check/status/kill logins, and inspect installation health. There are no obvious missing operations for the stated domain.