Skip to main content
Glama
yangsheng6810

Department Web-Search MCP Gateway

部门 Web 搜索 MCP 网关

一个自托管的 Web 搜索服务,整个部门可以共享。它复用一个单一的已登录浏览器会话(一个共享服务账户),因此内网 / SSO / 同意墙登录只需处理一次——每个客户端只需调用 web_search 工具,无需按用户登录或 API 密钥。

任何 MCP 客户端都连接到一个 URL:

  • Chatbox (≥1.14)

  • OpenCode — 本地、共享服务器上,或通过 vscode-remote

  • Claude Code(以及其他支持 MCP 的编码代理)

这是研究笔记中的 T1 “集中式搜索网关”:一台内部机器 + 一个共享 Chrome 配置文件 + 一个 HTTP MCP 端点。


工作原理

Chatbox / OpenCode(local|server|vscode-remote) / Claude Code
        │  remote MCP (Streamable HTTP, /mcp) — same URL for everyone
        ▼
┌──────────────────────────────────────────────┐
│  Gateway (this service, Node + Express)       │
│   • Bearer token (optional) + Host validation │
│   • MCP tools: web_search / read_webpage      │
└──────────────────────────────────────────────┘
        │  connectOverCDP / launchPersistentContext
        ▼
┌──────────────────────────────────────────────┐
│  Chrome (persistent profile, shared account)  │  ← logged in ONCE via `npm run login`
│   • per-request new tab (isolation)           │
│   • concurrency cap + timeouts                │
└──────────────────────────────────────────────┘
        │  optional fallback
        ▼
   SearXNG (if SEARXNG_URL set) — public-search fallback when browser returns nothing

createMcpHandler 在同一 /mcp 端点上同时服务于 2025 时代和 2026 时代的 MCP 客户端,因此客户端传输兼容性不是问题。


Related MCP server: local-web-search-service

无头 Linux 服务器 + 用于登录的 Windows PC

服务器没有图形界面,但人类可以在 Windows PC 上登录。在 .env 中选择一种模式(BROWSER_MODE)——代码相同,仅配置不同。

⚠️ 不要将 Windows Chrome 配置文件目录复制到 Linux。Chromium 使用操作系统绑定的密钥加密 cookie(Windows 上为 DPAPI,Linux 上为 keyring/”peanuts”),因此复制的配置文件会静默丢失登录状态。请使用以下跨操作系统安全模式之一。

模式 C — BROWSER_MODE=cdp(推荐):Linux 网关连接到 Windows 浏览器

  • Windows PC(保持开机):使用共享账户登录一次,然后让 Chrome 以仅本地调试端口运行:

    chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 ^
           --user-data-dir=C:\dept-search-profile
  • 通过 SSH 反向隧道将该端口安全地传输到 Linux 服务器(在 Windows PC 上运行;Win10/11 自带 OpenSSH):

    ssh -R 9222:127.0.0.1:9222 linuxuser@gateway.server
  • Linux 服务器:.env → BROWSER_MODE=cdp,CDP_ENDPOINT=http://127.0.0.1:9222(服务器本地,通过隧道回到 Windows 浏览器)。然后 npm start。

  • 登录状态保持活跃(浏览器使用时 cookie 会刷新);无需复制配置文件;未认证的 CDP 端口永远不会暴露在网络中。缺点:Windows PC 关机 → 搜索失败,直到它重新上线(如果无法接受,请使用模式 B)。

模式 B — BROWSER_MODE=storagestate:快照,Linux 自给自足

  • Windows PC:npm run login(有界面),登录,按 Enter → 写入 auth.json(操作系统无关的 cookie + localStorage JSON)。

  • 将 auth.json 复制到 Linux 服务器,设置 BROWSER_MODE=storagestate,STORAGE_STATE_FILE=./auth.json,运行 npm start。Linux 运行自己的无头浏览器加载快照——无需隧道,即使 Windows PC 关机也能工作。

  • 权衡:一个冻结的快照——当 SSO cookie 过期时重新导出;仅携带 cookie + localStorage(不包括 IndexedDB/客户端证书)——对大多数 SSO 来说没问题。

模式 A — BROWSER_MODE=persistent:Windows PC 运行一切

  • 如果有一台备用的 Windows PC 可以作为始终在线的服务主机:在那里运行 npm run login(播种配置文件),然后使用 BROWSER_MODE=persistent 运行 npm start。

  • Linux 服务器是纯客户端,指向 http://<windows-pc>:8787/mcp。

  • 所有模式中最简单——无需隧道,无需快照仪式。

客户端接入在所有模式下都是相同的:客户端指向网关的 MCP URL;网关与配置的任何浏览器模式通信。


启动运行手册 — 模式 C(Linux 网关 + Windows 浏览器)

已确认的设置:一台 Linux 服务器运行网关;一台始终在线的 Windows PC 运行真实的 Chrome(已登录一次)和一个 SSH 反向隧道。Linux 服务器上不下载浏览器(仅 playwright-core)。

