WebVector MCP Server
WebVector
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
Requisitos: Node.js ≥ 22.12 (se recomienda Node 24). macOS, Linux y Windows.
Related MCP server: MyWebSearch MCP
1. Pruébalo en 30 segundos
Sin instalación, sin claves:
npx -y webvector-cli search "how does reciprocal rank fusion work" --statsVerá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 doctor2. Ú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-mcpClaude 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 3333 → http://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 webvectorimport { 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 server5. 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 ( | ~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 |
Semantic ( | + | 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: code → config file → environment variables → defaults. 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: warnEnvironment 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 |
| — |
| — |
| — |
|
|
| — |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| rerank | — |
|
|
|
| rerank |
|
|
|
|
| rerank |
|
|
|
|
| rerank |
|
| — | any Vercel AI SDK model | — | rerank | — |
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 intoresult.failures[]andsources[].failure. If every page fails you still get the search snippets (degraded: 'search_only',ALL_FETCHES_FAILED).Errors are thrown as
WebVectorErrorwithcode,message,remediation,retryable,provider,stage, andtoJSON(); 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(withretryAfterMs),EMBEDDING_DIMENSION_MISMATCH(name both models; suggestsstore.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 respetaRetry-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 configy en la herramienta MCPwebvector_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, validaHost/Origin(protección contra el reenlace DNS), admite un token de portador y se niega a vincularse en otro lugar sin--allow-remotey 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 honestoWebVector/….
¿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 7Estructura 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.ts — SearchProvider, 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-indexwebvector/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, MarkdownEjecució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
This server cannot be deployed
Maintenance
Related MCP Connectors
Scrape, crawl and search the web for AI agents via MCP.
Live AI-native web search with citations. One tool for every MCP client. Flat per-request pricing.
Web search, fetch, extract, and research for AI agents. Markdown output + AI-synthesized answers.
Web search, scraping, RAG answers with citations, and translation as MCP tools.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to perform grounded web research with injection resistance, claim verification, and cost-aware routing through MCP tools like web_search, fetch_url, extract_claims, and check_grounding.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to perform unified web research through a single MCP server, including search, page fetching, recursive crawling, document parsing, YouTube transcript extraction, and deep multi-query research.3-
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to perform live web searches across 9 engines, scrape web pages into clean formats, and run agentic research with citations via MCP.98 PyPI3MIT
- AlicenseBqualityBmaintenanceEnables AI agents to perform web searches and extract full-text content from web pages via standard MCP tools, with fallback search and semantic reranking.22MIT