Skip to main content
Glama

EnriWeb

EnriWeb is a Model Context Protocol (MCP) server over stdio that exposes web search and URL fetching tools by delegating execution to EnriProxy.

If your MCP client can call MCP tools, it can do web search / fetch in a consistent way without implementing provider-specific scraping logic.

What this project is

  • An MCP server process your MCP host launches (OpenCode, Claude Code, Codex, etc.)

  • A thin client for EnriProxy (input validation + structured output)

Related MCP server: url-context-mcp

Requirements

  • Node.js >= 24 (Node 24 LTS)

  • A reachable EnriProxy server with:

    • POST /v1/tools/web_search

    • POST /v1/tools/web_fetch

  • An EnriProxy API key (configured on the EnriProxy side)

Install

# Global install
npm install -g @bedolla/enriweb

# Or run without installing
npx -y @bedolla/enriweb@latest --help

Build

npm install
npm run typecheck
npm run build

Usage

1) Configure your MCP host

EnriWeb runs as an MCP server over stdio. Your MCP host is responsible for launching the process.

Example: global install

{
  "EnriWeb": {
    "type": "stdio",
    "command": "enriweb",
    "args": [],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8888",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY"
    }
  }
}

Example: no install (always uses whatever npm currently tags as latest)

{
  "EnriWeb": {
    "type": "stdio",
    "command": "npx",
    "args": ["-y", "@bedolla/enriweb@latest"],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8888",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY"
    }
  }
}
{
  "EnriWeb": {
    "type": "stdio",
    "command": "node",
    "args": ["C:\\\\Users\\\\Administrator\\\\Projects\\\\EnriWeb\\\\dist\\\\index.js"],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8888",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY"
    }
  }
}

Configuration

