Skip to main content
Glama
yangsheng6810

Department Web-Search MCP Gateway

Department Web-Search MCP Gateway

Un servicio de búsqueda web autoalojado que todo el departamento puede compartir. Reutiliza una única sesión de navegador iniciada (una cuenta de servicio compartida), por lo que los inicios de sesión en intranet/SSO/muros de consentimiento se manejan una vez — cada cliente simplemente llama a una herramienta web_search, sin necesidad de inicio de sesión por usuario ni clave API.

Cualquier cliente MCP se conecta a una URL:

  • Chatbox (≥1.14)

  • OpenCode — local, en un servidor compartido, o mediante vscode-remote

  • Claude Code (y otros agentes de codificación que hablen MCP)

Es la "puerta de enlace de búsqueda centralizada" T1 de las notas de investigación: una máquina interna + un perfil compartido de Chrome + un endpoint MCP HTTP.


Cómo funciona

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 sirve tanto a clientes MCP de la era 2025 como de la era 2026 en el mismo endpoint /mcp, por lo que la compatibilidad del transporte del cliente no es una preocupación.


Servidores Linux sin pantalla + un PC con Windows para iniciar sesión

Los servidores no tienen interfaz gráfica, pero un humano puede iniciar sesión en un PC con Windows. Elige un modo en .env (BROWSER_MODE) — el código es idéntico, solo cambia la configuración.

⚠️ NO copies un directorio de perfil de Chrome de Windows a Linux. Chromium cifra las cookies con claves vinculadas al sistema operativo (DPAPI en Windows, keyring/"peanuts" en Linux), por lo que un perfil copiado pierde la sesión iniciada silenciosamente. Usa uno de los modos seguros entre sistemas operativos a continuación.

Modo C — BROWSER_MODE=cdp (recomendado): La puerta de enlace Linux se conecta al navegador Windows

  • PC con Windows (permanece encendido): inicia sesión una vez con la cuenta compartida, luego mantén Chrome ejecutándose con un puerto de depuración solo local:

    chrome --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 ^
           --user-data-dir=C:\dept-search-profile
  • Lleva ese puerto de forma segura al servidor Linux con un túnel inverso SSH (ejecutar en el PC con Windows; Win10/11 incluye OpenSSH):

    ssh -R 9222:127.0.0.1:9222 linuxuser@gateway.server
  • Servidor Linux: .envBROWSER_MODE=cdp, CDP_ENDPOINT=http://127.0.0.1:9222 (local en el servidor, tunelizado de vuelta al navegador Windows). Luego npm start.

  • La sesión iniciada permanece activa (las cookies se actualizan mientras se usa el navegador); sin copia de perfil; el puerto CDP no autenticado nunca está en la red. Inconveniente: PC con Windows apagado → las búsquedas fallan hasta que se enciende (usa el Modo B si eso es inaceptable).

Modo B — BROWSER_MODE=storagestate: instantánea, Linux autosuficiente

  • PC con Windows: npm run login (con pantalla), inicia sesión, presiona Enter → escribe auth.json (JSON de cookies + localStorage independiente del sistema operativo).

  • Copia auth.json al servidor Linux, configura BROWSER_MODE=storagestate, STORAGE_STATE_FILE=./auth.json, ejecuta npm start. Linux ejecuta su propio navegador sin pantalla cargando la instantánea — sin túnel, sobrevive al apagado del PC con Windows.

  • Compensación: una instantánea congelada — reexportar cuando caduque la cookie SSO; solo lleva cookies + localStorage (no IndexedDB/certificados de cliente) — suficiente para la mayoría de SSO.

Modo A — BROWSER_MODE=persistent: El PC con Windows lo ejecuta todo

  • Si un PC con Windows de repuesto puede ser el anfitrión del servicio siempre encendido: ejecuta npm run login allí (siembra el perfil), luego npm start con BROWSER_MODE=persistent.

  • Los servidores Linux son clientes puros que apuntan a http://<windows-pc>:8787/mcp.

  • El más simple de todos — sin túnel, sin ceremonia de instantánea.

La incorporación del cliente es idéntica en todos los modos: los clientes apuntan a la URL MCP de la puerta de enlace; la puerta de enlace se comunica con el modo de navegador que esté configurado.


Manual de puesta en marcha — Modo C (puerta de enlace Linux + navegador Windows)

La configuración confirmada: un servidor Linux ejecuta la puerta de enlace; un PC con Windows siempre encendido ejecuta un Chrome real (iniciado sesión una vez) y un túnel inverso SSH. No se descarga ningún navegador en el servidor Linux (solo playwright-core).