Windows PC(一次性操作,然后保持运行)— 参见 windows/README.md

  1. windows\start-browser.ps1 → 在 127.0.0.1:9222 上启动专用 Chrome,配置文件为 C:\dept-search-profile。使用共享账户登录(SSO/2FA)。保持打开状态。

  2. $env:GATEWAY_SSH = "linuxuser@gateway.server"; windows\start-tunnel.ps1 → 维护 ssh -R 9222:127.0.0.1:9222 gateway,自动重连。

  3. 将两者设为计划任务(启动时/登录时,无论用户是否登录都运行),使该 PC 成为一个自我修复的浏览器设备。

Linux 网关服务器(本机)

cd dept-web-search-gateway
cp .env.example .env
# edit .env:
#   BROWSER_MODE=cdp                       (default)
#   CDP_ENDPOINT=http://127.0.0.1:9222     (the tunneled port, local on this server)
#   HOST=0.0.0.0
#   ALLOWED_HOSTS=search.internal,localhost   # hostnames clients will use
#   GATEWAY_TOKEN=...                      (optional; else rely on network ACL)
npm install                 # lean — playwright-core, no Chromium download
npm run build               # typecheck
npm start                   # dev (tsx); or `npm run build && npm run start:prod`
curl http://127.0.0.1:8787/health         # {"ok":true,...}

将客户端指向 http://<this-server>:8787/mcp(参见客户端接入)。

检查隧道是否正常

在 Linux 服务器上:

curl -s http://127.0.0.1:9222/json/version   # Chrome's JSON → tunnel + Chrome are up

空/连接被拒绝 → Windows Chrome 或反向隧道尚未运行;web_search 将失败,直到它运行起来。


设置(一次性)

cd dept-web-search-gateway
npm install                 # also runs `playwright install chromium`
cp .env.example .env       # then edit .env (see knobs below)

1) 播种共享登录(关键步骤)

在有显示器的机器上(或在 xvfb-run -a 下)运行一次:

npm run login
# or, for an internal portal:
LOGIN_START_URL=https://wiki.internal npm run login

一个真实的 Chrome 窗口会打开。使用共享服务账户登录(SSO / 2FA),确认您已登录到搜索引擎/门户,然后关闭窗口。会话将持久化到 BROWSER_PROFILE_DIR(默认为 ./.profile),并由无头网关从现在开始重用。

服务器是无头的?在 Windows PC 上执行 npm run login 步骤,然后按照上面 “无头 Linux 服务器 + 用于登录的 Windows PC” 中的描述选择模式 B(将 auth.json 复制到 Linux)或模式 C(SSH 隧道 CDP 到 Linux)。 当 SSO 会话过期时续期:模式 A/B → 重新运行 npm run login(对于 B 还要重新复制 auth.json);模式 C → 只需在 Windows Chrome 中重新登录。

2) 运行网关

npm start                   # dev (tsx)
# or production:
npm run build && npm run start:prod

您应该看到:

[server] MCP gateway on http://0.0.0.0:8787/mcp  (engine=bing)
[server] profile=./.profile

客户端接入(将这些提供给您的同事)

将 search.internal / 8787 替换为您的网关主机/端口。每个人都使用相同的 URL。

Chatbox (≥1.14)

设置 → MCP → 添加服务器 → 选择 远程 / URL:

  • URL:http://search.internal:8787/mcp

  • (如果设置了 GATEWAY_TOKEN)在客户端支持的地方添加一个标头 Authorization: Bearer <TOKEN>;否则使用网络 ACL 保护。

一键深度链接(放在您的内网页面上):

chatbox://mcp/install?server=<base64 of {"name":"websearch","url":"http://search.internal:8787/mcp"}>

OpenCode — 所有三种风格

添加到 opencode.json(项目)或 ~/.config/opencode/opencode.json(全局):

{
  "mcp": {
    "websearch": {
      "type": "remote",
      "url": "http://search.internal:8787/mcp",
      "enabled": true
    }
  }
}
  • 本地 opencode:相同的代码片段,主机为 127.0.0.1 或网关主机。

  • 服务器 opencode:进程在服务器上运行 → 直接指向网关的内部 URL(服务器必须能通过内部网络访问它)。

  • vscode-remote opencode:进程在远程主机上运行 → 指向网关的内部 URL(可从该主机访问)。无需隧道,因为网关在内部网络上。

  • 验证:opencode mcp list。

Claude Code

claude mcp add --transport http websearch http://search.internal:8787/mcp
# with a token:
claude mcp add --transport http --header "Authorization: Bearer <TOKEN>" \
  websearch http://search.internal:8787/mcp

Cline / Cursor / 其他

如果它们支持远程 MCP,指向相同的 URL。如果它们只支持 stdio,运行一个调用 HTTP 网关的微小本地 shim(一个 20 行的包装器)——此处未包含,但添加起来很简单。


