Skip to main content
Glama

cn-websearch-mcp

Language: English | 简体中文

One MCP tool, several built-in web-search channels. A Model Context Protocol server that fronts a set of upstream web-search APIs behind a single web_search tool. Channels ship in different wire formats — some are chat-completions-style APIs with a server-side tool-call or fiber loop, others are standalone search REST endpoints — and this server normalizes all of them into one schema and gives you two strategies:

  • fallback (default) — try channels in your priority order, return the first success. Few calls, low latency.

  • aggregate — query several channels in parallel, merge results, dedupe by URL, and tag each item with its source. Wider coverage.

fallback:   kimi ──✓ 1.2s → return          aggregate:  kimi  ─┐
            stepfun (only if kimi failed)               stepfun ─┼─→ merge + dedupe → return
            zhipu   (only if the above failed)          zhipu   ─┘

The channel identifiers shown above (kimi, stepfun, zhipu, mimo) are the literal config keys — see Configuration for the full set.

Install

Requires Node >= 18. From a checkout:

npm install
npm run build     # tsc → dist/

Provide at least one channel API key — via environment variables, a .env file, or a JSON config file (see Configuration). Channels without a key are skipped automatically.

Related MCP server: mcp-toolkit

Use it as an MCP server

Point your MCP client at the built entry point. Keys go in the client's env block — the server reads .env relative to its working directory, which is not necessarily your project.

{
  "mcpServers": {
    "cn-websearch": {
      "command": "node",
      "args": ["/absolute/path/to/cn-websearch-mcp/dist/index.js"],
      "env": {
        "STEPFUN_API_KEY": "sk-...",
        "WEBSEARCH_STRATEGY": "aggregate"
      }
    }
  }
}

Claude Code:

claude mcp add cn-websearch -e STEPFUN_API_KEY=sk-... -- node /absolute/path/to/cn-websearch-mcp/dist/index.js

Running the binary with no arguments starts the stdio MCP server, so existing MCP client configurations keep working.

Use it in a terminal

The same binary is a CLI, so you can search and test without wiring up a client.

cn-websearch-mcp                       # start the MCP stdio server (default)
cn-websearch-mcp search "query text"   # one-shot search
cn-websearch-mcp search --strategy aggregate --count 12 "query text"
cn-websearch-mcp status                # effective settings + channel status
cn-websearch-mcp test                  # probe every ready channel once
cn-websearch-mcp repl                  # interactive session
cn-websearch-mcp help                  # full usage

Options: -n/--count <1-50>, --strategy fallback|aggregate, --providers a,b (restrict this call), --no-dedupe, -q/--query (query for test), --json (raw output for scripting), -h, -v.

Exit codes: 0 success, 1 runtime failure (search failed / nothing configured), 2 usage error — so scripts and CI can branch on them.

In the interactive session, bare text is a search and / commands control the session:

cn-websearch> a recent news query
cn-websearch> /strategy aggregate      # switch this session to multi-source
cn-websearch> /aggregate rust async    # one-off multi-source search
cn-websearch> /count 12
cn-websearch> /providers stepfun,zhipu # restrict this session
cn-websearch> /test stepfun            # probe one channel
cn-websearch> /status  /config  /json on  /help  /quit

Configuration

Configuration is merged in this order — later layers win:

built-in defaults → JSON config file → environment variables

Environment variables win because MCP clients can generally only pass env.

Config file

Put cn-websearch.config.json in the working directory; it is picked up automatically. Or point at it explicitly with WEBSEARCH_CONFIG=/path/to/file.json.

See cn-websearch.config.example.json for every supported key. A minimal example:

{
  "strategy": "aggregate",
  "providers": {
    "stepfun": { "apiKey": "sk-...", "priority": 10 },
    "zhipu":   { "priority": 5, "options": { "searchEngine": "search_pro" } },
    "kimi":    { "enabled": false }
  }
}

If you put API keys in this file, do not commit it — cn-websearch.config.json is git-ignored by default for that reason (use git add -f if you keep a keyless, shareable config there).

Choosing channel priority

Three equivalent ways, evaluated in this order:

  1. order (config file) or WEBSEARCH_ORDER (env) — an explicit list, highest priority first: ["stepfun", "zhipu"].

  2. priority per channel — a number, higher goes earlier. Ties are broken alphabetically so the result is deterministic.

  3. Neither set → alphabetical default (kimi, mimo, stepfun, zhipu).

Set in the config file:

{ "providers": { "stepfun": { "priority": 10 }, "zhipu": { "priority": 5 } } }

or by environment:

WEBSEARCH_ORDER=stepfun,zhipu,kimi
STEPFUN_PRIORITY=10

On the command line, --providers narrows a single call without changing the configured priority.

Settings

Config file

Environment

Default

Meaning

strategy

WEBSEARCH_STRATEGY

fallback

fallback = first success wins; aggregate = multi-source merge

order

WEBSEARCH_ORDER

alphabetical

Explicit priority list

count

WEBSEARCH_COUNT

8

Default result count when a tool call omits count

timeoutMs

WEBSEARCH_TIMEOUT_MS

