Skip to main content
Glama
radoxtech

DeepSeek Search Everywhere MCP

by radoxtech

DeepSeek Search Everywhere MCP

CI License: MIT Python 3.11 | 3.12 | 3.13

An MCP server that gives AI agents web search, page fetching and multi-source research through the official DeepSeek Search & Fetch API.

It is a thin, careful layer over that API: results are deduplicated, content is capped to a budget you set, repeated calls are served from an in-memory cache, and outgoing calls are paced by a local rate limiter. Six tools, stdio by default, optional bearer-protected streamable HTTP.

It is a standard MCP server, so it works with any MCP-compatible AI client or agent: Claude Code, Claude Desktop, Google Antigravity, Cursor, Windsurf, VS Code (GitHub Copilot), Gemini CLI, Codex CLI, Zed, opencode and others. Local clients launch it over stdio; remote or shared setups can connect over HTTP.

Not affiliated with DeepSeek. This is an independent, community-maintained project.

Features

  • Search with content in one request: need_content returns page text inline, so there is usually no need for a second fetch per result.

  • Batch fetch and research: deepseek_fetch_many fetches up to 10 URLs concurrently; deepseek_research runs a search and fills the gaps by fetching pages whose search content is missing.

  • Result hygiene: canonical-URL and title/site deduplication, per-result and total character budgets, Markdown unescaping.

  • Optional query rewrite to English through deepseek-flash (thinking disabled), plus an explicit lang operator for non-English queries.

  • Cache and limiter: TTL + LRU cache with single-flight, per-endpoint token buckets, refresh to bypass.

  • Safe by default: static SSRF checks, secret redaction, no telemetry. See Security.

Related MCP server: Web Search MCP Server

Requirements

Install

The package is installed from GitHub; no registry release is required.

# Run without installing (uv)
uvx --from git+https://github.com/radoxtech/deepseek-search-everywhere-mcp deepseek-search-everywhere-mcp

# Or install with pip
pip install git+https://github.com/radoxtech/deepseek-search-everywhere-mcp.git

# Or from a source checkout
git clone https://github.com/radoxtech/deepseek-search-everywhere-mcp.git
cd deepseek-search-everywhere-mcp
pip install -e .

The HTTP transport needs the http extra (it pulls in uvicorn):

pip install "deepseek-search-everywhere-mcp[http] @ git+https://github.com/radoxtech/deepseek-search-everywhere-mcp.git"
# from a checkout: pip install -e ".[http]"

Check that the key and connectivity work (prints your balance, then exits):

export DEEPSEEK_API_KEY=sk-...
deepseek-search-everywhere-mcp --check

Quick start

All examples use stdio. Replace sk-... with your key.

The server is client-agnostic: the examples below cover common clients, and any other MCP client (Antigravity, Cursor, Windsurf, VS Code, Gemini CLI, Codex CLI, Zed, ...) works with the same command, arguments and environment variable in its own MCP configuration format.

Claude Code

claude mcp add deepseek-search-everywhere -e DEEPSEEK_API_KEY=sk-... -- \
  uvx --from git+https://github.com/radoxtech/deepseek-search-everywhere-mcp deepseek-search-everywhere-mcp

Claude Desktop

Edit claude_desktop_config.json (Settings, Developer, Edit Config):

{
  "mcpServers": {
    "deepseek-search-everywhere": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/radoxtech/deepseek-search-everywhere-mcp",
        "deepseek-search-everywhere-mcp"
      ],
      "env": { "DEEPSEEK_API_KEY": "sk-..." }
    }
  }
}

opencode

In opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "deepseek-search-everywhere": {
      "type": "local",
      "command": [
        "uvx",
        "--from",
        "git+https://github.com/radoxtech/deepseek-search-everywhere-mcp",
        "deepseek-search-everywhere-mcp"
      ],
      "environment": { "DEEPSEEK_API_KEY": "{env:DEEPSEEK_API_KEY}" },
      "enabled": true
    }
  }
}

Any stdio client

Most clients (including Antigravity, Cursor and Windsurf) use an mcpServers JSON block. Use the uvx form from the Claude Desktop example, or, if the package is already installed, point the client at the console script:

{
  "mcpServers": {
    "deepseek-search-everywhere": {
      "command": "deepseek-search-everywhere-mcp",
      "env": { "DEEPSEEK_API_KEY": "sk-..." }
    }
  }
}

python -m deepseek_search_everywhere_mcp is equivalent.

Streamable HTTP

For clients that connect over HTTP. A bearer token is always required; the server refuses to start without one.

pip install -e ".[http]"          # or the pip/GitHub form shown above
export DEEPSEEK_API_KEY=sk-...
export MCP_BEARER_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
deepseek-search-everywhere-mcp --transport streamable-http   # http://127.0.0.1:8000/mcp

Options: --host (default 127.0.0.1), --port (default 8000), --path (default /mcp). The token is read from the environment only, never from the command line. The server runs stateless and answers with JSON (no SSE streams). Client entry:

{
  "mcpServers": {
    "deepseek-search-everywhere": {
      "url": "http://127.0.0.1:8000/mcp",
      "headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
    }
  }
}

Claude Code: claude mcp add --transport http deepseek-search-everywhere http://127.0.0.1:8000/mcp --header "Authorization: Bearer $MCP_BEARER_TOKEN". The server speaks plain HTTP; when binding beyond loopback, put TLS in front of it (see SECURITY.md).

Tools

Tool

Purpose

Parameters (defaults)

deepseek_web_search

Search; titles, URLs, dates, authority, rerank scores, optional page content

query, count=10 (1-50), ori_question, need_content=False, lang, max_chars_per_result=4000, max_total_chars=40000, dedupe=True, rewrite=False, refresh=False

deepseek_search_and_read

Search with page content in one request; the recommended retrieval path

query, count=5 (1-50), ori_question, lang, max_chars_per_result=4000, max_total_chars=30000, dedupe=True, rewrite=False, refresh=False

deepseek_fetch

Fetch one known URL as Markdown

url, clean=True, max_chars=20000, refresh=False

deepseek_fetch_many

Fetch 1-10 URLs concurrently (up to 5 at a time)

urls, max_chars_per_url=4000, max_total_chars=20000, clean=True, refresh=False

deepseek_research

One search with content, then fetch the sources whose content is missing or shorter than 200 characters (at most 5 fetches)

question, max_pages=5 (1-50), per_page_chars=6000, total_chars=30000, refresh=False

deepseek_balance

Account balance

none

