cc-switch-websearch

cc-switch-websearch

Provides a web_search MCP tool for cc-switch, backed by a self-hosted SearXNG instance, enabling AI assistants like Claude Code to perform web searches without a premium API key.

Category
访问服务器

README

cc-switch Web Search

Built-in web_search MCP tool for cc-switch (Claude Code / Codex / Gemini CLI provider manager), backed by a self-hosted SearXNG JSON API.

Once installed, cc-switch starts an in-process MCP HTTP server, registers the entry in ~/.claude/mcp_servers.json, and Claude Code can call mcp__cc-switch-websearch__web_search for fresh web results — no premium search API key required.

Attribution. This integration and the bundled installer were produced by Minimax-M3 (a Claude Opus 4.8 based coding agent developed by Minimax). Use, modify, and redistribute under the terms of the upstream cc-switch MIT licence.


What you get

  • A web_search MCP tool exposed via in-process HTTP on a random free port (127.0.0.1:<port>/mcp)
  • Automatic registration of the entry in ~/.claude/mcp_servers.json on enable, automatic removal on disable
  • A new Web Search section under Settings > Advanced in cc-switch with an enable/disable switch, a SearXNG instance URL input
    • Save, live status (port, running indicator, last query at), and a Test search box for one-shot queries
  • Plain-text natural-language reports (title, URL, snippet per result) suitable for Claude to cite directly

Quick start

1. Apply

From the repository root:

python3 install.py apply

Or target a specific checkout:

python3 install.py apply --source ~/Code/cc-switch

The installer reads spec.yaml and applies it to the target cc-switch source tree. New files (under spec/files/) are copied into place; for each modification rule the installer finds a tree-sitter anchor in the user's file (Rust/TSX/TS) or parses the file as JSON/TOML, then makes the edit in place. Backups of every modified file are written under <source>/.spec-backups/<timestamp>/ so revert can restore the tree exactly. Pass --auto-deps to also run pnpm install and cargo check afterwards.

To revert later:

python3 install.py revert

To check the current state:

python3 install.py status

2. Self-host a SearXNG instance

DuckDuckGo's HTML endpoint rejects any Rust HTTP client with a captcha, and every public SearXNG instance we tested sits behind an Anubis / Cloudflare challenge — neither works from a headless tool. You need a self-hosted SearXNG. Pick the one-liner for your platform:

<details> <summary><b>macOS (OrbStack recommended)</b></summary>

# Install OrbStack if you don't have a Docker runtime yet
brew install --cask orbstack
open /Applications/OrbStack.app   # first launch: ~30 s to bring up the Linux VM

# Persist a permissive settings.yml (disables IP rate-limit, enables JSON)
mkdir -p ~/.config/searxng
cat > ~/.config/searxng/settings.yml <<EOF
use_default_settings: true
server:
  secret_key: "$(openssl rand -hex 32)"
botdetection:
  ip_limit:
    enable: false
  link_token: false
search:
  formats:
    - json
EOF

# Start SearXNG
docker run -d --name searxng --restart unless-stopped \
  -p 8888:8080 \
  -v ~/.config/searxng/settings.yml:/etc/searxng/settings.yml:ro \
  searxng/searxng

# Smoke test
curl -s 'http://127.0.0.1:8888/search?q=rust+language&format=json' | python3 -m json.tool | head

</details>

<details> <summary><b>Windows (PowerShell, Docker Desktop)</b></summary>

# Install Docker Desktop if you don't have a Docker runtime yet
winget install Docker.DockerDesktop
# Launch Docker Desktop from the Start menu and wait for the whale icon to settle

# Persist a permissive settings.yml
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.config\searxng" | Out-Null
@"
use_default_settings: true
server:
  secret_key: "$(-join ((1..32) | ForEach-Object { '{0:x2}' -f (Get-Random -Maximum 256) }))"
botdetection:
  ip_limit:
    enable: false
  link_token: false
search:
  formats:
    - json
"@ | Set-Content -Encoding UTF8 "$env:USERPROFILE\.config\searxng\settings.yml"

# Start SearXNG
docker run -d --name searxng --restart unless-stopped `
  -p 8888:8080 `
  -v "$env:USERPROFILE\.config\searxng\settings.yml:/etc/searxng\settings.yml:ro" `
  searxng/searxng

# Smoke test
(Invoke-WebRequest 'http://127.0.0.1:8888/search?q=rust+language&format=json').Content

</details>

<details> <summary><b>Linux (Docker)</b></summary>

# Install Docker if you don't have it yet (Debian / Ubuntu shown)
sudo apt-get update && sudo apt-get install -y docker.io
sudo usermod -aG docker $USER && newgrp docker
sudo systemctl enable --now docker

# Persist a permissive settings.yml
mkdir -p ~/.config/searxng
cat > ~/.config/searxng/settings.yml <<EOF
use_default_settings: true
server:
  secret_key: "$(openssl rand -hex 32)"
botdetection:
  ip_limit:
    enable: false
  link_token: false
search:
  formats:
    - json
EOF

# Start SearXNG
docker run -d --name searxng --restart unless-stopped \
  -p 8888:8080 \
  -v ~/.config/searxng/settings.yml:/etc/searxng/settings.yml:ro \
  searxng/searxng

# Smoke test
curl -s 'http://127.0.0.1:8888/search?q=rust+language&format=json' | python3 -m json.tool | head

For Podman the same command works — just replace docker with podman and drop the sudo / usermod steps.

</details>

3. Wire it into cc-switch

  1. Build cc-switch from your patched source: pnpm install && pnpm tauri dev
  2. Open Settings > Advanced > Web Search
  3. Paste http://127.0.0.1:8888 into the SearXNG URL field and click Save
  4. Click Test search — you should see real results within a few seconds
  5. Flip the Enable Web Search switch on. cc-switch will:
    • Start the in-process MCP HTTP server
    • Write the cc-switch-websearch entry into ~/.claude/mcp_servers.json
  6. Restart Claude Code. The web_search tool is now available — try "Search the web for the latest Rust 1.85 release notes and summarise."

Repository layout

.
├── README.md              # this file
├── LICENSE                # our MIT
├── install.py             # apply / revert / status / dev
├── spec.yaml              # the patch — single source of truth
├── spec/files/            # new file contents at their cc-switch paths
│   ├── src/
│   └── src-tauri/
├── requirements.txt       # tree-sitter + grammar deps for install.py
└── upstream/              # unmodified cc-switch clone (gitignored)
Path Purpose
install.py Spec-driven patch engine. Loads spec.yaml, applies it to a target cc-switch source via tree-sitter (Rust/TSX/TS) or stdlib (JSON/TOML). Apply backs up every modified file under <source>/.spec-backups/<ts>/; revert restores from that backup.
spec.yaml The single source of truth for the patch. 10 new files (creates:) and 15 modifications (modifies:) declared as data. Maintained by AI. To change what the patch does, edit this file — install.py is generic.
spec/files/ The 10 new files the patch adds, at their natural cc-switch relative paths. install.py copies each into the target tree.
requirements.txt Python deps for install.py: PyYAML, tree-sitter, tree-sitter-rust, tree-sitter-typescript, tomli_w. Install with pip install -r requirements.txt.
upstream/ Reference clone of unmodified cc-switch. Gitignored; install.py bootstraps test-target/ from here when no real cc-switch source is detected.

Why "patch as spec" instead of .patch files or post-patch files?

Earlier revisions of this project used two other approaches, both with real downsides:

  • Static .patch files (e.g. git format-patch output). A unified diff is fragile: it depends on the exact line numbers and surrounding context of the upstream file at the time the diff was generated. One whitespace tweak or a single upstream commit can make git apply fail with a three-way merge conflict.
  • Static post-patch files (a patch/ directory of files at their final state). Sidesteps the diff-fragility problem but forces the patch to one specific upstream version — apply the 3.16.3 snapshot to a 3.16.0 checkout and you get code that doesn't compile.

The current design — a declarative spec applied at install time via tree-sitter — trades a touch of complexity for both:

  • Robust to upstream drift. Each modification is anchored on a semantic identifier (find: { node: mod_item, field: name, equals: usage_script }), not a line number. As long as the anchor still exists in the user's file, the patch lands.
  • Fails loudly when it can't. If an anchor no longer exists in a new cc-switch release, install.py exits with a clear error naming the file + the spec rule. No silent skips, no half-applied state.
  • Easy to update. To rebase on a new cc-switch release, edit the spec's anchors. To add a new feature, append rules. install.py is generic — the spec is data, the engine is code.

Files the patch adds / modifies

The spec applies 25 changes — 10 new files and 15 modifications. The full list lives at the top of spec.yaml; in short:

New (10):

  • src/components/websearch/{WebSearchPanel,WebSearchStatusBadge}.tsx
  • src/hooks/useWebSearch.ts
  • src/lib/api/webSearch.ts
  • src/types/websearch.ts
  • src-tauri/src/commands/web_search.rs
  • src-tauri/src/web_search/{mod,mcp_server,tools,searxng}.rs

Modified (15):

  • src/components/settings/SettingsPage.tsx
  • src/i18n/locales/{en,ja,zh,zh-TW}.json
  • src-tauri/Cargo.toml
  • src-tauri/src/{lib,store}.rs
  • src-tauri/src/commands/mod.rs

Why SearXNG (and not DuckDuckGo)?

The original implementation scraped html.duckduckgo.com — the same approach used by NousResearch/hermes-agent. In practice every Rust HTTP client (reqwest, ureq, with or without http2, with or without custom TLS fingerprints) gets back an HTTP 202 captcha page. The only way we found to make a successful request is to shell out to curl, which is not a sustainable build-time dependency for a Tauri app.

Public SearXNG instances (baresearch.org, disroot.org, search.hbubli.cc, …) all sit behind Anubis or Cloudflare challenges — same problem. The integration is therefore designed around a user-configured, self-hosted SearXNG instance. The one-liners in §2 bring one up locally in under a minute on any platform.


Rebasing on a new cc-switch release

The spec is anchored on identifiers (function names, JSX attribute values, JSON keys, TOML tables), not line numbers — so most upstream changes apply cleanly without any edit to spec.yaml. When a new cc-switch release renames or removes one of those anchors, install.py apply will fail with a clear error naming the file and the anchor that no longer matches. To recover:

python3 install.py revert        # restore from the last .spec-backups/
cd ~/Code/cc-switch
git fetch upstream
git rebase upstream/main        # or: git pull upstream main
python3 install.py apply         # re-apply; if a rule now fails, edit
                                # spec.yaml to point at the new anchor
pnpm install && pnpm tauri dev

The most likely edits after a rebase are:

  • A find: { node: mod_item, field: name, equals: ... } rule where the upstream renamed the mod. Update equals.
  • A find: { attribute: { name: value, equals: logConfig } } rule where the upstream renamed the AccordionItem value. Update equals.
  • A find: { table: dependencies } rule where upstream restructured Cargo.toml. Update the table path.

If you want to start clean against a new cc-switch release without trying to rebase an already-patched tree:

python3 install.py revert        # restore from backup
git pull upstream main           # pull new upstream
python3 install.py apply         # re-apply the spec on top

Verifying the SearXNG instance is reachable

The panel's Test search button is the fastest check. From a terminal:

curl -s 'http://127.0.0.1:8888/search?q=hello&format=json' | python3 -m json.tool | head

You should see a JSON object with a results array. If you get HTML or a 403, the instance is up but is missing the botdetection.ip_limit: false / search.formats: [json] overrides — re-check the settings.yml in §2.

Container lifecycle:

docker ps --filter name=searxng      # is it running?
docker logs --tail 50 searxng        # what went wrong?
docker restart searxng               # after editing settings.yml

Licence & attribution

  • Integration code: MIT, same as upstream cc-switch.
  • Installer (install.py): MIT.
  • Generated by Minimax-M3 (Minimax).

Issues and PRs welcome — please open them on this repository rather than on the upstream cc-switch tracker.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选