EnriWeb is configured via environment variables:

  • ENRIPROXY_URL (string, optional, default: http://127.0.0.1:8888)

  • ENRIPROXY_API_KEY (string, required)

  • ENRIWEB_TIMEOUT_MS (string, optional, default: 300000)

    • Parsed as an integer (milliseconds); fetch budget at double the proxy's total fetch budget (150 s) plus margin. Operator policy is a uniform 5-minute tool budget for both tools; when slow SearXNG engines are kept (server budget up to ~310 s), raise ENRIWEB_SEARCH_TIMEOUT_MS beyond the 300 s default.

  • ENRIWEB_SEARCH_TIMEOUT_MS (string, optional, default: 300000)

    • Parsed as an integer (milliseconds); uniform 5-minute tool budget. Residual: the SearXNG server budget alone can reach 310 s on slow-engine days, so searches slower than 300 s still end in the retryable timeout; raise the env var to extend it.

  • ENRIWEB_WEB_FETCH_DEFAULT_MAX_CHARS (string, optional, default: 200000, max 4000000)

    • Parsed as an integer.

  • ENRIWEB_GITHUB_TOKEN (string, optional)

    • Used for GitHub API enrichment to improve rate limits.

  • ENRIWEB_SCREENSHOT_MODE (string, optional, one of auto | force | none | analyze)

    • Installation-level default for web_fetch screenshots, applied when the host omits the screenshot parameter (explicit host values always win). Designed for clients whose provider rejects image blocks inside tool results (OpenAI-compatible Chat Completions APIs accept images in user messages but not in tool messages — e.g. OpenCode surfaces "this model does not support image input" even for vision models). Set analyze on such installs: the server captures the page and returns a TEXT description per segment (screenshot_analyses) instead of image blocks. Invalid values warn on stderr and are ignored.

  • ENRIWEB_SEARCH_ENGINES (string, optional, e.g. google or google,bing)

    • Operator SearXNG engine selector applied to every web_search call. Overrides the EnriProxy server default without reconfiguring the server; unset uses the server configuration. This is operator configuration on purpose — the model-facing web_search schema exposes no engine option so models cannot narrow their own results. Invalid values warn on stderr and are ignored.

MCP tools

EnriWeb exposes these MCP tools:

  • web_search

  • web_fetch

General notes:

  • All tools accept a single JSON object as their input (the MCP arguments for that tool).

  • EnriWeb returns both:

    • a short human-readable preview (content)

    • the full result payload (structuredContent)


Search the web via EnriProxy.

Inputs:

  • query (string or string[], required unless queries is provided): the search query. As an array it accepts a batch of 1 to 4 non-blank queries (each entry trimmed; duplicates collapse) — equivalent to sending queries.

  • queries (string[], optional): batch of 1 to 4 queries. Takes precedence over query. EnriProxy runs every query in parallel, merges results by relevance rank, and deduplicates by URL.

  • recency (string, optional, default: noLimit)

    • One of: oneDay | oneWeek | oneMonth | oneYear | noLimit

  • allowed_domains (string[], optional; alias allowedDomains): allowlist of domains to include.

  • blocked_domains (string[], optional; alias blockedDomains): blocklist of domains to exclude.

  • search_prompt (string, optional; alias searchPrompt): extra context to refine the search intent. Capped at 2000 characters server-side (the excess is trimmed).

Outputs (structuredContent):

  • query / queries: the executed query (or batch).

  • results[]: entries with url, title, snippet, and published_at when available.

  • count: number of returned results.

  • perQuery[]: for batched searches, { query, urls } attribution groups so each result can be traced back to the query that found it.

  • failedQueries[]: queries that failed while at least one other succeeded (their section is absent from perQuery).

  • fetchedContents[] / fetchedCount: when server-side auto-fetch is enabled, the verified content of the top result pages (url, title, content, truncated). Read these before concluding information is missing.

  • verified[]: registry verification rows for npm / PyPI / crates.io / NuGet / GitHub URLs found in the results (kind, name, latest_stable, latest_prerelease, status, error).

The result count is decided by the server (its configured limit); there is no per-call count parameter.

Example arguments object:

{
  "queries": ["qdrant docker compose autostart", "qdrant container restart policy"],
  "recency": "oneMonth"
}

web_fetch

Fetch and read content from a URL via EnriProxy.

Inputs:

  • url (string, required unless cursor is provided): full URL (http:// or https://).

  • cursor (string, optional): opaque cursor returned by a previous web_fetch call. A valid cursor always wins over a coexisting url. Send url together with cursor whenever you know it: if the cursor expired server-side (TTL ~10 minutes), EnriWeb transparently re-fetches the url with the same parameters and returns fresh content with a new cursor (recovered_from_expired_cursor) instead of an error.

  • action ("delete", optional): releases the server-side capture owned by cursor. Send with cursor; other parameters are ignored. Responds { deleted, cursor }.

  • ranges (array, 1-10 items, optional): grouped { offset_chars, limit_chars } windows read in one call. With cursor: each range is read server-side in parallel and the response is a grouped object (range_applied, range_count, ranges[], range_hint). With url: the document is fetched first; if it arrives truncated with a cursor the ranges read that capture in parallel, otherwise they are sliced locally from the returned content.

  • offset_chars (integer, optional, default: 0; aliases offsetChars and legacy offset): read offset in characters. With cursor: server-side window over the capture. With url (first read): local slice over the returned content, like EnriCode.

  • limit_chars (integer, optional, default: max_chars; aliases limitChars and legacy limit): read limit in characters. A value of 0 is ignored.

  • prompt (string, optional): extraction hint (what to focus on).

  • max_chars (integer, optional, default: ENRIWEB_WEB_FETCH_DEFAULT_MAX_CHARS; alias maxChars): maximum content length.

  • format (string, optional): content flavor for HTML pages — "text" (default, lightweight structured text), "markdown" (full markdown with links, emphasis, code fences, images, and tables), or "html" (sanitized markup for DOM inspection — scripts/styles stripped, tags intact). Use markdown only when the exact page structure matters; text is cheaper for factual lookups.

  • content (string, optional): HTML scope — "main" (default, article/main container only; drops nav, sidebars, cookie banners, and footers, typically saving 60-80% of tokens) or "full" (whole page).

  • include_links (boolean, optional, default: true; alias includeLinks): append the ENLACES DE LA PÁGINA inventory with every unique link (label + URL, up to 200) — useful for informed crawling or handing image URLs to URL-capable media analysis tools. Send false to omit it.

  • include_metadata (boolean, optional, default false; alias includeMetadata): append the METADATOS DE LA PÁGINA block with language, author, published date, and og:image.

  • anchor (string, optional): section selector — element id (with or without #) or exact heading text; returns only that section up to the next same-or-higher heading. When the section is missing, the response says so and returns the full document.

  • screenshot ("auto" | "force" | "none" | "analyze", optional, per-call): page capture request. "auto" captures when the page looks visual AND additionally attaches the server-side TEXT description per captured segment (screenshot_analyses), so the visual material is consumable with or without vision input; "force" always captures images without the text follow-up; "none" disables capture for this call; "analyze" returns only the TEXT description per segment instead of image blocks. Explicit per-call values always win over the installation-level ENRIWEB_SCREENSHOT_MODE default.

Outputs (structuredContent):

  • Single read: content, status, content_type, truncated, url, and pagination fields when present (cursor, offset_chars, limit_chars, total_chars, has_more, next_offset_chars, reduced, fetched_truncated, applied_max_chars on the npm path).

  • Delete: deleted (whether the cursor existed and was released) plus the addressed cursor.

  • Grouped ranges: range_applied: true, range_count, ranges[] (per-range index, offsets, content, truncated, Spanish error/note rows), range_hint, and the backing cursor/total_chars when a capture exists.

Notes:

  • If the response is truncated and includes a cursor, page through the captured content by calling web_fetch again with cursor + offset_chars + limit_chars (or a ranges batch for non-contiguous windows) — no re-download needed. Always echo the url on cursor calls: an expired cursor then recovers automatically (recovered_from_expired_cursor: true, fresh offsets, new cursor) instead of failing with HTTP 400.

  • Exhausted captures are reclaimed automatically: when a read reports has_more: false, EnriWeb releases the server-side cursor best-effort, omits it from the result, and tells you the capture was fully read (a 10-minute TTL backstops anything left behind).

  • npm package pages (npmjs.com/package/<name>, including /v/<version> pins and scoped packages) get a structured projection: registry metadata for the requested version plus the repository README, with pagination fields propagated when the README sub-fetch is truncated.

  • EnriProxy-side URL controls travel glued to the URL and are documented in the tool description: ?enri_find=TEXT (find text with offsets), ?enri_parts= (select page sections), ?enri_body_offset=N&enri_body_limit=M (body window), ?enri_section= for YouTube (manifest/transcript/comments/description), and Drive/OneDrive folder listings.

Example arguments object:

{
  "url": "https://example.com/docs",
  "max_chars": 200000,
  "ranges": [
    { "offset_chars": 0, "limit_chars": 5000 },
    { "offset_chars": 120000, "limit_chars": 5000 }
  ]
}

Available Tools

2 tools
web_fetchLectura de URL EnriProxyA
Read-onlyIdempotent

Obtiene y lee el contenido de una URL mediante el servicio multi-nivel de EnriProxy.

Cuándo usarla:

  • Cuando necesite leer el contenido completo de una página web.

  • Cuando necesite acceder a documentación, artículos o archivos de código.

  • Cuando métodos de fetch más simples fallen por protección anti-bot.

Características:

  • Detección de APIs de registros de paquetes (npm, PyPI)

  • Fetch de archivos raw (GitHub raw, HuggingFace)

  • Fetch robusto para sitios estáticos, dinámicos y protegidos (best-effort)

  • Respaldo automático entre múltiples estrategias de recuperación (detalles omitidos intencionalmente)

  • Proyección controlable: format ('text' ligero por defecto, 'markdown' estructura completa, 'html' DOM saneado), content ('main' por defecto elimina navegación/banners y conserva el artículo; use 'full' para todo), anchor (lee sólo una sección por id o título de encabezado), include_links (inventario de enlaces de la página, INACTIVO por defecto para ahorrar tokens; envíe true si necesita el inventario completo) e include_metadata (idioma/autor/fecha/imagen destacada)

  • Render de páginas con JavaScript: cuando la página devuelve un cascarón sin contenido renderizado, el servidor reintenta automáticamente con tiers que sí renderizan antes de responder

  • Sitios con JavaScript pesado (Steam, Reddit, X, Instagram, tiendas) se renderizan con navegador real: entregan texto, reseñas, comentarios, imágenes y archivos descargables completos, organizados en secciones (DATOS, MEDIOS, ENLACES, ARCHIVOS PARA DESCARGAR, COMENTARIOS)

  • Controles enri_* (sufijos que se agregan a la URL): ?enri_find=TEXTO busca dentro de toda la captura y devuelve las líneas con offsets (ÚSELO PRIMERO en páginas grandes); ?enri_parts= elige partes: sections,post,ld,imagenes,variantes,media,links,drive,nota,archivos,body (ej: ?enri_parts=links solo enlaces, omita body para respuestas pequeñas); ?enri_body_offset=N&enri_body_limit=M ventana del cuerpo en caracteres

  • YouTube: ?enri_section= manifest (por defecto: inventario con instrucciones) | transcripcion | comentarios | descripcion | todo, con enri_transcript_offset/enri_transcript_limit (caracteres) y enri_comments_offset/enri_comments_limit (cantidad). Cada corte trae su URL de continuación ya construida

  • Carpetas de Google Drive/OneDrive: inventario de archivos con URL de descarga directa por elemento

  • PDFs: cualquier URL de PDF (incluso bitstreams de repositorios tras muros anti-bot) se devuelve como TEXTO EXTRAÍDO (hasta 40 páginas por pasada, con avisos de truncado); los PDFs ESCANEADOS sin capa de texto se transcriben renderizando sus páginas con visión del lado del servidor en la misma respuesta; cuando la transcripción no es posible, la respuesta lo declara y sugiere pasar la misma URL a la herramienta de análisis de media para el análisis completo (multipass: páginas, tablas, diagramas)

  • Documentos de Office: URLs o descargas de Word (.docx), Excel (.xlsx) y PowerPoint (.pptx) — incluso tras Content-Disposition o tipos genéricos application/octet-stream — se extraen a TEXTO PLANO en la misma respuesta (párrafos, textos compartidos y valores de celdas, diapositivas en orden); los archivos de texto plano (txt, csv) adjuntos se decodifican directo; un zip sin documento de Office reconocible se declara honestamente

  • Decodificación de páginas con encoding legado (windows-1252/ISO-8859-1) sin mojibake

Notas:

  • Proporcione la URL completa incluyendo protocolo (https://).

  • El contenido se limita con el parámetro max_chars (por defecto: 200000).

  • Si el resultado viene truncado e incluye un cursor, vuelva a llamar web_fetch con cursor + offset_chars + limit_chars para leer más sin volver a descargar.

  • Envíe url junto con cursor siempre que la conozca: si el cursor expiró en el servidor (TTL ~10 minutos), la herramienta re-obtiene la url con los mismos parámetros y devuelve contenido fresco con cursor nuevo (campo recovered_from_expired_cursor) en vez de un error; sin url el cursor expirado sigue devolviendo error.

  • Los controles enri_* van pegados a la URL: web_fetch(url="https://ejemplo.com/pagina?enri_find=precio") — no son parámetros aparte de la herramienta.

Inputs: url, limit, action, anchor, +12 more. Returns: Contenido obtenido con metadatos de paginación; variantes: lectura única, borra…

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.3/5.0

    Scored across 2 tools

    Disambiguation5/5

    web_search and web_fetch have clearly distinct purposes: discovery of information versus retrieval of a specific URL. Although web_search can include fetched page contents, the descriptions make the boundary clear and guide the agent appropriately.

    Naming Consistency5/5

    Both tools use a consistent snake_case verb_noun pattern with the same web_ prefix. There is no mixing of naming conventions.

    Tool Count4/5

    Two tools is slightly below the typical 3–15 range, but for a focused web search/fetch server this is reasonable and each tool has a clear role. No unnecessary tools are present.

    Completeness4/5

    The search and fetch pair covers the core web-access workflow, including pagination, raw files, PDFs, and Office documents. However, the web_fetch description references a media-analysis tool that is not included, creating a minor dead end for scanned PDFs and complex media.

    Maintenance

    ActivityActive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that provides AI assistants with web search and intelligence capabilities via the ihyee API. It allows users to search the web, fetch extracted content from URLs, and perform full browser rendering for JavaScript-heavy websites.
      3
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that fetches web pages and extracts clean, AI-usable context from them, enabling tools for link discovery, content search, and integrated fetch-and-search operations.
      5
      17 npm
      1
      MIT
    • A
      license
      Not graded
      quality
      C
      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