30000

Budget per attempt; a retry gets a fresh budget, so one channel's worst case is ~2× plus the retry backoff. Capped at 600000 ms

maxProviders

WEBSEARCH_MAX_PROVIDERS

4

Cap on channels per call (chain length / fan-out)

dedupe

WEBSEARCH_DEDUPE

true

Merge duplicate URLs when aggregating

—

WEBSEARCH_CONFIG

—

Explicit config file path

Booleans accept true/false, 1/0, yes/no, on/off. Invalid values are ignored with a warning rather than failing.

A blank value at any layer (KIMI_API_KEY=, "baseUrl": "") counts as "not set", so the empty placeholders in a template file never mask a value configured in the layer below — the config file, or the built-in default. A timeoutMs beyond 600000 ms is rejected with a warning and falls back, because such a delay no longer fits a 32-bit timer and would silently become 1 ms.

Per-channel settings

Every channel supports the same generic knobs, in the config file or as <NAME>_<SUFFIX> environment variables:

apiKey (_API_KEY), baseUrl (_BASE_URL), model (_MODEL), enabled (_ENABLED), priority (_PRIORITY), timeoutMs (_TIMEOUT_MS, overrides the global budget for that channel), and options for channel-specific parameters.

The four built-in channel slots and the options keys each one recognises:

Slot

Channel type

Recognised options

kimi

chat-completions with a multi-round tool-call loop and a separate fiber endpoint

maxRounds (1-5, default 2), maxTokens (256-32768, default 8192)

mimo

chat-completions with a server-side web_search tool

location (object {country, region, city}; see below), maxKeyword (1-10, default 3), forceSearch (default true)

stepfun

standalone search REST endpoint (POST {base}/v1/search)

category (omitted unless set)

zhipu

standalone web-search API (POST {base}/api/paas/v4/web_search)

searchEngine (default search_std), contentSize (default high); searchEngine is also readable from ZHIPU_SEARCH_ENGINE

The kimi slot's multi-round loop caps at maxRounds tool-call rounds; only when the last round still returns tool calls does it force one final chat call (without tools) for the answer. maxTokens is the token budget per chat call. The mimo slot sends a server-side web_search tool with maxKeyword and forceSearch knobs and an approximate user_location assembled from the configured location keys (country is always sent and defaults to China; region and city only when explicitly configured). The stepfun and zhipu slots are direct REST calls — their options map one-to-one onto the request fields those APIs accept.

Keys are never logged or echoed: error text is scrubbed of credential-looking strings, and status output only reports whether a key is set. A slot that has a key and a baseUrl which is not an https URL is reported with a warning at startup, because that key would otherwise travel in cleartext.

Tools

Input: { "query": string, "count"?: integer, "strategy"?: "fallback"|"aggregate", "providers"?: string[] }.

count defaults to your configured count, strategy to your configured strategy, and providers (when given) must name slots that are enabled and have a key — otherwise the call returns a structured error naming the problem rather than silently ignoring it. count is clamped to 1-50 and query is capped at 400 characters, on this surface and on every CLI command alike.

Output: normalized results plus an audit trail. In aggregate mode each item carries source, and _meta.providers lists everyone who answered:

{
  "results": [
    {
      "title": "…",
      "url": "https://…",
      "snippet": "…",
      "content": "optional full text when the channel returns it",
      "published_date": "2026-09-06",
      "source": "stepfun"
    }
  ],
  "_meta": {
    "provider": "stepfun",
    "providers": ["stepfun", "zhipu"],
    "total_latency_ms": 2586,
    "attempts": [
      { "provider": "stepfun", "status": "ok", "latency_ms": 2025 },
      { "provider": "zhipu",   "status": "transient_error", "latency_ms": 611, "error": "HttpError: HTTP 429: …" }
    ]
  }
}

provider_status

Read-only: effective strategy and settings, and per slot whether it is enabled, has a key, and is in the active chain.

Fallback & failure semantics

  • Only channels that are enabled and have a key participate. fallback walks them in priority order; aggregate queries them in parallel.

  • Per attempt: one wall-clock budget (timeoutMs); hung requests are aborted and recorded as timeout.

  • Transient failures (network errors, HTTP 5xx, 429) are retried once after a short backoff (250 ms, 1 s for a 429), then the next channel is tried. A budget timeout is not retried — its wall-clock budget is already spent, so the chain moves on to the next channel instead.

  • Permanent failures (HTTP 4xx other than 429) skip the retry and move on immediately.

  • If the MCP client cancels the request, the search stops immediately: the in-flight attempt is aborted, its audit record is cancelled, and the call ends there instead of walking the rest of the chain.

  • In aggregate, partial failure is not failure: successful channels' results are returned and the failures stay in _meta.attempts.

  • Every attempt is recorded in _meta.attempts — success, retry, timeout, cancellation or error.

  • If everyone fails, web_search returns a structured error containing the full attempt list.

  • Worst case for a full fallback walk is roughly 2 × timeoutMs + backoff per channel, so a four-channel chain with the default budget can take up to ~4 minutes. Set WEBSEARCH_TIMEOUT_MS or WEBSEARCH_MAX_PROVIDERS to suit your client's deadline.

Channel matrix

Slot

Wire channel

Structured fields

Body excerpt

kimi

chat-completions + multi-round tool-call loop + POST {base}/v1/formulas/moonshot/web-search:latest/fibers

reference URLs from fiber

LLM answer in _meta.answer

mimo

chat-completions with a server-side web_search tool

url_citation + web_search_highlight annotations

LLM answer in _meta.answer

stepfun

POST {base}/v1/search

title, time, snippet, content

full text in content

zhipu

POST {base}/api/paas/v4/web_search

title, link, content, publish_date

summary in snippet, full text in content

Two slots (kimi, mimo) return an LLM-synthesized answer plus citations rather than a plain result list. This server surfaces the citations as result items and puts the synthesized answer in _meta.answer (labelled per channel when aggregating several).

Development

npm install
npm run build          # tsc → dist/
npm run typecheck      # tsc over src/, test/ and scripts/ (no emit)
npm test               # vitest, all HTTP mocked (no keys needed)
npm run test:coverage  # coverage gate: 95% minimum on src/ (lines/functions/branches/statements)
npm run smoke          # real requests against every ready channel, prints a latency table
npm run cli -- repl    # run the CLI from source via tsx

Conventions

  • Comments and file headers are written in English.

  • Runtime-visible strings stay in English — tool descriptions, CLI output, log lines and error messages — so clients and scripts get stable, greppable output.

  • SERVER_NAME / SERVER_VERSION in src/server-info.ts are the single source of truth for the server identity; test/server-info.test.ts asserts they match package.json on every test run.

  • Layering is one-directional: types / errors / config-file / normalize at the bottom, then config / http / provider-selection, then orchestrator / probe, then providers, then runtime / tools / cli.

Repository layout

Path

Contents

src/

All runtime code (TypeScript, ESM)

src/cli/

Terminal interface: argument parsing, one-shot commands, interactive session

src/providers/

Per-channel adapters and the adapter registry

test/

Vitest suite: unit tests and subprocess end-to-end tests

scripts/

Live-network utilities (smoke probe, MCP stdio probe)

Working rules for coding agents live in AGENTS.md.

License

MIT

Available Tools

2 tools
provider_statusA

Read-only status: effective strategy and settings, plus which providers are enabled, have API keys and are part of the active search chain.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

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

No annotations are provided, so the description carries the behavioral disclosure burden. It explicitly declares the operation is read-only and enumerates the information included, which is the key behavioral trait for a parameterless status tool. It does not discuss failure behavior or auth, but that is less critical for a read-only status endpoint.

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?

The entire description is one sentence, front-loaded with 'Read-only status' and followed by a compact but precise enumeration of what the status includes. Every clause earns its place; there is no filler.

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 a zero-parameter tool, the description sufficiently covers what the agent needs: the operation is read-only, and the list of reported items is explicit. Since there is no output schema, a slightly more detailed return-format hint would improve completeness, but it is not required to call the tool 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?

The tool has zero parameters and 100% schema description coverage, so the schema already fully defines inputs; the baseline is 4. The description adds value by clarifying what the returned status covers, though no parameter-level explanation 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?

The description names the resource (provider status) and specifies exactly what it reports: effective strategy/settings, enabled providers, API-key presence, and active search chain membership. The 'read-only status' framing clearly distinguishes it from the sibling web_search tool, which performs searches.

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 implied rather than stated: the tool is obviously for checking provider/strategy status, and web_search is for searching. However, the description never explicitly says 'use this when you need to inspect configuration' or contrasts it with web_search.

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. 2 tool updatesv0.2.0
    • First observedprovider_status
    • First observedweb_search

TDQS

A4.4/5.0

Scored across 2 tools

Disambiguation5/5

The two tools are clearly distinct: web_search performs searches, while provider_status reads configuration and provider health. There is no overlap or ambiguity between them.

Naming Consistency4/5

Both names are concise, readable, and use snake_case consistently. web_search follows a verb_noun pattern while provider_status is noun-based, but they remain predictable and clearly tied to the server's purpose.

Tool Count3/5

With only two tools, the server sits at the low end of acceptable scope. It feels slightly thin, but the narrow search-focused purpose makes the minimal count defensible.

Completeness5/5

The domain is a stateless web search service, and the two tools fully cover its needs: performing searches and checking provider status/configuration. There are no obvious dead ends or missing lifecycle operations.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Wraps the Kimi Coding Search and Fetch APIs into MCP tools for web searching and content retrieval. It enables LLMs to perform targeted searches and crawl web pages using standardized interfaces.
    1
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    A plug-and-play MCP toolkit providing AI agents with web search (Chinese sources prioritized), file operations, and shell command execution capabilities.
    2
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server for multi-engine web search and web page fetching, supporting parallel search, content extraction, and optional LLM-powered search summarization and deep search.
    5
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to run curated English and Chinese web searches through a single search_web MCP tool, returning each result with explicit fetched_at timestamps for grounded, provenance-aware answers.
    Apache 2.0