Department Web-Search MCP Gateway
Provides web search capabilities using Google as a search engine, leveraging a shared browser session for authentication.
Acts as a fallback search engine for public web searches when the browser-based search returns no results.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Department Web-Search MCP Gatewaysearch the internal wiki for deployment runbook"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Department Web-Search MCP Gateway
A self-hosted web-search service the whole department can share. It reuses a
single logged-in browser session (a shared service account), so intranet /
SSO / consent-wall logins are handled once — every client just calls a
web_search tool, no per-user login or API key.
Any MCP client connects to one URL:
Chatbox (≥1.14)
OpenCode — local, on a shared server, or via vscode-remote
Claude Code (and other coding agents that speak MCP)
It is the T1 “centralized search gateway” from the research notes: one internal machine + one shared Chrome profile + one HTTP MCP endpoint.
How it works
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 nothingcreateMcpHandler serves both 2025-era and 2026-era MCP clients on the same
/mcp endpoint, so client transport compatibility is not a concern.
Headless Linux servers + a Windows PC for login
Servers have no GUI, but a human can log in on a Windows PC. Pick a mode in
.env (BROWSER_MODE) — the code is identical, only config differs.
⚠️ Do NOT copy a Windows Chrome profile directory to Linux. Chromium encrypts cookies with OS-bound keys (DPAPI on Windows, keyring/”peanuts” on Linux), so a copied profile loses the login silently. Use one of the cross-OS-safe modes below.
Mode C — BROWSER_MODE=cdp (recommended): Linux gateway attaches to the Windows browser
Windows PC (stays on): log in once with the shared account, then keep Chrome running with a local-only debug port:
chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 ^ --user-data-dir=C:\dept-search-profileCarry that port safely to the Linux server with an SSH reverse tunnel (run on the Windows PC; Win10/11 ships OpenSSH):
ssh -R 9222:127.0.0.1:9222 linuxuser@gateway.serverLinux server:
.env→BROWSER_MODE=cdp,CDP_ENDPOINT=http://127.0.0.1:9222(local on the server, tunneled back to the Windows browser). Thennpm start.Login stays live (cookies refresh as the browser is used); no profile copy; the unauthenticated CDP port is never on the network. Downside: Windows PC off → searches fail until it’s back (use Mode B if that’s unacceptable).
Mode B — BROWSER_MODE=storagestate: snapshot, Linux self-sufficient
Windows PC:
npm run login(headed), log in, press Enter → writesauth.json(OS-agnostic JSON of cookies + localStorage).Copy
auth.jsonto the Linux server, setBROWSER_MODE=storagestate,STORAGE_STATE_FILE=./auth.json, runnpm start. Linux runs its own headless browser loading the snapshot — no tunnel, survives the Windows PC being off.Trade-off: a frozen snapshot — re-export when the SSO cookie expires; carries only cookies + localStorage (not IndexedDB/client certs) — fine for most SSO.
Mode A — BROWSER_MODE=persistent: Windows PC runs everything
If a spare Windows PC can be the always-on service host:
npm run loginthere (seeds the profile), thennpm startwithBROWSER_MODE=persistent.Linux servers are pure clients pointing at
http://<windows-pc>:8787/mcp.Simplest of all — no tunnel, no snapshot ceremony.
Client onboarding is identical in every mode: clients point at the gateway’s MCP URL; the gateway talks to whichever browser mode is configured.
Bring-up runbook — Mode C (Linux gateway + Windows browser)
The confirmed setup: a Linux server runs the gateway; an always-on
Windows PC runs a real Chrome (logged in once) and an SSH reverse tunnel.
No browser is downloaded to the Linux server (playwright-core only).
Windows PC (once, then leave running) — see windows/README.md
windows\start-browser.ps1→ dedicated Chrome on127.0.0.1:9222, profileC:\dept-search-profile. Sign in with the shared account (SSO/2FA). Keep it open.$env:GATEWAY_SSH = "linuxuser@gateway.server"; windows\start-tunnel.ps1→ maintainsssh -R 9222:127.0.0.1:9222 gateway, auto-reconnects.Make both Scheduled Tasks (At startup / On logon, run whether logged on or not) so the PC is a self-healing browser appliance.
Linux gateway server (this machine)
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,...}Point clients at http://<this-server>:8787/mcp (see Client onboarding).
Sanity-check the tunnel
On the Linux server:
curl -s http://127.0.0.1:9222/json/version # Chrome's JSON → tunnel + Chrome are upEmpty / connection refused → the Windows Chrome or the reverse tunnel isn’t
running yet; web_search will fail until it is.
Setup (one-time)
cd dept-web-search-gateway
npm install # also runs `playwright install chromium`
cp .env.example .env # then edit .env (see knobs below)1) Seed the shared login (the crux)
Run once on a machine with a display (or under xvfb-run -a):
npm run login
# or, for an internal portal:
LOGIN_START_URL=https://wiki.internal npm run loginA real Chrome window opens. Sign in with the shared service account
(SSO / 2FA), confirm you’re logged in to the search engine / portal, then close
the window. The session is persisted to BROWSER_PROFILE_DIR (default ./.profile)
and reused by the headless gateway from now on.
Servers are headless? Do the
npm run loginstep on the Windows PC, then choose Mode B (copyauth.jsonto Linux) or Mode C (SSH-tunnel CDP to Linux) as described in “Headless Linux servers + a Windows PC for login” above. Renewal when the SSO session expires: Mode A/B → re-runnpm run login(and re-copyauth.jsonfor B); Mode C → just re-login in the Windows Chrome.
2) Run the gateway
npm start # dev (tsx)
# or production:
npm run build && npm run start:prodYou should see:
[server] MCP gateway on http://0.0.0.0:8787/mcp (engine=bing)
[server] profile=./.profileClient onboarding (give these to your colleagues)
Replace search.internal / 8787 with your gateway host/port. Everyone uses
the same URL.
Chatbox (≥1.14)
Settings → MCP → Add Server → choose Remote / URL:
URL:
http://search.internal:8787/mcp(if
GATEWAY_TOKENis set) add a headerAuthorization: Bearer <TOKEN>where the client supports it; otherwise protect with network ACL.
One-click deep link (put on your intranet page):
chatbox://mcp/install?server=<base64 of {"name":"websearch","url":"http://search.internal:8787/mcp"}>OpenCode — all three flavors
Add to opencode.json (project) or ~/.config/opencode/opencode.json (global):
{
"mcp": {
"websearch": {
"type": "remote",
"url": "http://search.internal:8787/mcp",
"enabled": true
}
}
}Local opencode: same snippet, host =
127.0.0.1or the gateway host.Server opencode: the process runs on the server → point at the gateway’s internal URL directly (the server must reach it over the internal network).
vscode-remote opencode: the process runs on the remote host → point at the gateway’s internal URL (reachable from that host). No tunneling needed because the gateway is on the internal network.
Verify:
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/mcpCline / Cursor / others
If they support remote MCP, point at the same URL. If they only do stdio, run a tiny local shim that calls the HTTP gateway (a 20-line wrapper) — not included here, but trivial to add.
Tools exposed
Tool | Args | Returns |
|
| list of |
|
|
|
The agent in Chatbox/OpenCode/Claude Code will call web_search when it needs
fresh info, and read_webpage to read a specific page — no extra wiring.
Config knobs (.env)
Var | Default | Meaning |
|
| bind address. |
| — | comma list of hostnames clients use (enables Host-header validation). Set when binding 0.0.0.0 |
|
| listen port |
| — | if set, require |
|
|
|
|
| cdp mode: the attached browser’s CDP URL (usually a tunneled port) |
|
| storagestate mode: login snapshot exported on Windows, copied here |
|
| persistent mode: Chrome profile holding the shared login |
|
|
|
|
| concurrency cap (one Chrome, isolated tabs) |
|
| per-page hard timeout |
|
|
|
| — | custom URL with |
|
| results per query |
| — | optional public-search fallback (needs outbound internet), e.g. |
Adding a custom internal-portal extractor
extractBing in src/tools.ts is tuned for Bing’s DOM. For an internal portal,
add extractPortal(page, count) and select it on the engine name in
searchWithBrowser. The generic extractGeneric already returns anchor links +
nearby text as a passable fallback for unknown DOMs.
Security & ops notes
Bind & expose: prefer keeping the gateway on the internal network. If you bind
0.0.0.0, setALLOWED_HOSTSand use a firewall / network ACL, or setGATEWAY_TOKEN, or put it behind an SSO reverse proxy.Shared profile = shared identity: every search is attributed to the shared account. Fine for a department service account; review if the target audits per-user or has quota.
Session renewal: re-run
npm run loginwhen SSO expires. Consider a weekly cron that emails a reminder, or a health probe that detects a login wall (read_webpageon a known-login-required URL returns the login page text).Concurrency / scale: one Chrome with isolated tabs handles a small department. Grow to a browser pool (N persistent contexts) if it saturates — the
withPageseam is the only place to change.Headless Chrome on Linux:
--no-sandbox --disable-dev-shm-usageare already set (container-friendly).
Development & testing
Probes live in
scripts/and import from../dist/, so build first:npm run build.scripts/probe-search.mjs "<query>"— drives the shared browser directly (bypasses MCP); validates CDP attach + the Bing extractor.scripts/probe-mcp.mjs <url> "<query>"— connects to a running gateway over Streamable HTTP (the real client path), lists tools, callsweb_search. Start the gateway first:node --env-file=.env dist/server.js.
Dev mode (
npm start→ tsx): under npm 11,tsx's transitiveesbuildpostinstall is blocked byallow-scriptsby default. Approve it once (npm approve-scripts) or just use the compiled path everywhere:npm run build && node --env-file=.env dist/server.js.
Status
This is a reviewable PoC / skeleton — verified against the v2 MCP SDK API
(@modelcontextprotocol/server 2.x, createMcpHandler / createMcpExpressApp
/ requireBearerAuth / toNodeHandler) and Playwright’s persistent-context
API. Before production: pin exact dependency versions, add tests, and harden the
auth layer (JWT / introspection instead of a static token) if you expose it
beyond a trusted internal network.
Design context (Mode A/B/C topologies, the cross-OS cookie-encryption gotcha, SearXNG boundaries) is in the “Headless Linux servers + a Windows PC for login” section above.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Multi-engine search for AI agents. Trust scoring, local corpus, MCP-native. Self-hostable, BYOK.
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/yangsheng6810/web-search-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server