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.
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 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
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
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/rthomas24/web-vector'
If you have feedback or need assistance with the MCP directory API, please join our Discord server