暴露的工具

工具

参数

返回

web_search

query(字符串,必需),engine(bing|google|duck|custom,可选)

列表 {title, url, snippet} 作为文本 + JSON

read_webpage

url(字符串,必需)

# title + 主要文本(≤20k 字符),登录/SSO 已处理

Chatbox/OpenCode/Claude Code 中的代理将在需要最新信息时调用 web_search,并调用 read_webpage 来读取特定页面——无需额外配置。


配置旋钮(.env)

变量

默认值

含义

HOST

0.0.0.0

绑定地址。127.0.0.1 = 仅本地主机(+自动 DNS 重新绑定保护)

ALLOWED_HOSTS

—

客户端使用的主机名逗号列表(启用 Host 标头验证)。绑定 0.0.0.0 时设置

PORT

8787

监听端口

GATEWAY_TOKEN

—

如果设置,要求 Authorization: Bearer <token>。空 = 无认证(仅网络 ACL)

BROWSER_MODE

cdp

persistent / storagestate / cdp — 参见拓扑部分

CDP_ENDPOINT

http://127.0.0.1:9222

cdp 模式:所连接浏览器的 CDP URL(通常是一个隧道端口)

STORAGE_STATE_FILE

./auth.json

storagestate 模式:在 Windows 上导出的登录快照,复制到这里

BROWSER_PROFILE_DIR

./.profile

persistent 模式:保存共享登录的 Chrome 配置文件

HEADLESS

true

false 仅用于调试

MAX_CONCURRENT_PAGES

4

并发上限(一个 Chrome,隔离的标签页)

PAGE_TIMEOUT_MS

20000

每页硬超时

SEARCH_ENGINE

bing

bing(调优的提取器) / google / duck / custom

SEARCH_URL_TEMPLATE

—

自定义 URL,带有 {q} 占位符,例如 https://wiki.internal/search?q={q}(覆盖引擎 URL)

RESULT_COUNT

10

每次查询的结果数

SEARXNG_URL

—

可选的公共搜索回退(需要出站互联网),例如 http://127.0.0.1:8080


添加自定义内部门户提取器

src/tools.ts 中的 extractBing 针对 Bing 的 DOM 进行了调优。对于内部门户,添加 extractPortal(page, count) 并在 searchWithBrowser 中根据引擎名称选择它。通用的 extractGeneric 已经返回锚链接 + 附近文本,作为未知 DOM 的可用回退。


安全与运维说明

  • 绑定与暴露:最好将网关保留在内部网络上。如果您绑定 0.0.0.0,请设置 ALLOWED_HOSTS 并使用防火墙/网络 ACL,或设置 GATEWAY_TOKEN,或将其放在 SSO 反向代理后面。

  • 共享配置文件 = 共享身份:每次搜索都归因于共享账户。对于部门服务账户来说没问题;如果目标审计按用户或有限额,请检查。

  • 会话续期:当 SSO 过期时重新运行 npm run login。考虑设置一个每周 cron 任务发送提醒邮件,或一个健康探测来检测登录墙(read_webpage 在已知需要登录的 URL 上返回登录页面文本)。

  • 并发/规模:一个带有隔离标签页的 Chrome 可以处理一个小型部门。如果饱和,可以扩展到浏览器池(N 个持久上下文)——withPage 接缝是唯一需要更改的地方。

  • Linux 上的无头 Chrome:已设置 --no-sandbox --disable-dev-shm-usage(容器友好)。


开发与测试

  • 探测脚本位于 scripts/ 中,并从 ../dist/ 导入,因此先构建:npm run build。

    • scripts/probe-search.mjs "<query>" — 直接驱动共享浏览器(绕过 MCP);验证 CDP 连接 + Bing 提取器。

    • scripts/probe-mcp.mjs <url> "<query>" — 通过 Streamable HTTP(真实的客户端路径)连接到正在运行的网关,列出工具,调用 web_search。先启动网关:node --env-file=.env dist/server.js。

  • 开发模式(npm start → tsx):在 npm 11 下,tsx 的传递依赖 esbuild 的 postinstall 默认被 allow-scripts 阻止。批准一次(npm approve-scripts)或直接在所有地方使用编译路径:npm run build && node --env-file=.env dist/server.js。


状态

这是一个 可审查的概念验证 / 骨架 —— 已对照 v2 MCP SDK API (@modelcontextprotocol/server 2.x、createMcpHandler / createMcpExpressApp / requireBearerAuth / toNodeHandler)和 Playwright 的持久上下文 API 进行了验证。投入生产之前:锁定精确的依赖版本、添加测试,并且如果在受信任的内部网络之外暴露此功能,请加固认证层(使用 JWT / 内省机制,而不是静态令牌)。

设计上下文(Mode A/B/C 拓扑、跨操作系统 Cookie 加密陷阱、 SearXNG 边界)详见上面的 “Headless Linux 服务器 + 用于登录的 Windows PC” 部分。

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers