Skip to main content
Glama
rthomas24

WebVector MCP Server

by rthomas24

WebVector

npm: webvector npm: webvector-mcp npm: webvector-cli CI License: MIT

Haz que tu agente de IA haga investigación web real en una sola llamada a la herramienta: búsqueda → leer las páginas completas → clasificación → fragmentos citados.

Las herramientas de búsqueda entregan a un modelo títulos y fragmentos de 150 caracteres, por lo que el modelo adivina el resto. Las herramientas de fetch le entregan 40 KB de navegación y código repetido, con lo que se ahoga. WebVector hace todo el trabajo intermedio: ejecuta la búsqueda, descarga y limpia cada resultado (HTML, PDF, Markdown), lo divide en fragmentos, clasifica esos fragmentos según la pregunta —semánticamente cuando hay un modelo de embeddings disponible, y léxicamente (BM25) cuando no— y devuelve solo los fragmentos que responden a la pregunta, cada uno con su URL, título, autoescódigo y puntuación.

  • Funciona sin claves de API y sin descargar modelos: DuckDuckGo + BM25, instalación de ~12 MB.

  • Conecta cualquier backend de búsqueda, proveedor de embeddings, almacén de vectores o re-reranker (o escribe un adaptador propio en un solo archivo).

  • Se distribuye como biblioteca, server MCP (Claude Code, Claude Desktop, Cursor, Windsurf, …) y CLI.

  • Político y seguro por defecto: robots.txt, límites de velocidad por host, protección contra SSRF, límites de tamaño/redirección/tiempo, sin telemetría.

npx -y webvector-cli search "what changed in the MCP spec in 2026?"
# Web research: what changed in the MCP spec in 2026?

**[1]** Streamable HTTP — Model Context Protocol — <https://modelcontextprotocol.io/specification/2026-07-28/…> (score 1.00)
> ### Earlier Streamable HTTP Revisions
> Protocol versions 2025-03-26 through 2025-11-25 also used the Streamable HTTP transport, but in a
> different shape: servers could assign a session via the Mcp-Session-Id header … None of these
> mechanisms are part of this revision.
…
## Sources
- Streamable HTTP — Model Context Protocol — <https://…> [1]

Índice de contenido

  1. Pruébalo en 30 segundos

  2. Úsalo como servidor MCP (Claude Code, Claude Desktop, Cursor…)

  3. Úsalo como biblioteca

  4. Úsalo desde la línea de comandos

  5. Los dos niveles: léxico vs semántico

  6. Configuración

  7. Proveedores

  8. Lo que obtienes

  9. Errores y fallos

  10. Seguridad y etiqueta

  11. Ejecútalo desde el código fuente (desarrollo local)

  12. Escribir tu propio adaptador

  13. Cómo funciona

  14. Roadmap · Licencia

Requisitos: Node.js ≥ 22.12 (se recomienda Node 24). macOS, Linux y Windows.


1. Pruébalo en 30 segundos

Sin instalación, sin claves:

npx -y webvector-cli search "how does reciprocal rank fusion work" --stats

Verás los fragmentos y, a continuación, una línea de estadísticas como search duckduckgo 957ms · pages 4/5 908ms · embed 0 chunks (none/bm25) · retrieve 10ms · total 1879ms. embed … none/bm25 significa que estás en el nivel léxico (verat §5) — el nivel semántico se activa automáticamente cuando hay un runtime de modelo o una clave de API de embeddings.

Comprueba lo que usará tu máquina:

npx -y webvector-cli doctor

2. Úsalo como servidor MCP

El servidor MCP expone cuatro herramientas — web_research (el principal), web_fetch, web_search, webvector_status — para cualquier cliente MCP.

Claude Code

claude mcp add webvector -- npx -y webvector-mcp

Claude Desktop / Cursor / Windsurf / VS Code — añade a tu configuración MCP (claude_desktop_config.json, ~/.cursor/mcp.json, …):

{
  "mcpServers": {
    "webvector": {
      "command": "npx",
      "args": ["-y", "webvector-mcp"],
      "env": { "BRAVE_API_KEY": "optional — see §7" }
    }
  }
}

