Skip to main content
Glama
lhswgzy

free-search-mcp-ts

by lhswgzy

free-search-mcp-ts

Local-first web search, page fetching and document parsing for any MCP client — with no API key.

By lhswg (GitHub lhswgzy) · © 2025 · MIT licensed.

Origin. This repository is an independent, from-scratch TypeScript implementation. The idea, and the older and more mature Python project that came first, belong to sweetcornna: sweetcornna/free-search-mcp — MIT, published on PyPI as free-search-mcp. This is not a fork, none of its code was copied or read, and no parity is claimed. Full statement: AUTHORS.md.

free-search-mcp-ts is a Model Context Protocol server that gives Claude, GPT, Cursor, Codex, local Ollama front ends and any other MCP-capable client the ability to search the web, read pages and parse documents. It runs entirely on your machine, needs no account, and returns Markdown instead of JSON because Markdown costs a model roughly a third fewer tokens for the same information.

Not on npm yet. npx -y free-search-mcp-ts will install it once the package is published; until then use the Quick start, which builds it locally in four commands.

CI License: MIT Node Author


Why this exists

Asking a model to "look it up" usually means one of three things: pay for a search API, hand the model a browser it cannot drive, or paste URLs manually. None of them work well for a local model, and none of them keep your queries on your machine.

This server takes a different position:

  • No keys by default. DuckDuckGo, Mojeek and Google News are queried directly over their public endpoints. Nothing to sign up for, nothing to leak.

  • Several engines, not one. Every engine's result list is merged with Reciprocal Rank Fusion, so a page that three engines independently surface outranks a page only one engine found. The result set is stable in a way a single provider's is not.

  • It degrades instead of failing. Engines that are blocked, rate-limited or unreachable are detected, benched, and replaced by the next tier. The server keeps working on networks that filter some providers — one of the engines in the default fallback tier is there precisely because it stays reachable where others do not.

  • Everything stays local. Queries go to the search engine; the pages you read are stored in a local SQLite index on your disk, and nothing else is uploaded anywhere. There is no telemetry.

  • Markdown first. The output is a heading-and-list document a model can read directly. Measured on a ten-result search, the Markdown form is 32 % fewer tokens than the equivalent JSON (see the measurements).

Related MCP server: webfetch

Quick start

This is the fastest path that works today. It needs no npm publication, because it builds the package from source and links the free-search-mcp-ts binary into your PATH:

git clone https://github.com/lhswgzy/free-search-mcp-ts
cd free-search-mcp-ts
npm install && npm run build && npm link
free-search-mcp-ts install

That registers the server with the MCP clients installed on this machine. Then confirm the engines and network work from here, and try it without involving a client at all:

free-search-mcp-ts doctor
free-search-mcp-ts search "state of the art in retrieval augmented generation"
free-search-mcp-ts research "how does reciprocal rank fusion work" --depth 2

Restart your client afterwards. If you would rather see what the installer would do before it touches anything, free-search-mcp-ts install --dry-run prints the plan without writing.

Once the package is published to npm

npx -y free-search-mcp-ts is the intended path for anyone who does not want a local clone, but the package is not on npm yet. Once it is, every command above becomes:

npx -y free-search-mcp-ts install
npx -y free-search-mcp-ts doctor
npx -y free-search-mcp-ts search "state of the art in retrieval augmented generation"
npx -y free-search-mcp-ts install --dry-run

Tools exposed to the model

Tool

What it does

web_search

Searches several engines at once, fuses the results with RRF, de-duplicates and re-ranks. Use it when you do not yet know which page holds the answer.

research

The whole loop in one call: search, derive follow-up queries from the vocabulary the first results actually use, fetch the most relevant and diverse sources, then extract the passages that answer the question. Returns a citable Markdown brief.

fetch_url

Fetches one page or document and returns clean Markdown. Handles HTML, PDF, DOCX, XLSX, PPTX, EPUB, ODT, CSV, JSON and plain text. Long documents are truncated with a continuation offset instead of being refused.

fetch_urls

Fetches up to twelve URLs concurrently, reporting failures per URL.

parse_document

Parses a local file or a remote document: spreadsheets become Markdown tables, presentations become one section per slide, DOCX keeps headings, lists and tables.

search_index

Full-text search over everything fetched so far, using a local SQLite FTS5 index with CJK bigram support. Costs no network requests.

local_index

Reports on, prunes, clears or compacts that local index.

list_engines

Lists every engine with its tier, whether it needs a key, and its live health.

Every tool accepts format: "json" for callers that want to post-process the output instead of reading it.

Engines

Twenty-four engines, in tiers. The tier decides when an engine runs; you can always override with engines: ["bing", "wikipedia"] or ask for everything with engines: ["all"].

Primary — the documented keyless defaults

Engine

Notes

duckduckgo

HTML endpoint with an automatic fallback to the lite endpoint; region, freshness and safe-search parameters.

mojeek

An independent index with its own crawler — useful for escaping the Bing/Google duopoly.

googlenews

Google News RSS: articles rather than a general web index, with when: recency operators.

Fallback — used automatically when the primary tier returns too little

Engine

Notes

bing

Keyless HTML. Reachable on networks where DuckDuckGo and Google are not, which is what makes it the global fallback.

baidu, sogou, so360

Chinese-language engines, eligible for CJK queries or an explicit region: "cn". Their redirect wrappers are decoded locally.

Subject indexes — selected automatically from the query

wikipedia, hackernews, github, stackexchange, arxiv, openalex, crossref, npm, crates

A question about a Rust crate quietly gains crates.io; a question containing an error message gains Stack Overflow. The trigger list lives in src/engines/registry.ts and is plain readable regex.

Optional and keyed

Engine

Notes

startpage, brave

Keyless HTML front ends. Frequently behind a consent or anti-bot wall, which is detected and reported as a block rather than as "no results".

searxng

Point it at your own instance with SEARXNG_URL. Accepts a comma-separated list and fails over between them.

brave-api, serper, tavily, exa, google-cse

Optional API-key engines. When a key is present they join the first tier and get a higher RRF weight, because a documented JSON API beats a scraper.

BRAVE_API_KEY=... npx -y free-search-mcp-ts search "..."      # or SERPER_API_KEY, TAVILY_API_KEY, EXA_API_KEY
SEARXNG_URL=https://searx.example.org npx -y free-search-mcp-ts search "..."

How it works

Reciprocal Rank Fusion. Each engine contributes 1 / (k + rank) for every document it returns, with per-engine weights and a small bonus when several engines agree. RRF needs no calibration between providers, which matters because a Bing relevance score, a Mojeek BM25 score and a Google News publish order are not comparable numbers. See src/rrf.ts.

Tiered escalation. The primary tier runs first. Only if it returns fewer than a threshold does the fallback tier run, and only then do the subject indexes that match the query. A query the primary tier answers costs two or three HTTP requests; a query on a filtered network still succeeds.

A circuit breaker that survives restarts. A provider that is blocked (403/429/captcha) is benched for ten minutes after a single occurrence; a provider whose host cannot be connected to at all is benched for five. That state is written to the local SQLite index, so a restarted server — or your next command-line search — does not pay the timeout again.

Redirect wrappers decoded offline. Baidu, Sogou, 360, DuckDuckGo and Bing all hand back /link?url= or /ck/a?u= wrappers. Those are decoded locally, which fixes both citation quality and cross-engine de-duplication; only wrappers that cannot be decoded cost a redirect-following request.

A local full-text index. Every fetched page is stored in SQLite with an FTS5 index over its title and body. CJK text is expanded into overlapping bigrams at index time, because unicode61 would otherwise treat a whole Chinese sentence as a single token and make it unsearchable.

Markdown extraction. linkedom parses, Mozilla Readability isolates the article, and Turndown renders GitHub-flavoured Markdown — headings, lists, tables, fenced code with language tags and absolute links. When Readability declines a page (a changelog, a spec, a table-heavy docs site) a conservative fallback strips the chrome from the body instead of giving up.

Measured, not claimed

Claim

Measurement

Markdown costs fewer tokens than JSON

On a ten-result search: 3,752 chars / ~938 tokens as Markdown versus 5,550 chars / ~1,388 tokens as JSON — 32 % fewer tokens.

Fetching a page returns far less than the raw HTML

MDN's Fetch API page: 150 kB of HTML → 5.4 kB of Markdown (96 % smaller, ~38,300 → ~1,378 estimated tokens). Bing's and Baidu's result pages measure 97 % and 99 %.

The circuit breaker pays for itself