PC con Windows (una vez, luego dejar ejecutándose) — consulta windows/README.md

  1. windows\start-browser.ps1 → Chrome dedicado en 127.0.0.1:9222, perfil C:\dept-search-profile. Inicia sesión con la cuenta compartida (SSO/2FA). Mantenlo abierto.

  2. $env:GATEWAY_SSH = "linuxuser@gateway.server"; windows\start-tunnel.ps1 → mantiene ssh -R 9222:127.0.0.1:9222 gateway, reconexión automática.

  3. Convierte ambos en Tareas programadas (Al iniciar / Al iniciar sesión, ejecutar esté o no el usuario conectado) para que el PC sea un dispositivo de navegador auto-reparable.

Servidor de puerta de enlace Linux (esta máquina)

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,...}

Apunta los clientes a http://<este-servidor>:8787/mcp (consulta Incorporación de clientes).

Verificar el túnel

En el servidor Linux:

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

Vacío / conexión rechazada → el Chrome de Windows o el túnel inverso aún no se están ejecutando; web_search fallará hasta que lo estén.


Configuración (una vez)

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

1) Sembrar el inicio de sesión compartido (la clave)

Ejecutar una vez en una máquina con pantalla (o bajo xvfb-run -a):

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

Se abre una ventana real de Chrome. Inicia sesión con la cuenta de servicio compartida (SSO / 2FA), confirma que has iniciado sesión en el motor de búsqueda / portal, luego cierra la ventana. La sesión se persiste en BROWSER_PROFILE_DIR (por defecto ./.profile) y la puerta de enlace sin pantalla la reutilizará a partir de ahora.

¿Los servidores no tienen pantalla? Haz el paso npm run login en el PC con Windows, luego elige el Modo B (copia auth.json a Linux) o el Modo C (túnel SSH CDP a Linux) como se describe en "Servidores Linux sin pantalla + un PC con Windows para iniciar sesión" arriba. Renovación cuando caduque la sesión SSO: Modo A/B → volver a ejecutar npm run login (y recopiar auth.json para B); Modo C → simplemente volver a iniciar sesión en el Chrome de Windows.

2) Ejecutar la puerta de enlace

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

Deberías ver:

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

Incorporación de clientes (entregar esto a tus colegas)

Reemplaza search.internal / 8787 con el host/puerto de tu puerta de enlace. Todos usan la misma URL.

Chatbox (≥1.14)

Configuración → MCP → Agregar servidor → elige Remoto / URL:

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

  • (si GATEWAY_TOKEN está configurado) añade una cabecera Authorization: Bearer <TOKEN> donde el cliente lo soporte; de lo contrario, protege con ACL de red.

Enlace profundo de un solo clic (ponlo en tu página de intranet):

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

OpenCode — las tres variantes

Añadir a opencode.json (proyecto) o ~/.config/opencode/opencode.json (global):

{
  "mcp": {
    "websearch": {
      "type": "remote",
      "url": "http://search.internal:8787/mcp",
      "enabled": true
    }
  }
}
  • opencode local: el mismo fragmento, host = 127.0.0.1 o el host de la puerta de enlace.

  • opencode de servidor: el proceso se ejecuta en el servidor → apunta directamente a la URL interna de la puerta de enlace (el servidor debe poder alcanzarla a través de la red interna).

  • opencode vscode-remote: el proceso se ejecuta en el host remoto → apunta a la URL interna de la puerta de enlace (accesible desde ese host). No se necesita túnel porque la puerta de enlace está en la red interna.

  • Verificar: 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 / otros

Si soportan MCP remoto, apunta a la misma URL. Si solo hacen stdio, ejecuta un pequeño shim local que llama a la puerta de enlace HTTP (un envoltorio de 20 líneas) — no incluido aquí, pero trivial de añadir.


Herramientas expuestas

Herramienta

Argumentos

Retorna

web_search

query (str, obligatorio), engine (bing|google|duck|custom, opcional)

lista de {title, url, snippet} como texto + JSON

read_webpage

url (str, obligatorio)

# título + texto principal (≤20k chars), login/SSO manejado

El agente en Chatbox/OpenCode/Claude Code llamará a web_search cuando necesite información actualizada, y a read_webpage para leer una página específica — sin cableado adicional.


Opciones de configuración (.env)

Variable

Valor por defecto

Significado

HOST

0.0.0.0

dirección de enlace. 127.0.0.1 = solo localhost (+protección automática contra reenlace DNS)

ALLOWED_HOSTS