Esa es la capa léxica. Para la búsqueda semántica en el dispositivo, instala el runtime del modelo junto a él:

"args": ["-y", "-p", "@huggingface/transformers", "-p", "webvector-mcp", "webvector-mcp"]

…o simplemente pon una clave de embeddings en env (OPENAI_API_KEY, VOYAGE_API_KEY, GEMINI_API_KEY, COHERE_API_KEY) y se actualizará automáticamente.

A través de HTTP (para frameworks de agentes): npx -y webvector-mcp --http --port 3333http://127.0.0.1:3333/mcp (HTTP streamable, solo local host). Añade --token <secreto> (o WEBVECTOR_MCP_TOKEN) para exigir Authorization: Bearer <secreto>; la vinculación a cualquier otra dirección requiere --host 0.0.0.0 --allow-remote --token … y debe estar detrás de TLS/usuario propio. GET /health para comprobar disponibilidad.

Cada resultado de web_research llega tanto en Markdown compacto (para el modelo) como en structuredContent (para tu app), con notificaciones de progreso durante el proceso.

3. Using it as a library

npm i webvector
import { WebVector } from 'webvector';

const wv = new WebVector();                                        // zero-config
const res = await wv.research('what is reciprocal rank fusion');

console.log(res.markdown);                                         // ready to drop into a prompt
for (const p of res.passages) console.log(p.score, p.citation);    // "[1] Title — https://…"
await wv.close();

Configuración pasando opciones (ver sección 6 para la lista completa):

const wv = new WebVector({
  search: { provider: 'brave' },                                   // reads BRAVE_API_KEY
  embeddings: { provider: 'openai', model: 'text-embedding-3-small' },
  retrieval: { topK: 8, rerank: 'cohere' },
  store: { mode: 'session' },                                      // reuse pages across calls
});

const res = await wv.research('How does Node 24 handle AbortSignal.any?', {
  relatedQueries: ['AbortSignal.any example'],  // extra angles (also searched)
  freshness: 'year',                            // day | week | month | year
  domainsAllow: ['nodejs.org', 'developer.mozilla.org'],
  sessionId: 'conversation-42',                 // pages already read this session are reused
  onProgress: (p) => console.error(p.stage, p.message),
});

Otras llamadas: wv.search(query) (solo resultados), wv.fetch(url) (una página → Markdown), wv.fetchAndRetrieve(url, query) (una página → fragmentos relevantes), wv.listSessions(), wv.clearSession(id).

Dáselo a un modelado como herramienta — los bindings para los SDK más populares están a una simple importación:

// Vercel AI SDK
import { generateText, isStepCount } from 'ai';
import { webVectorTools } from 'webvector/ai-sdk';
await generateText({ model, tools: await webVectorTools(wv), stopWhen: isStepCount(5), prompt });

// Anthropic Messages API           // OpenAI Responses API            // LangChain.js
import { anthropicTools, runAnthropicTool } from 'webvector/anthropic';
import { openaiTools, runOpenAITool } from 'webvector/openai';
import { langchainTools } from 'webvector/langchain';

// Anything else: plain JSON Schema
import { webResearchToolDefinition } from 'webvector';

Cada una con su versión ejecutable en examples/.

4. Using it from the command line

npm i -g webvector-cli          # or keep using npx -y webvector-cli …

webvector search "query" [-k 8] [-p 12] [--provider brave] [--embeddings openai] [--rerank local] [--json|--md] [--stats]
webvector fetch <url> [--query "…"]     # one page as Markdown, or just the passages relevant to --query
webvector serp "query"                  # search results only
webvector doctor [--live]               # config, dependencies, provider connectivity, active tier
webvector init                          # writes webvector.config.yaml + .env.example
webvector config                        # print resolved config (secrets redacted)
webvector providers                     # every provider and the env var it reads
webvector mcp [--http]                  # run the MCP server

5. Los dos niveles: léxico vs semántico

Un solo ajuste — embeddings.provider, por defecto auto — decide cómo se clasifican los pasajes:

Tier

Install

Ranking

Chosen when

Lexical (none)

~12 MB, no downloads