On a network where DuckDuckGo and Google News are unreachable: first search 11.9 s, subsequently 1.3 s, because the dead engines are benched in between.

Reproduce the first two with npx tsx scripts/measure-savings.mts; the third with two consecutive free-search-mcp-ts search calls.

Clients

free-search-mcp-ts install detects and patches the following. Only clients whose config file already exists are touched, every write is preceded by a .bak copy, and only the one key the server owns is modified.

Client

Config file

Claude Desktop

claude_desktop_config.json

Claude Code

~/.claude.json

Cursor

~/.cursor/mcp.json

Windsurf

~/.codeium/windsurf/mcp_config.json

VS Code (Copilot agent mode)

.vscode/mcp.json or the user profile

Cline

cline_mcp_settings.json

Roo Code

mcp_settings.json

Zed

settings.json (context_servers)

Codex CLI

~/.codex/config.toml

Gemini CLI

~/.gemini/settings.json

opencode

~/.config/opencode/opencode.json

LM Studio

~/.lmstudio/mcp.json

Continue

~/.continue/config.json

npx -y free-search-mcp-ts clients                          # every supported client id
npx -y free-search-mcp-ts install --client cursor --dry-run
npx -y free-search-mcp-ts install --client claude-desktop,codex
npx -y free-search-mcp-ts uninstall --client cursor

Ollama does not speak MCP. The tool-calling loop has to live in the client, so point an MCP-aware front end at this server instead — run it over HTTP and add it in Open WebUI, LibreChat or any other MCP-capable UI:

free-search-mcp-ts serve --transport http --port 8765
# then add http://127.0.0.1:8765/mcp as a streamable-HTTP MCP server

Command line

free-search-mcp-ts                     # serve MCP over stdio (what a client runs)
free-search-mcp-ts install             # register with the clients found here
free-search-mcp-ts uninstall           # remove the registration
free-search-mcp-ts doctor              # check the network, every engine and the local index
free-search-mcp-ts search  "<query>"   # multi-engine search
free-search-mcp-ts research "<query>"  # search + read + extract, as a citable brief
free-search-mcp-ts fetch   <url>       # one page or document as Markdown
free-search-mcp-ts parse   <file>      # a local document as Markdown
free-search-mcp-ts engines [--check]   # list engines, tiers and health
free-search-mcp-ts cache   <stats|search|clear|prune|vacuum|path>
free-search-mcp-ts config              # the effective configuration
free-search-mcp-ts tools [--json]      # the MCP tool manifest
free-search-mcp-ts clients             # supported client ids

Useful flags: --json, --max <n>, --engines a,b, --freshness day|week|month|year, --lang, --region, --depth 1-3, --sources <n>, --data-dir <path>, --verbose.

The subcommands call the same handlers the MCP server registers, so free-search-mcp-ts search "…" is a genuine end-to-end test of what a model would receive.

Configuration

Everything is optional. Copy .env.example to ~/.free-search-mcp/.env, or set the variables in your client's env block.

Variable

Default

Purpose

FREE_SEARCH_ENGINES

auto

Comma-separated engine ids, or auto for the tiered policy.

FREE_SEARCH_MAX_RESULTS

12

Default result count.

FREE_SEARCH_TIMEOUT

15000

Per-request timeout in milliseconds.

FREE_SEARCH_CACHE

1

Set to 0 to disable the local SQLite index.

FREE_SEARCH_CACHE_TTL_HOURS

24

How long a fetched page stays fresh.

FREE_SEARCH_PROXY

—

HTTP(S) proxy, e.g. http://127.0.0.1:7890. Standard HTTPS_PROXY is also honoured.

FREE_SEARCH_RESPECT_ROBOTS

1

Set to 0 to ignore robots.txt when fetching.

FREE_SEARCH_ALLOW_PRIVATE

0

Set to 1 to allow loopback/private addresses (off by default as an SSRF guard).

FREE_SEARCH_REGION, FREE_SEARCH_LANGUAGE

—

Region and language hints, e.g. us / en, cn / zh.

FREE_SEARCH_DATA_DIR

~/.free-search-mcp

Where the index and configuration live.

FREE_SEARCH_LOG_LEVEL

warn

silent, error, warn, info or debug.

A JSON config file at ~/.free-search-mcp/config.json accepts the same keys in camelCase.

Behind a proxy or on a filtered network

The server works out of the box on the open internet. If your network filters some providers, either point it at a proxy:

FREE_SEARCH_PROXY=http://127.0.0.1:7890 free-search-mcp-ts search "..."

or leave it alone and let the tiered fallback do its job — the fallback engines were chosen because they are reachable from networks that block DuckDuckGo and Google. free-search-mcp-ts doctor prints a per-engine sweep so you can see exactly which engines work from where you are.

Security and privacy

  • Nothing leaves your machine except the search queries and the pages you ask for. There is no telemetry, no analytics, and no server component.

  • fetch_url is model-driven, so it is guarded. Loopback, link-local and private-range addresses are refused by default; set FREE_SEARCH_ALLOW_PRIVATE=1 if you deliberately want to read a local service.

  • robots.txt is honoured for fetch_url by default, and a disallowed path is reported as such rather than silently skipped.

  • Local file access is limited to regular files under 100 MB, and only through the explicit parse_document tool.

  • Nothing is written to stdout except JSON-RPC. Diagnostics go to stderr, so a misbehaving log line can never corrupt the protocol stream.

Requirements

  • Node.js 20.19+ to run.

  • Node.js 22.5+ (24+ recommended) for the local SQLite index, which uses the built-in node:sqlite module. On older runtimes the cache degrades to an in-process LRU and everything else keeps working — doctor tells you which backend is active.

  • No native modules, no compiler, no post-install build step.

Verification status

Honesty about what has been tested matters more than a feature list.

  • Verified against live traffic from this machine: Bing, Baidu, Sogou, 360, Hacker News (Algolia), GitHub, Stack Exchange, npm, crates.io, OpenAlex, Crossref, and the full fetch → Markdown → index → re-read pipeline. doctor's engine sweep and the CLI subcommands were exercised end to end, and the MCP server completed a 17-check protocol handshake (initialize, tools/list, tools/call, ping) over stdio.

  • Verified against captured real responses: arXiv, and every engine listed above.

  • Verified against fixtures only, because this network cannot reach them: DuckDuckGo, Mojeek, Google News, Startpage, Brave, SearXNG, Wikipedia. Their parsers, parameter mapping and error paths are covered by tests built from the documented markup and API shapes, but they have not been exercised against a live response. If one of them misbehaves for you, that is the most likely place, and an issue with the raw response attached is the fastest fix.

  • Not verified at all: the five keyed engines' live responses (no keys available here). Their request construction and response mapping are verified offline against fixtures.

Development

git clone https://github.com/lhswgzy/free-search-mcp-ts
cd free-search-mcp-ts
npm install
npm run build          # tsc -> dist/
npm test               # vitest
npm run typecheck
node dist/cli.js doctor

npm run verify runs the whole acceptance sequence — typecheck, build, the test suite, both MCP protocol probes, the CLI surface and a packaging leak check — and prints one summary. npm run verify:online additionally performs a live search, fetch and engine sweep. The two protocol probes are standalone scripts, so you can also check a build directly:

node scripts/probe-stdio.mjs     # speaks MCP over stdio like a client does
node scripts/probe-http.mjs      # exercises the streamable-HTTP transport

Layout:

src/
  cli.ts            command line and the stdio/HTTP entry point
  server.ts         MCP server, tool registration, both transports
  search.ts         tiered orchestration, RRF, wrapper decoding
  research.ts       search -> fetch -> passage extraction
  rrf.ts            Reciprocal Rank Fusion and de-duplication
  cache.ts          SQLite FTS5 page index and circuit-breaker state
  http.ts           proxy, retries, size caps, the SSRF guard
  robots.ts         robots.txt parsing and caching
  config.ts         defaults <- config.json <- .env <- environment
  html/markdown.ts  HTML -> Markdown pipeline
  fetch/page.ts     the fetch-and-convert service
  fetch/documents.ts PDF, DOCX, XLSX, PPTX, EPUB, ODT, CSV, JSON
  engines/          one file per engine, plus kit.ts and registry.ts
  install/          client specs and the installer
  tools/            tool schemas, handlers and Markdown renderers
tests/              vitest suites and engine fixtures

Adding an engine is one file plus one line in src/engines/registry.ts. Every engine declares its own tier, RRF weight and, for subject indexes, the query patterns that make it eligible.

Limitations

  • research() quotes, it does not summarise. This server has no language model, so the brief is extractive by design: it selects and quotes the passages that match the query and labels every one with a citable source. The calling model writes the synthesis. Calling that a summary would be a lie.

  • HTML scraping is inherently fragile. Engines change their markup. The structural fallback extractor adapts without selectors, and a broken engine degrades to zero results and gets benched rather than breaking the search — but a provider can still change its markup faster than a release cycle.

  • The subject indexes are narrow by design. Wikipedia, GitHub, crates.io and arXiv index specific corpora, not the web.

  • No OCR. A scanned PDF is reported as having no extractable text rather than silently returning nothing.

Origin and authorship

Author: lhswg — GitHub lhswgzy — © 2025, MIT. Source of the idea: sweetcornna/free-search-mcp, a Python project (MIT, on PyPI as free-search-mcp).

This codebase was written from scratch. It is not a fork; it shares no commit history, no source file and no engine parser with that project, and nothing from it was read while this was written. The npm package is named free-search-mcp-ts rather than free-search-mcp deliberately, so that the original author keeps their own project name on their own registry. Bugs in the Python project should be reported there, not here. The full statement, including what is and is not claimed, is in AUTHORS.md.

License

MIT — see LICENSE. Copyright © 2025 lhswg.


If this saves you a search API bill, a star is appreciated. Issues with a raw engine response attached are the most useful kind of bug report.

Available Tools

8 tools
fetch_urlFetch a URL as MarkdownA
Read-only

Fetch one web page or document and return its main content as clean Markdown, with the navigation, adverts and cookie banners removed. Handles HTML (via a readability pass), PDF, DOCX, XLSX, PPTX, EPUB, ODT, CSV, JSON and plain text. Results are stored in a local SQLite index, so reading the same URL again is instant and the page becomes searchable offline with search_index. Long pages are truncated rather than refused: the response reports a next offset you can pass back to continue. robots.txt is honoured by default and private/loopback addresses are refused as a safety measure.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesAbsolute http(s) URL to fetch.
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
offsetNoSkip this many characters into the content. Use the `next offset` from a previous call to page through a long document.
refreshNoIgnore the local cache and re-fetch (default false).
max_charsNoMaximum characters of content to return. Longer content is truncated and a `next offset` is reported so you can continue reading.
use_cacheNoUse the cached copy when it is fresh (default true).
include_linksNoAlso return the outgoing links found in the body, for follow-up fetching.
respect_robotsNoHonour robots.txt for this request (default: the server setting, which is true).

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint/openWorldHint annotations, it discloses substantial behavior: results cached in a local SQLite index, long pages truncated rather than refused, robots.txt honoured by default, and private/loopback addresses refused as a safety measure. These are exactly the operational traits an agent needs and are not derivable from the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core behavior (fetch → clean Markdown) and then layers caching, truncation, and safety in tight successive sentences. It is dense but each sentence carries distinct information; only the long format enumeration is slightly list-like.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 8-parameter tool with no output schema, the description covers return shape (Markdown main content, `next offset` for continuation), caching semantics, and safety constraints, which is everything needed to invoke it correctly. Nothing material is left unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all 8 parameters are already documented in the schema, and the description adds only the conceptual paging loop around `offset`/`max_chars` plus the supported file types. It reinforces but does not meaningfully extend the schema's parameter documentation, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Fetch one web page or document') and names the exact output ('clean Markdown' with navigation, adverts and cookie banners removed). It is clearly distinguishable from the plural sibling fetch_urls and from parse_document by emphasizing single-URL retrieval of the main content.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context: reading the same URL again is instant, the page becomes searchable offline with search_index, and the `next offset` is passed back to continue long documents. However, it never explicitly contrasts itself with fetch_urls for batch fetching or with parse_document, leaving the sibling routing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fetch_urlsFetch several URLsA
Read-only

Fetch up to 12 URLs concurrently and return each as Markdown under its own heading. Cheaper and faster than calling fetch_url repeatedly because the requests overlap. Failures are reported per URL instead of failing the whole call.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlsYesAbsolute http(s) URLs to fetch.
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
refreshNoIgnore the local cache and re-fetch (default false).
max_charsNoMaximum characters returned *per page*.
concurrencyNoHow many fetches to run at once (default 5).

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered. The description still adds meaningful behavior beyond that: the 12-URL batch cap, per-URL failure isolation ('Failures are reported per URL instead of failing the whole call'), and the concurrent overlap that makes it fast. Remaining gaps are minor, such as the caching behavior behind the refresh param.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, zero waste. The core capability and return shape are front-loaded, followed by the sibling comparison and then the failure-handling contract, in descending order of importance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-shape burden and does so adequately by describing per-URL Markdown sections and per-URL error reporting. Behavior is complete enough to invoke correctly, though ordering/pagination semantics and cache behavior are left to the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents urls, format, refresh, max_chars, and concurrency with enums and ranges. The description adds little parameter-level detail beyond echoing the 12-URL cap and Markdown default, so the baseline 3 for schema-driven parameters is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb (Fetch), a resource (URLs), and a concrete scope: 'up to 12 URLs concurrently', returning 'each as Markdown under its own heading'. It also differentiates itself from the sibling fetch_url by naming it explicitly, so an agent can route between them without opening the schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states a clear selection condition: 'Cheaper and faster than calling fetch_url repeatedly because the requests overlap', which tells the agent to prefer this tool over the single-URL sibling for batches. There is no explicit when-not guidance (e.g. use web_search when you have no URLs, or fetch_url when you need one page), so it falls short of the top score.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_enginesList search engines and their statusA
Read-only

Show every engine this server knows about: what it indexes, whether it needs an API key, which tier it belongs to, and its current health (a repeatedly blocked engine is benched for a while). Use it to pick explicit engines for web_search, or to diagnose an empty result list.

ParametersJSON Schema
NameRequiredDescriptionDefault
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds genuinely new behavioral context: engines can be temporarily 'benched' after repeated blocks, which explains otherwise confusing absence from results. It does not cover rate limits or output shape, so it lands just short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler, with the content of the listing front-loaded and the actionable routing ('pick explicit engines for web_search') placed second. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the return-value burden and does it by enumerating the four fields returned. Combined with the annotation-backed safety profile and the schema-documented format parameter, an agent has everything needed to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single 'format' parameter is fully documented in the schema, so the description correctly stays out of it. Baseline 3 applies since the description adds no syntax or default-value detail beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Show every engine this server knows about') and enumerates exactly what the listing contains: indexed content, API-key requirement, tier, and current health. It also routes the agent toward web_search as the downstream consumer, so it is distinguishable from the other search/index siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives two concrete use cases: selecting explicit engines for web_search and diagnosing an empty result list. That is clear when-to-use guidance, but it stops short of an explicit exclusion or comparison against the other siblings (research, search_index, local_index).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

local_indexInspect or maintain the local indexA
DestructiveIdempotent

Report on, prune, clear or compact the local cache that stores fetched pages and recent engine responses. stats is safe and cheap; clear and prune delete data permanently.

ParametersJSON Schema
NameRequiredDescriptionDefault
scopeNoFor `clear`: what to delete (default all).
actionNostats (default) reports sizes; clear empties the cache; prune drops entries older than max_age_days or beyond max_pages; vacuum compacts the database file.
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
max_pagesNoFor `prune`: keep only this many of the most recently fetched pages.
max_age_daysNoFor `prune`: drop pages fetched more than this many days ago.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true and readOnlyHint=false, but they are tool-level and cannot say that the danger is confined to specific actions. The description adds per-action detail by naming `stats` as safe and `clear`/`prune` as permanently deleting data, which is genuinely useful disambiguation. It doesn't cover what `vacuum` does or what stats returns, so it stops short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the capability and immediately followed by the safety-relevant distinction. Every clause earns its place and nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an action-dispatch maintenance tool with 5 optional params and no output schema, the description covers purpose and the primary risk (permanent deletion). It could say more about what `stats` reports or how `vacuum` differs, but with 100% schema coverage and destructive annotations, the essentials are present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the schema fully documents scope, action, format, max_pages, and max_age_days. The description only echoes the action names already enumerated in the schema, adding no syntax, defaults, or interaction detail beyond it, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description pairs concrete verbs (report, prune, clear, compact) with a specific resource: the local cache of fetched pages and recent engine responses. This is far more than a restatement of the name, though it never names or differentiates itself from siblings like search_index or list_engines.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The line '`stats` is safe and cheap; `clear` and `prune` delete data permanently' hints at action selection, but there is no explicit 'use this when' guidance and no comparison to the sibling index/engine tools. The agent must infer that destructive maintenance is only warranted in specific situations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