lista separada por comas de nombres de host que usan los clientes (habilita validación de cabecera Host). Configurar al enlazar 0.0.0.0

PORT

8787

puerto de escucha

GATEWAY_TOKEN

si se configura, requiere Authorization: Bearer <token>. Vacío = sin autenticación (solo ACL de red)

BROWSER_MODE

cdp

persistent / storagestate / cdp — consulta la sección de topología

CDP_ENDPOINT

http://127.0.0.1:9222

modo cdp: la URL CDP del navegador adjunto (generalmente un puerto tunelizado)

STORAGE_STATE_FILE

./auth.json

modo storagestate: instantánea de inicio de sesión exportada en Windows, copiada aquí

BROWSER_PROFILE_DIR

./.profile

modo persistent: perfil de Chrome que contiene el inicio de sesión compartido

HEADLESS

true

false solo para depuración

MAX_CONCURRENT_PAGES

4

límite de concurrencia (un Chrome, pestañas aisladas)

PAGE_TIMEOUT_MS

20000

tiempo de espera máximo por página

SEARCH_ENGINE

bing

bing (extractor optimizado) / google / duck / custom

SEARCH_URL_TEMPLATE

URL personalizada con marcador {q}, ej. https://wiki.internal/search?q={q} (anula la URL del motor)

RESULT_COUNT

10

resultados por consulta

SEARXNG_URL

alternativa opcional de búsqueda pública (necesita internet de salida), ej. http://127.0.0.1:8080


Añadir un extractor personalizado para portal interno

extractBing en src/tools.ts está optimizado para el DOM de Bing. Para un portal interno, añade extractPortal(page, count) y selecciónalo por el nombre del motor en searchWithBrowser. El genérico extractGeneric ya retorna enlaces de anclaje + texto cercano como alternativa aceptable para DOMs desconocidos.


Notas de seguridad y operaciones

  • Enlazar y exponer: prefiere mantener la puerta de enlace en la red interna. Si enlazas 0.0.0.0, configura ALLOWED_HOSTS y usa un cortafuegos / ACL de red, o configura GATEWAY_TOKEN, o ponlo detrás de un proxy inverso SSO.

  • Perfil compartido = identidad compartida: cada búsqueda se atribuye a la cuenta compartida. Está bien para una cuenta de servicio del departamento; revisa si el destino audita por usuario o tiene cuota.

  • Renovación de sesión: vuelve a ejecutar npm run login cuando caduque el SSO. Considera un cron semanal que envíe un recordatorio por correo, o un sondeo de salud que detecte un muro de inicio de sesión (read_webpage en una URL que requiera inicio de sesión conocido devuelve el texto de la página de inicio de sesión).

  • Concurrencia / escala: un Chrome con pestañas aisladas maneja un departamento pequeño. Crece a un grupo de navegadores (N contextos persistentes) si se satura — la costura withPage es el único lugar para cambiar.

  • Chrome sin pantalla en Linux: --no-sandbox --disable-dev-shm-usage ya están configurados (amigable con contenedores).


Desarrollo y pruebas

  • Sondas están en scripts/ e importan desde ../dist/, así que primero compila: npm run build.

    • scripts/probe-search.mjs "<consulta>" — maneja el navegador compartido directamente (omite MCP); valida el adjunto CDP + el extractor de Bing.

    • scripts/probe-mcp.mjs <url> "<consulta>" — se conecta a una puerta de enlace en ejecución a través de Streamable HTTP (la ruta real del cliente), lista herramientas, llama a web_search. Inicia la puerta de enlace primero: node --env-file=.env dist/server.js.

  • Modo de desarrollo (npm start → tsx): en npm 11, el esbuild transitivo de tsx postinstall está bloqueado por allow-scripts por defecto. Apruebalo una vez (npm approve-scripts) o simplemente usa la ruta compilada en todos lados: npm run build && node --env-file=.env dist/server.js.


Estado

Este es un PoC / esqueleto revisable — verificado contra la API v2 del SDK de MCP (@modelcontextprotocol/server 2.x, createMcpHandler / createMcpExpressApp / requireBearerAuth / toNodeHandler) y la API de contexto persistente de Playwright. Antes de producción: fijar versiones exactas de dependencias, agregar pruebas y endurecer la capa de autenticación (JWT / introspección en lugar de un token estático) si se expone más allá de una red interna de confianza.

El contexto de diseño (topologías Modo A/B/C, el problema de cifrado de cookies entre sistemas operativos, los límites de SearXNG) se encuentra en la sección “Headless Linux servers + a Windows PC for login” anterior.

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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.

View all MCP Connectors

Latest Blog Posts

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