BM25 absolute over the full scanned pages + query expansion + per-izer diversity

No need empty model runtime and no embedding key present (the live npx path)

Semantic (local, openai, …)

+ @huggingface/transformers (→ 230 MB, model 23 MB, fully offline) o any embedding API key

Hybrid: vectors + BM25 fused with RRF, MMR diversity, optional re-ranker

Automatically, as soon as either is available

Upgrade at any time: npm i @huggingface/transformers next to the package, or set a key. webvector doctor shows which tier is active. The lexical mode is a supported mode, not a fallback — results are marked stats.embed.provider: 'none' and are not a fallback.

6. Configuration

Precedence: codeconfig fileenvironment variablesdefaults. Config file locations: webvector.config.{ts,js,mjs,json,yaml,yml}, .webvectorrc, o una clave webvector in package.json — located by looking up the working directory. ${VAR} / ${VAR:-default} inside values are filled from the environment:

webvector init writes a commented starter; here are the important settings people usually change:

search:
  provider: duckduckgo          # duckduckgo | brave | serper | serpapi | google-cse | searxng | tavily | tavily-keyless | exa | perplexity | wikipedia
  fallbackProviders: [tavily-keyless, wikipedia]
  resultsPerQuery: 10
embeddings:
  provider: auto                # auto | none | local | openai | openai-compatible | gemini | voyage | cohere | mistral | jina | ollama
  model: Xenova/all-MiniLM-L6-v2   # local aliases: minilm (fast) | granite (quality) | embeddinggemma (best) | bge-small | nomic …
store:
  provider: memory              # memory | chroma | qdrant | pgvector
  mode: ephemeral               # ephemeral (per call) | session (reuse by sessionId, TTL) | persistent (external store)
retrieval:
  topK: 12
  hybrid: true                  # BM25 + vectors fused with RRF (semantic tier)
  queryExpansion: true          # heuristic (no LLM); pass retrieval.llm in code for LLM multi-query
  maxPerSource: 3
  mmr: true
  rerank: false                 # local | cohere | voyage | jina | llm
ingestion:
  maxPages: 10
  maxConcurrentFetches: 8
  timeoutMs: 15000
  totalDeadlineMs: 45000
  respectRobotsTxt: true
  chunkSize: 480                # tokens
output:
  markdown: true
  maxPassageChars: 1500
logging:
  level: warn

Environment equivalents: WEBVECTOR_SEARCH_PROVIDER, WEBVECTOR_EMBEDDINGS_PROVIDER, WEBVECTOR_EMBEDDINGS_MODEL, WEBVECTOR_STORE_PROVIDER, WEBVECTOR_STORE_MODE, WEBVECTOR_LOCALITY_WEIGHT, WEBVECTOR_TOP_K, WEBVECTOR_MAX_PAGES, WEBVECTOR_LOG_LEVEL, WEBVECTOR_MODEL_CACHE, plus the provider keys below. Every option with its default at docs/CONFIGURATION.md.

7. Providers

Set the env var, specify the provider, and you’re done. Details and gotchas per provider: docs/PROVIDERS.md.

Search

env

Embeddings

env

Stores / Rerankers

env

duckduckgo (por defecto)

none (BM25)

memory (default)

brave

BRAVE_API_KEY

local (Transformers.js)

chroma

CHROMA_URL

serper (Google)

SERPER_API_KEY

openai

OPENAI_API_KEY

qdrant

QDRANT_URL

serpapi

SERPAPI_API_KEY

openai-compatible (LM Studio, vLLM, …)

OPENAI_COMPATIBLE_BASE_URL

pgvector

DATABASE_URL

tavily / tavily-keyless

TAVILY_API_KEY

gemini

GEMINI_API_KEY

rerank local

exa

EXA_API_KEY

voyage

VOYAGE_API_KEY

rerank cohere

COHERE_API_KEY

perplexity

PERPLEXITY_API_KEY

cohere

COHERE_API_KEY

rerank voyage

VOYAGE_API_KEY

searxng (self-hosted)

SEARXNG_URL

mistral / jina / ollama

MISTRAL_API_KEY / JINA_API_KEY / OLLAMA_HOST

rerank jina

JINA_API_KEY

wikipedia

any Vercel AI SDK model

rerank llm (your function)

If the primary search provider fails or is rate-limited, the fallbackProviders chain is tried automatically and every attempt is recorded in stats.search.attempts.

8. What you get back

interface ResearchResult {
  query: string; queries: string[];        // the query + expansions actually used
  passages: Passage[];                     // ranked; each: text, url, title, score (0–1), cosine?, bm25?,
                                           //   rerankScore?, chunkIndex, startOffset, endOffset, publishedAt?,
                                           //   fetchedAt, matchedQueries, citation "[n] Title — url"
  sources: SourceSummary[];                // one per page: status ok|failed|cached, chunks, bestScore, passageIndices, failure?
  failures: Failure[];                     // per-URL / per-stage problems with machine codes (never thrown)
  stats: { search, ingest, embed, retrieve, totalMs, warnings };   // timings + counts per stage
  markdown?: string;                       // the pre-rendered version above
  degraded?: 'search_only' | 'partial';    // e.g. every fetch failed → search snippets returned instead
}

9. Errors and failures

Two categories, deliberately kept separate:

  • Failures are page-level, and never abort an execution: FETCH_TIMEOUT, FETCH_HTTP_ERROR, FETCH_BLOCKED_ROBOTS, FETCH_BLOCKED_SSRF, FETCH_TOO_LARGE, TOO_MANY_REDIRECTS, UNSUPPORTED_CONTENT_TYPE, PARSE_EMPTY, PARSE_FAILED. They go into result.failures[] and sources[].failure. If every page fails you still get the search snippets (degraded: 'search_only', ALL_FETCHES_FAILED).

  • Errors are thrown as WebVectorError with code, message, remediation, retryable, provider, stage, and toJSON(); sensitive parts are redacted. Examples: MISSING_API_KEY (“Show us”), MISSING_DEPENDENCY (npm i @huggingface/transformers — or embeddings.provider: 'none'), SEARCH_BLOCKED, PROVIDER_RATE_LIMITED (with retryAfterMs), EMBEDDING_DIMENSION_MISMATCH (name both models; suggests store.clear() or a new vehicle), INVALID_CONFIG.

10. Security and Good-Neighbor

WebVector fetches URLs chosen by a search engine — i.e. content an attacker can influence — so the fetcher is defensive by default:

  • Protección SSRF: se rechazan los destinos privados, de bucle local (loopback), enlace local (link-local), CGNAT, multidifusión, reservados, IPv4-mapeado-IPv6 y localhost/*.internal; se comprueban las respuestas DNS y se vuelve a verificar cada salto de redirección. Solo se puede desactivar para entornos locales de confianza (ingestion.allowPrivateNetworks).

  • Límites en redirecciones (5), tamaño de la respuesta (5 MB), tiempo por petición (15 s) y plazo total de ejecución (45 s); concurrencia acotada globalmente y por host.

  • Etiqueta: se respeta robots.txt (incluido Crawl-delay), User-Agent identificable, intervalo mínimo por host, se respeta Retry-After.

  • Análisis sin ejecución: el HTML se analiza con linkedom (sin scripts, sin carga de subrecursos), los PDF con pdf.js en modo sin evaluación (no-eval); quienes lo invocan solo reciben Markdown/texto plano con los caracteres de control eliminados.

  • Secretos: se leen de variables de entorno/configuración, nunca se registran; se enmascaran en los errores, en webvector config y en la herramienta MCP webvector_status. No se escribe nada en el disco a menos que se active el directorio de caché de páginas.

  • Sin telemetría, nunca.

  • MCP sobre HTTP se vincula solo a 127.0.0.1, valida Host/Origin (protección contra el reenlace DNS), admite un token de portador y se niega a vincularse en otro lugar sin --allow-remote y un token.

  • El reenlace DNS se cierra en el momento de la conexión: la comprobación SSRF se ejecuta dentro de la búsqueda DNS utilizada para abrir el socket, por lo que la dirección comprobada es la dirección a la que se conecta.

  • Nota sobre DuckDuckGo: el proveedor sin clave se comunica con los endpoints HTML públicos de DuckDuckGo con un User-Agent similar al de un navegador (no hay API oficial). Está limitado por tasa y es frágil por naturaleza; el uso intensivo o comercial debería cambiar a un proveedor con clave (brave, serper, tavily). Las descargas de páginas siempre usan el User-Agent honesto WebVector/….

¿Has encontrado algo? Abre un aviso de seguridad privado en GitHub en lugar de una incidencia pública.

11. Ejecutarlo desde el código fuente (desarrollo local)

git clone https://github.com/rthomas24/web-vector
cd webvector
npm install                     # installs all workspaces (~1 min; includes the optional model runtime for tests)
npm run build                   # tsdown → packages/*/dist