parse_documentParse a local file or document URLA
Read-only

Read a document from a local path or a URL and return its text as Markdown. Supports PDF, DOCX, XLSX, PPTX, EPUB, ODT, CSV, JSON, HTML and plain text. Spreadsheets become Markdown tables, presentations become one section per slide, DOCX keeps headings, lists and tables, and PDFs get their hyphenation and column artefacts repaired. Everything is parsed on this machine — no upload, no conversion service.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoAbsolute http(s) URL of a document. Mutually exclusive with `path`.
pathNoLocal file path (absolute, or relative to the server working directory).
sheetNoFor spreadsheets: only return the worksheet with this name.
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
max_charsNoMaximum characters of content to return. Longer content is truncated and a `next offset` is reported so you can continue reading.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint, openWorldHint), so the bar is lower, and the description adds genuine context beyond them: local-only parsing with no upload or conversion service, plus per-format conversion behavior (spreadsheets to tables, presentations to one section per slide, PDF hyphenation/column repair). It does not discuss cost, size limits, or failure modes on remote fetches, but the added context is substantive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with what the tool does, then supported formats, then conversion specifics, then the privacy guarantee. Each sentence carries distinct information and nothing is repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully explains the return shape (text as Markdown, with format-specific structuring), and the schema handles offsets and mutual exclusivity. What is missing is any guidance on large documents or when a remote URL fetch would fail, but nothing an agent needs to invoke it correctly is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all five parameters — including the url/path mutual exclusivity, sheet selection, format enum, and max_chars truncation with next offset — are already fully documented in the schema. The description adds only the format list and conversion outcomes, not parameter-level meaning, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (read/parse) and resource (document from a local path or URL) and names the exact output (text as Markdown), plus an enumerated list of supported formats. It never names a sibling such as fetch_url or web_search, so the agent must infer the boundary between 'parse a document' and 'fetch a URL' on its own.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies its niche — documents (PDF, DOCX, XLSX, etc.) rather than arbitrary web pages — but gives no explicit when-to-use or when-not-to-use statement and no alternative tool named. The presence of fetch_url/fetch_urls among siblings makes that omission a real gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

researchResearch a question (search + read + organise)A
Read-only

One call that does the whole mechanical research loop: search several engines, derive extra queries from the vocabulary of the results, fetch the most relevant and diverse sources, then extract the passages from each that actually answer the question. Returns a Markdown brief with numbered, citable sources and verbatim quoted passages — it does NOT paraphrase, because this server has no language model; you write the synthesis from the evidence it gathers. Use this instead of chaining web_search and several fetch_url calls whenever you need more than a snippet. depth 1 = the query as written. depth 2 (default) = the query plus expansions derived from what the first page of results is actually about. depth 3 = broader expansion and more sources.

ParametersJSON Schema
NameRequiredDescriptionDefault
depthNoHow hard to dig. 1 is fastest, 3 is most thorough (default 2).
queryYesThe research question, in natural language.
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
regionNoRegion hint such as "us" or "cn".
enginesNoEngines to use, in priority order. Omit for automatic tiered selection (keyless defaults first, then fallbacks, then matching subject indexes). Use ["all"] for every configured engine, or see the list_engines tool for ids.
refreshNoIgnore cached pages and re-fetch every source (default false).
languageNoLanguage hint such as "en" or "zh".
freshnessNoOnly return results newer than this. Honoured by engines that support it (Google News, Bing, Serper, Tavily, Brave, Google CSE) and ignored by the rest.
max_sourcesNoHow many sources to fetch and quote (default 8 at depth 2+).
safe_searchNoSafe-search level. Defaults to the server configuration ("moderate").
exclude_domainsNoExclude these domains from the research.
include_domainsNoRestrict the research to these domains.
passages_per_sourceNoPassages to quote per source (default 4).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/openWorldHint, but the description adds behavior annotations cannot: the server has no language model, so it does NOT paraphrase and the agent must write the synthesis itself. It also describes the actual return shape (verbatim passages, numbered citable sources), which is critical given there is no output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the pipeline and clear value; every clause carries signal. Slightly long, and the depth elaboration partly overlaps the schema, but nothing is wasted filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full burden of explaining return values and does so (Markdown brief, numbered sources, verbatim quotes, markdown vs json formats). It also flags the key limitation (no LLM synthesis server-side), so an agent knows exactly what it must do next.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description earns above baseline by elaborating what depth actually does ('depth 2 = the query plus expansions derived from what the first page of results is actually about'), which the terse schema ('How hard to dig') does not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific compound action (search several engines, expand queries, fetch sources, extract answering passages) and names the exact output artifact (a Markdown brief with numbered citable sources and verbatim quotes). An agent can distinguish this from web_search or fetch_url without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: 'Use this instead of chaining web_search and several fetch_url calls whenever you need more than a snippet' — naming the alternatives and the condition ('more than a snippet') that selects this tool. It also gives depth-tier guidance so the agent knows which intensity to pick.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_indexSearch the local page indexA
Read-only