Notes on the parameters:

  • query and question are limited to 128 characters and support site:, -site:, "phrase", lang:, after: and before:. ori_question (max 128 characters) is the natural-language question and improves ranking.

  • lang is an explicit language operator such as es, de, ja or pt-BR. Without it, results for non-English queries may be mixed-language; for named entities, compare with and without it.

  • With dedupe=True, searches ask the upstream for a few extra results (max(2, ceil(count * 0.5)), capped at 50) and trim to count after removing duplicates, so you still get count results when the index returns repeats. With dedupe=False exactly count are requested.

  • rewrite=True translates the query to English with deepseek-flash before searching. It is skipped when lang is set and falls back silently to the original query if the rewrite fails.

  • deepseek_fetch_many skips empty entries, fetches duplicate URLs (same canonical URL) once, keeps the input order, and reports a failing URL as an item with ok: false instead of failing the batch. All fetches share a 120 s deadline; URLs still pending then are reported as deadline_exceeded. Each page gets at most min(max_chars_per_url, max_total_chars // number of URLs) characters.

  • deepseek_research does no summarising and makes no model call: it returns sources for the caller to synthesise.

Example calls:

deepseek_search_and_read(query="rust async runtime comparison", count=5)
deepseek_web_search(query="gobierno de España presupuestos 2026", lang="es", need_content=False)
deepseek_fetch_many(urls=["https://example.com/a", "https://example.com/b"], max_total_chars=12000)
deepseek_research(question="how does HTTP/3 connection migration work", max_pages=4)

Response envelope

Every tool returns a JSON object with ok.

Field

Where

Meaning

ok

all

true on success, false on failure

cached

search, search-and-read, fetch; and per item in deepseek_fetch_many and per source in deepseek_research

true when the response came from the in-memory cache

cached_count

deepseek_fetch_many, deepseek_research

how many responses in this call were served from the cache

requests_used

deepseek_fetch_many, deepseek_research

upstream requests actually made (cache hits are free)

notes

all success envelopes

human-readable remarks: dedupe counts, rewrite applied or failed, lang applied, truncation ("Some page content was truncated to respect the character budget.")

error

failures

{ "code", "message", "status"? }; status is the upstream HTTP status when there was one

partial

deepseek_fetch_many, deepseek_research

true when some items or sources failed; each failed one carries its own error. The top-level ok stays true, so check partial

failed_count

deepseek_fetch_many, deepseek_research

number of failed items or sources

Search results carry title (the URL host without www. when the upstream title is blank), url, date, authority (0-4, -1 unknown), rerank_score, site, snippet, and content/content_chars or summary where available. deepseek_fetch returns title, content, content_chars, truncated.

Error codes

Code

Meaning

missing_api_key

no key in DEEPSEEK_API_KEY or the fallback file

ssrf_blocked

URL rejected locally: internal host, literal private/loopback address, or embedded credentials

bad_url, invalid_url, empty_url, url_too_long

malformed or oversized URL

empty_query, query_too_long, bad_count, bad_lang, bad_request

invalid arguments

rate_limited_local

no local rate-limit token became available within the maximum wait

response_too_large

upstream body exceeded the cap (8 MiB search, 32 MiB fetch)

deadline_exceeded

batch item not finished within the shared batch deadline

timeout, network_error, request_failed, invalid_json

transport-level failures

internal_error

unexpected exception while fetching one batch item

other / http_error

upstream API errors keep the code from DeepSeek (or http_error) and set error.status

Cache and rate limiting

The cache and the limiter live in the server process and are not shared between processes.

Search

Fetch

Query rewrite

Cache TTL

5 min

30 min

24 h

Entry limit

128

128

1024

Size limit

16 MiB

32 MiB

1 MiB

Rate limit (sustained / burst)

10 / s, 20

5 / s, 10

not limited

  • Only successful, non-empty responses are cached. Errors are never cached.

  • Identical concurrent calls share one upstream request (single-flight); only the one that made the request pays a limiter token, and none of them reports cached: true.

  • refresh=true skips the cache read for that call and replaces the stored entry.

  • dedupe changes the upstream request (the search over-fetches), so dedupe=true and dedupe=false calls do not share a cache entry.

  • Cache hits and rewrite calls cost no limiter token. One logical call takes one token, retries included.

  • If a token would take longer than 5 s to become available, the call fails immediately with rate_limited_local rather than queueing indefinitely.

  • Transient failures (HTTP 429 and gateway 5xx) are retried once.

  • DEEPSEEK_MCP_CACHE=0 disables all caches.

Configuration

Variable

Required

Purpose

DEEPSEEK_API_KEY

yes

DeepSeek platform key

DEEPSEEK_BASE_URL

no

API base URL (default https://api.deepseek.com)

MCP_BEARER_TOKEN

HTTP only

token clients must send as Authorization: Bearer ...

DEEPSEEK_MCP_CACHE

no

1 (default) or 0 to disable caching

DEEPSEEK_MCP_LOG_LEVEL

no

stderr log level for the server, the MCP SDK and uvicorn: DEBUG, INFO, WARNING (default), ERROR, CRITICAL

DEEPSEEK_AUTH_JSON

no

path to a JSON file used as the key fallback (see Security)

.env.example lists the same variables. The server does not read .env files itself; export the variables or set them in your MCP client's env.

Security

Full policy and reporting instructions: SECURITY.md.

  • Static SSRF checks only. URLs are validated before the cache, the limiter or the network are touched: non-http(s) schemes, internal hostnames, literal private/loopback/link-local addresses and URLs with embedded credentials return ssrf_blocked. The server does not resolve DNS, and DeepSeek performs the actual fetch, so this is not network isolation.

  • Key handling. The key is sent only in the Authorization header to the configured API base URL. Keys, bearer tokens and similar secrets are redacted from error messages and notes.

  • HTTP transport requires the bearer token on every request (constant-time comparison, never logged).

  • No telemetry. The only outbound destination is the DeepSeek API.

  • Legacy opencode fallback. If DEEPSEEK_API_KEY is unset, the server reads the key from ~/.local/share/opencode/auth.json ({"deepseek": {"key": "..."}}), or from the file named by DEEPSEEK_AUTH_JSON. This exists only for opencode users; prefer the environment variable.

  • Tool inputs (queries, URLs) are sent to DeepSeek to do the work. Do not pass secrets in them.

Limitations

  • deepseek_fetch returns HTTP 404 for PDFs and Reddit and times out on some sites. Use deepseek_search_and_read with the page title or a site:-scoped query instead.

  • Fetch returns only title and content: no final URL, status code or publication date.

  • Search and fetch only. No site mapping or crawling, no asynchronous jobs, a single provider (DeepSeek).

  • The cache and rate limiter are per process; running several server processes multiplies the effective rate.

  • Local rate limits are conservative defaults and do not replace DeepSeek's own quotas.

  • The HTTP transport is plain HTTP, stateless and JSON-only; there are no OAuth and no SSE streams.

Development

git clone https://github.com/radoxtech/deepseek-search-everywhere-mcp.git
cd deepseek-search-everywhere-mcp
python -m venv .venv && . .venv/bin/activate
pip install -e ".[test,lint,http]" build twine

pytest -q                     # offline suite; no network, no key needed
ruff check .                  # lint
python -m build               # sdist + wheel in dist/
python -m twine check dist/*  # validate package metadata

The offline tests clear DEEPSEEK_* variables and point the key fallback at a nonexistent file, so they cannot reach the network. CI runs lint, tests and build on Python 3.11, 3.12 and 3.13.

Live tests

The live suite calls the real DeepSeek API end to end and uses deepseek-flash as an LLM judge to grade whether results are relevant and answer the question. It is excluded from the default run, skips itself without a key, and a full run costs well under $0.01.

cp .env.example .env          # then put your key in DEEPSEEK_API_KEY=
set -a && . ./.env && set +a  # export the variables; tests do not read .env themselves
pytest -m live

Judge scores and reasons are printed after the run. .env is git-ignored; never commit it.

Releasing

Releases are published by .github/workflows/release.yml when a v* tag is pushed. The workflow builds once, checks that the tag equals the version in pyproject.toml and server.json, runs twine check and a clean-wheel smoke test, publishes to PyPI through Trusted Publishing (OIDC, no tokens), creates a GitHub Release with the artifacts and then updates the MCP Registry. Running the workflow manually publishes to TestPyPI only.

# bump the version in pyproject.toml and server.json (tests/test_version_sync.py checks they agree), commit, then:
git tag v0.2.1 && git push origin v0.2.1
bash scripts/smoke_package.sh   # optional local check: build, install into a clean venv, run the CLI

Publishing to the MCP Registry manually

server.json describes the PyPI package for the MCP Registry. The README carries an mcp-name comment that the registry uses to verify package ownership. The release workflow does this automatically; to publish by hand:

python scripts/validate_server_json.py   # structural checks; also runs `mcp-publisher validate` if installed
mcp-publisher login github               # the io.github.radoxtech namespace requires GitHub authentication
mcp-publisher publish

Bump the version in pyproject.toml and server.json together and publish to PyPI first; tests/test_version_sync.py fails when the versions drift apart.

Changelog

See CHANGELOG.md for release notes.

License

MIT. Not affiliated with DeepSeek.

Available Tools

6 tools
deepseek_balanceA

Check the DeepSeek account balance (granted and topped-up, in CNY and USD).

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior3/5

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

No annotations are provided, so the description carries the burden. It does disclose the currency units and the split between granted and topped-up balance, which is useful. However, it does not state whether authentication/API key is required or whether the call is read-only (implied but not declared).

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?

A single sentence, front-loaded with the verb and resource, with the currency/breakdown detail trailing. No waste.

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?

An output schema exists, so return values need not be explained. For a zero-parameter read-only balance check, the description gives all an agent needs to select and invoke it correctly.

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?

There are zero parameters, so the baseline is 4 by rule. The description adds nothing parameter-related, and nothing is needed.

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 (check) and resource (DeepSeek account balance) and even names the returned breakdown (granted vs topped-up, CNY and USD). Clearly distinguishable from all siblings, which are search/fetch/research tools.

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?

Usage is strongly implied: this is the only account/balance tool among search and fetch siblings, so an agent can infer when to call it. But there is no explicit when-to-use/when-not-to-use statement or named alternative.

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

deepseek_fetchA

Fetch one known URL and return its title and Markdown content.

Caveat measured on 2026-10-20: this endpoint returns HTTP 404 for PDFs and for Reddit, and times out on some sites. When the URL is a document or a blocked site, call deepseek_search_and_read with the page title or a site:-scoped query instead.

The response contains only title and content — no final URL, status code or publication date, so keep the provenance on your side.

Pages are cached in memory for about half an hour and identical concurrent calls share one upstream request; the envelope's cached flag tells which happened. Calls are rate-limited locally per endpoint: if no slot frees up within a short wait the error code is rate_limited_local. A response larger than 32 MiB fails with response_too_large.

URLs that point at internal hosts or literal private addresses, or that embed credentials (user:pass@host), are rejected locally with ssrf_blocked before any request is made.

Args: url: Absolute http(s) URL, max 8192 characters. clean: Remove DeepSeek's spurious Markdown escaping (# at line start, , , etc.). max_chars: Content cap, applied after cleaning. refresh: Bypass the response cache and fetch the page again (the envelope's cached flag says whether the page came from the cache).

NameRequiredDescriptionDefault
urlYes
cleanNo
refreshNo
max_charsNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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: known 404 and timeout cases, ~30 minute in-memory cache with concurrent-call dedupe, a local per-endpoint rate limit with the rate_limited_local error code, a 32 MiB response_too_large cap, and ssrf_blocked rejection of internal hosts, private addresses and credentialed URLs before any request. It also warns that the return payload omits final URL, status and date, telling the agent to preserve provenance itself.

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?

Front-loaded with purpose and return shape, then caveat, then cache/limit behavior, then Args. Dense but zero filler: every sentence either routes the agent or prevents a specific failure mode.

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?

Covers the whole call lifecycle for a four-parameter fetch tool: eligibility (SSRF, URL length), known failure modes with error codes, caching semantics, and the limits of the returned payload. Even though an output schema exists, the note about missing provenance fields is advisory context the schema would not supply.

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 0%, so the description must compensate and it does: url is bounded ('absolute http(s) URL, max 8192 characters'), clean explains the specific DeepSeek escaping artifacts it strips, max_chars is pinned to 'applied after cleaning', and refresh is tied to the cache flag in the envelope. Only minor gap is that defaults for clean/refresh/max_chars are left to the schema.

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?

Opens with a specific verb and scope ('Fetch one known URL') plus what comes back ('title and Markdown content'), which cleanly separates it from deepseek_web_search, deepseek_search_and_read and deepseek_fetch_many. The single-URL scope is stated explicitly, so an agent can pick this tool 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?

Gives an explicit when-not rule with the failure conditions spelled out (PDFs and Reddit return 404, some sites time out) and names the exact alternative plus the query shape to use there ('deepseek_search_and_read with the page title or a site:-scoped query'). Routing is fully determined, not inferred.

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

deepseek_fetch_manyA

Fetch up to 10 known URLs concurrently and return each page's title and Markdown content.

Each URL goes through the same validation, cache, rate limiter and SSRF checks as deepseek_fetch. Empty entries are skipped and duplicates (same canonical URL) are fetched once. Results keep the input order; a failing URL yields an item with ok=false and an error instead of failing the batch. All fetches share one deadline (120 s by default); URLs still pending then are cancelled and reported with error code deadline_exceeded.

Each page gets min(max_chars_per_url, max_total_chars // count) characters and the total content never exceeds max_total_chars; a note says when any content was truncated. The envelope reports count (unique URLs), requests_used (upstream fetches attempted), cached_count (items served from the cache), failed_count (items with ok=false), partial (true when any item failed), results and notes. The top-level ok only says the batch ran; check partial or each item's ok for per-URL failures. Each result carries its own cached flag.

Args: urls: 1-10 absolute http(s) URLs, max 8192 characters each. max_chars_per_url: Content cap per page, applied after cleaning. max_total_chars: Total content budget across all pages. clean: Remove DeepSeek's spurious Markdown escaping (# at line start, , etc.). refresh: Bypass the response cache and fetch every page again.

NameRequiredDescriptionDefault
urlsYes
cleanNo
refreshNo
max_total_charsNo
max_chars_per_urlNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

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: shared 120s deadline, cancellation with deadline_exceeded, per-URL failure isolation, duplicate/empty handling, cache interaction, and the top-level ok vs partial distinction. That last point in particular prevents a real misread of batch success.

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 capability, then behavior, then Args – a sound structure with little waste. The paragraph enumerating every envelope field is somewhat redundant given an output schema exists, which is the only thing keeping this below a 5.

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 5-parameter concurrent batch tool with no annotations, this covers the edge cases an agent must reason about: partial failure, deadline truncation, truncation notes, and per-item cache flags. Nothing needed to call it correctly is missing.

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

Parameters5/5

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

Schema coverage is 0%, yet the Args block gives meaning to all five parameters: the 1-10 / 8192-char URL bounds, the purpose of max_chars_per_url and max_total_chars, and what clean and refresh actually change. The allocation formula min(max_chars_per_url, max_total_chars // count) is semantics the schema cannot express at all.

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, resource, and cardinality bound ('Fetch up to 10 known URLs concurrently') plus the return shape (page title and Markdown content). The 'known URLs' phrasing and the explicit reference to deepseek_fetch's validation pipeline make the boundary with the single-URL sibling clear.

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?

'Known URLs' implicitly steers the agent away from this tool for discovery (deepseek_web_search/deepseek_research), and the deepseek_fetch reference establishes the shared pipeline. However, it never states explicitly when to pick this over calling deepseek_fetch repeatedly, leaving that inference to the reader.

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

deepseek_researchA

Gather source material for a question: one search with page content, then fetch the gaps.

Runs a single search with need_content=True (no query rewrite, no model call, no summary). Results whose content is missing or shorter than 200 characters and whose URL passes validation are fetched directly with deepseek_fetch_many (at most 5 per call); a fetched page replaces the search content of that source. A URL that fails validation (for example an internal address) is not fetched. Failed or rejected sources keep the search content and set the source's error; the call still succeeds with partial=true.

Each source has id, title, url, authority, rerank_score, content, chars, origin ('search' or 'fetch'), error (null or {code, message}) and cached (whether its content came from the cache). Content is capped at per_page_chars per source and total_chars overall, in source order; a note says when any content was truncated. requests_used counts upstream requests made (the search plus each fetch; cache hits are free) and cached_count counts responses served from the cache. failed_count counts sources whose error is set and partial is true when it is non-zero. Synthesis is left to the caller.

Args: question: Search query, max 128 characters. Supports site:, -site:, "phrase", lang:. max_pages: Number of search results to use, 1-50 (default 5). per_page_chars: Content cap per source. total_chars: Total content budget across all sources. refresh: Bypass the response caches for the search and the fetches.

NameRequiredDescriptionDefault
refreshNo
questionYes
max_pagesNo
total_charsNo
per_page_charsNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

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

With no annotations, the description carries the full behavioral burden and does so: it explains the fallback fetch path, the 200-character/URL-validation triggers, the 5-fetch cap, that failures keep search content and set error while the call still returns partial=true, cache-hit accounting in requests_used/cached_count, and truncation notes. Nothing important about failure modes or side effects is left implicit.

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

Conciseness3/5

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

It is front-loaded with the core action and organized into behavioral paragraphs followed by args, which is good. However, the sentence enumerating every source field (id, title, url, authority, rerank_score, content, chars, origin, error, cached) largely restates the output schema and adds bulk without new decision-relevant information.

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 5-parameter tool with a multi-stage search-and-fetch pipeline and partial-failure semantics, the description covers trigger conditions, budget capping, caching, error propagation, and counting fields. Combined with an existing output schema, an agent has everything needed to call it correctly and interpret the result.

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 description coverage is 0%, so the description must compensate, and it largely does: question gets a 128-character limit plus supported operators (site:, -site:, "phrase", lang:), max_pages gets the 1-50 range and default 5, and refresh is defined as bypassing caches for both search and fetches. per_page_chars and total_chars are only described as caps without ranges, which is the remaining gap.

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 opening sentence states a specific verb and resource plus the internal workflow: 'Gather source material for a question: one search with page content, then fetch the gaps.' It also disambiguates from siblings by declaring 'no query rewrite, no model call, no summary', which separates it from the search-and-read style tools listed alongside it.

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?

'Synthesis is left to the caller' establishes the boundary of responsibility, and the search-then-fetch-gaps framing tells an agent when this pipeline is the right shape. It stops short of explicitly naming deepseek_web_search or deepseek_search_and_read as alternatives with selection criteria, so no exclusion rules are given.

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

deepseek_search_and_readA

Search and return full page content in one request — the recommended retrieval path.

This is search with need_content=True. Measured on 14 benchmark questions it returned content for PDFs and Reddit threads where deepseek_fetch returned HTTP 404, in about 0.7s per call.

Responses are cached in memory for a few minutes and identical concurrent calls share one upstream request; the envelope's cached flag tells which happened. Calls are rate-limited locally per endpoint: if no slot frees up within a short wait the error code is rate_limited_local. A response larger than 8 MiB fails with response_too_large.

Args: query: Search query, max 128 characters. count: Number of results to return, 1-50 (default 5). With dedupe on, extras are requested upstream so duplicates do not leave you short. ori_question: Original natural-language question, max 128 characters. lang: Language operator, e.g. 'es', 'de' or 'ja'. For non-English queries set it to the appropriate code; without it results may be mixed-language. For named entities test with and without the operator. max_chars_per_result: Content cap per result. max_total_chars: Total content budget across all results. dedupe: Collapse near-duplicate results (same canonical URL or same title+site), default on. rewrite: Rewrite the query via deepseek-flash before searching, default off. Skipped when lang is set; falls back silently to the original query on failure. refresh: Bypass the response cache and query upstream again (the envelope's cached flag says whether the results came from the cache).

NameRequiredDescriptionDefault
langNo
countNo
queryYes
dedupeNo
refreshNo
rewriteNo
ori_questionNo
max_total_charsNo
max_chars_per_resultNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

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 well: in-memory caching window, concurrent-call coalescing, the `cached` envelope flag, per-endpoint local rate limiting with the specific `rate_limited_local` error, and an 8 MiB `response_too_large` ceiling. These are exactly the runtime traits an agent needs to retry or budget correctly.

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 recommendation and the one-request framing are front-loaded, followed by operational behavior, then an Args block — a sensible order. The benchmark anecdote (14 questions, ~0.7s/call) is slightly padded but does real work justifying the recommendation.

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?

An output schema exists, so return values need not be explained, and the description still flags the `cached` envelope field. For a 9-parameter retrieval tool with no annotations, the mix of routing guidance, failure modes, and per-argument behavior leaves nothing an agent needs missing.

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

Parameters5/5

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

Schema coverage is 0%, so the description must compensate — and it documents all nine parameters with behavior the schema cannot express: count interacts with dedupe (extras requested upstream), rewrite is skipped when lang is set and falls back silently, refresh bypasses cache, lang is a language operator with a test-with-and-without heuristic for named entities, plus the two char budgets.

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?

Opening line states a specific verb and resource plus the key differentiator: search *and* full page content in a single request. It also equates itself to a known primitive ('search with need_content=True') and names the sibling it outperforms (deepseek_fetch), so an agent can place it immediately.

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?

Calls itself 'the recommended retrieval path' and gives concrete evidence for preferring it over deepseek_fetch (PDFs/Reddit where fetch returned 404). It does not, however, state when to prefer deepseek_web_search (metadata-only) or deepseek_fetch_many, which are the closest alternatives in the sibling list.

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. 6 tool updatesv0.2.0
    • First observeddeepseek_balance
    • First observeddeepseek_fetch
    • First observeddeepseek_fetch_many
    • First observeddeepseek_research
    • First observeddeepseek_search_and_read
    • First observeddeepseek_web_search

TDQS

A4.3/5.0

Scored across 6 tools

Disambiguation3/5

Three tools (deepseek_web_search with need_content=True, deepseek_search_and_read, and deepseek_research) all perform search-plus-content retrieval and differ mainly by preset flags and internal fallback behavior rather than by distinct purpose. The descriptions do attempt to steer selection ('recommended retrieval path', 'search with need_content=True'), which mitigates but does not eliminate the overlap. Fetch vs fetch_many is clearly distinct.

Naming Consistency4/5

All names use a uniform deepseek_ prefix with snake_case and are mostly verb- or verb_noun-shaped (web_search, search_and_read, fetch, fetch_many, research). deepseek_balance is noun-only, a minor deviation from the otherwise predictable pattern.

Tool Count5/5

Six tools is well-scoped for a search-and-retrieval service, covering discovery, single fetch, batch fetch, a composite research call, and account balance. No bloat; the only questionable inclusion is search_and_read, which duplicates an existing flag.

Completeness4/5

The retrieval lifecycle is well covered: search, rank, read content inline, fan out over known URLs, and a gap-filling composite. Missing pieces are minor conveniences (e.g. a way to inspect rate-limit status or paginate beyond the 50-result cap), and documented caveats on deepseek_fetch give agents a workaround.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to perform web searches, extract webpage content, and conduct end-to-end search-and-extract operations using multiple search providers and content extraction methods.
    -
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to search and fetch high-quality information from multiple sources, including general web APIs, Wikipedia, arXiv, Hacker News, Stack Exchange, and Crossref, with optional pro-mode deep research and clean markdown page extraction.
    10
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to perform web searches, fetch and extract page content, and crawl sites with caching, rate limiting, and robots.txt compliance, all without needing API keys.
    11
    195 PyPI
    1
    MIT