# use the local build
node packages/cli/dist/cli.js search "reciprocal rank fusion" --stats
node packages/mcp/dist/bin.js                     # MCP server on stdio
node packages/mcp/dist/bin.js --http --port 3333  # …or HTTP

# point an MCP client at the local build
claude mcp add webvector-dev -- node /absolute/path/to/webvector/packages/mcp/dist/bin.js

# quality gates
npm test                        # unit tests, offline (mocked HTTP), ~5 s
npm run test:live               # real network + local model + MCP stdio round-trip (~20 s)
npm run lint                    # biome
npm run typecheck               # TypeScript 7

Estructura del repositorio y dónde añadir cosas: docs/ARCHITECTURE.md.

Para usar la compilación local desde otro proyecto sin publicar: npm pack en packages/core (y mcp/cli) y npm i ./webvector-0.1.0.tgz allí, o npm link.

12. Escribir tu propio adaptador

Cada tipo de proveedor es una interfaz pequeña en packages/core/src/types.tsSearchProvider, EmbeddingProvider, VectorStore, ContentParser, Reranker. Impléméntala y luego pasa una instancia en la configuración o registra un nombre para que los archivos de configuración puedan usarla:

import { customSearchProvider, registerSearchProvider, WebVector } from 'webvector';

const myIndex = customSearchProvider('my-index', async (query) => [
  { url: 'https://…', title: '…', snippet: '…' },
]);
new WebVector({ search: { instance: myIndex } });
// or: registerSearchProvider('my-index', (opts) => new MyProvider(opts));  → search.provider: my-index

webvector/testing exporta comprobaciones de conformidad (searchProviderConformance, embeddingProviderConformance, vectorStoreConformance) que puedes incorporar a cualquier ejecutor de pruebas.

13. Cómo funciona

research(query)
  1. search      provider chain (DuckDuckGo → fallbacks) → dedupe by canonical URL → domain filters → top N
  2. ingest      concurrent, polite fetch → HTML (Readability→Markdown) | PDF | text → page cache
  3. chunk+embed markdown-aware recursive chunks with heading breadcrumbs → content-hash dedupe → embed (batched, cached)
  4. retrieve    query + expansions → vector top-k lists + BM25 top-k lists → weighted RRF → cosine cutoffs
                 → near-duplicate removal → per-source cap → MMR → optional rerank → top-k
  5. format      passages with citations, sources, failures, per-stage stats, Markdown

Ejecución típica en un portátil: búsqueda ~1 s, 8 páginas descargadas + analizadas ~1–2,5 s, recuperación < 50 ms → ~2 s de búsqueda léxica / ~4 s de búsqueda semántica.

14. Hoja de ruta

Almacenes LanceDB y Pinecone · un adaptador de descarga con navegador sin interfaz (headless) para páginas renderizadas con JS · recuperación contextual (contexto de fragmentos resumido por LLM) como opción de activación voluntaria · un binario independiente sin requisito de Node · paquete de Python que comparte los fixtures de conformidad.

Licencia

MIT © Ryan Thomas

-
license - not tested
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (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

  • Web research for agents: quality-scored Google search, webpage extraction, and deep research.

  • LLM-ready web search + instant answers + URL-to-clean-text fetch for agents and RAG.

  • The best web search for your AI Agent

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/rthomas24/web-vector'

If you have feedback or need assistance with the MCP directory API, please join our Discord server