Full-text search over every page this server has already fetched on this machine, using a local SQLite FTS5 index (with CJK bigram support). Use it to recall something you read earlier in the session, to avoid re-fetching, or to work offline. Returns cached page URLs, titles and highlighted snippets — and it costs no network requests at all.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesFull-text query. Latin terms match by prefix; CJK phrases match by bigram, so "上下文" finds "模型上下文协议".
formatNoOutput format. "markdown" (default) is compact and readable; "json" returns structured data for post-processing.
max_resultsNoMaximum matches to return (default 10).

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered; the description still adds real context beyond them — the FTS5 backend, CJK bigram matching, and the fact that it issues no network requests. It does not cover ranking, result ordering, or pagination behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with what the tool is, then usage, then return values and cost. Every sentence carries information; only the closing 'costs no network requests at all' slightly restates the earlier 'local/on this machine' framing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only search tool with full schema coverage and annotations covering the safety profile, the description supplies the remaining essentials: what corpus is searched, when to use it, and what it returns (cached URLs, titles, highlighted snippets). No output schema is needed given that the return shape is described.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents query, format, and max_results in detail, including the default and bounds. The description only reinforces the CJK bigram behavior, adding marginal meaning over the schema — baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+scope: full-text search over pages already fetched on this machine, backed by a local SQLite FTS5 index. It implicitly separates itself from the web_search/fetch_url family by stressing locality and zero network cost, but it never differentiates from the sibling 'local_index', which is a plausible source of confusion.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives three concrete situations to reach for it: recalling something read earlier in the session, avoiding a re-fetch, and working offline. There is no explicit when-not or named alternative (e.g. 'for new content use web_search'), so routing is clear but not fully resolved.

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.

  1. 8 tool updatesv0.1.0
    • First observedfetch_url
    • First observedfetch_urls
    • First observedlist_engines
    • First observedlocal_index
    • First observedparse_document
    • First observedresearch
    • First observedsearch_index
    • First observedweb_search

TDQS

A4.1/5.0

Scored across 8 tools

Disambiguation4/5

Most tools have clearly distinct roles, and descriptions explicitly route between them (web_search vs research, fetch_url vs fetch_urls). The main overlap is fetch_url and parse_document, which both convert remote documents/HTML to Markdown from a URL, leaving some ambiguity about which to pick for a remote document.

Naming Consistency4/5

The dominant pattern is snake_case verb_noun (fetch_url, fetch_urls, parse_document, search_index, list_engines), which is predictable. Deviations are research (a bare verb/noun) and local_index (a noun phrase rather than an action), but these are minor.

Tool Count5/5

Eight tools is well-scoped for a search/fetch/research server, with each tool earning its place across search, fetch, parse, index recall and engine introspection. No redundancy or padding.

Completeness5/5

The surface covers the full lifecycle: multi-engine search, orchestrated deep research, single and batch fetching, document parsing, offline index recall, cache management and engine introspection. No obvious gaps or dead ends for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Local web search MCP server that fuses multiple search engines, fetches and compresses pages to reduce tokens and cost, with caching for repeated and paraphrased queries.
    58
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    MCP server enabling local-first web search, fetch, extract, and caching with citeable excerpts, no API key required. Supports research workflows for agents and apps.
    301 npm
    1
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Local MCP server for web search and page extraction, providing clean markdown from URLs, search results, site mapping, and research endpoints without API keys or accounts.
    5
    Apache 2.0