internet-context-mcp
Servidor MCP de solo lectura que proporciona a los agentes de IA la web como evidencia compacta, clasificada y verificada. Reranker de codificador cruzado local, verificación de afirmaciones NLI, detección de acuerdo y contradicción semántica entre fuentes. Sin claves API.
Documentación
internet-context-mcp
Un servidor MCP de solo lectura que les da a los agentes de IA la web como evidencia compacta, clasificada y verificada — sin claves API, sin recuperación en la nube, todos los modelos locales.
Seis herramientas de solo lectura — web_research, web_context, web_search, web_read, web_verify, web_extract — además de recursos MCP, prompts y metadatos outputSchema / readOnlyHint adecuados. Detrás de cada herramienta: ranking local BM25, un reranker local de codificador cruzado, un clasificador NLI local, embeddings de oraciones locales, un escáner de inyección de prompts basado en regex y una caché de recuperación de dos niveles (en memoria + SQLite).
Medido, no aspiracional: 20/20 en la evaluación de relevancia, 92.5% de recall de inyección de prompts con 0% de tasa de falsos positivos en páginas benignas, detector de contradicciones con 0 falsos positivos en la web real (ver tabla de evaluación más abajo).
Inicio rápido
Añade esto a tu claude_desktop_config.json (o configuración equivalente del host MCP):
{
"mcpServers": {
"internet-context": {
"command": "npx",
"args": ["-y", "internet-context-mcp"]
}
}
}
Reinicia el host. Eso es todo.
La primera llamada descarga de forma diferida tres modelos locales desde HuggingFace (~125 MB en total, en caché): el reranker de codificador cruzado, el clasificador NLI y el modelo de embeddings de oraciones. Una vez en caché, el servidor funciona completamente sin conexión. No se requieren claves API en ningún momento.
Variables de entorno opcionales
{
"env": {
"BRAVE_SEARCH_API_KEY": "", // optional: use Brave instead of the DDG fallback
"INTERNET_CONTEXT_MCP_RERANK": "0", // optional: disable the cross-encoder reranker
"INTERNET_CONTEXT_MCP_NLI": "0", // optional: disable NLI for web_verify
"INTERNET_CONTEXT_MCP_EMBEDDINGS": "0", // optional: disable semantic clustering / contradictions
"INTERNET_CONTEXT_MCP_CACHE_DIR": "" // optional: override SQLite cache path
}
}
Ejecutar localmente desde el código fuente (desarrolladores)
git clone https://github.com/vivekvar-dl/internet-context-mcp
cd internet-context-mcp
npm install
npm run build
node dist/index.js # exits when stdin closes — used by the MCP host
Qué hay en v0.4.x
web_research— búsqueda de un solo disparo + recuperación de múltiples fuentes + ranking entre fuentes + citas por fragmento + señal de acuerdo basada en redundancia + detección de contradicciones respaldada por NLI.web_verify— verificación de afirmaciones contra fuentes, respaldada por el clasificador NLI (implicación / neutral / contradicción), con respaldo de regex.web_context— recuperar + clasificar + devolver fragmentos de evidencia clasificados con cápsula de prioridad (TL;DR), señal de confianza de recuperación, extracción de datos estructurados, escaneo de inyección de prompts, procedencia de fuentes con rutas DOM.web_read— texto de página limpio y compacto con metadatos de ahorro de tokens.web_search— Brave cuandoBRAVE_SEARCH_API_KEYestá configurado; respaldo HTML de DuckDuckGo en caso contrario.web_extract— extracción basada en esquemas de mejor esfuerzo.
Además:
- Anotaciones
readOnlyHint: true+openWorldHint: trueen cada herramienta. Claude Desktop / Code puede omitir los avisos de permisos. outputSchemaen cada herramienta. Los hosts reciben JSON tipado (structuredContent) en lugar de volver a analizar texto de formato libre.- Plantilla de recurso MCP
internet-context://page/{fingerprint}— el host puede volver a referenciar páginas recuperadas por URI sin volver a llamar a una herramienta. - Plantillas de prompt MCP
verify_with_sources,summarize_from_context,research_a_topic. - Caché de recuperación de dos niveles: en memoria + una capa SQLite persistente en
~/.cache/internet-context-mcp/cache.sqlite. Sobrevive a los reinicios del host. - Tokenizador
js-tiktokenreal (cl100k_base). Se acabaron las aproximaciones conchars / 4. - Renderizado opcional con Playwright para SPAs con mucho JavaScript mediante
render: "browser", distribuido como unoptionalDependency.
Consulta CHANGELOG.md para el detalle por versión.
Qué contiene una cápsula de contexto
Una respuesta de web_context incluye:
- una cápsula de prioridad corta (TL;DR) antes de la evidencia larga
- fragmentos de evidencia clasificados con desplazamientos de caracteres, rutas de sección y rutas DOM
- una señal de confianza de recuperación para que el agente pueda pedir más fuentes cuando sea necesario
- datos estructurados de la página, cuando están presentes (JSON-LD, microdatos, metadatos)
- metadatos de la página y huellas de contenido
- advertencias de riesgo de inyección de prompts (visible / oculto / comentario / metadatos)
- estimaciones de ahorro de tokens
Herramientas
web_research
Herramienta de investigación de un solo disparo: busca en la web, recupera los N mejores resultados en paralelo, clasifica los fragmentos dentro de cada fuente (con el reranker local por defecto), luego clasifica globalmente entre fuentes y devuelve un paquete de evidencia unificado con citas de fuente por fragmento y una señal de acuerdo basada en redundancia.
Úsala cuando de otro modo estarías llamando a web_search y luego a web_context varias veces seguidas.
Entrada:
{
"query": "What is the Model Context Protocol and who built it?",
"depth": 4,
"max_tokens_total": 3000
}
Forma de salida (abreviada):
{
"query": "...",
"provider": "duckduckgo_html",
"depth": 4,
"unique_sources": 3,
"sources": [
{
"index": 0,
"requested_url": "https://en.wikipedia.org/wiki/Model_Context_Protocol",
"ok": true,
"title": "Model Context Protocol - Wikipedia",
"retrieval_confidence": { "level": "high", "score": 0.79 },
"selected_chunks": 1
}
],
"ranked_evidence": [
{
"source_index": 1,
"source_url": "https://www.anthropic.com/news/model-context-protocol",
"source_title": "Introducing the Model Context Protocol",
"chunk_id": 2,
"cluster_id": 2,
"agreement_count": 1,
"score": 1.0,
"combined_score": 1.0,
"section": null,
"matched_terms": ["model", "context", "protocol"],
"text": "Today, we're open-sourcing the Model Context Protocol..."
}
],
"agreement_score": 0.0,
"verdict_reasons": ["sources_did_not_overlap"],
"token_budget": { "max_tokens_total": 3000, "used_tokens": 1949 }
}
agreement_count y agreement_score usan similitud semántica (coseno all-MiniLM-L6-v2, ~22MB, cargado de forma diferida) por defecto en v0.4.0+. El acuerdo parafraseado ahora cuenta: tres fuentes que dicen de forma independiente el mismo hecho con palabras diferentes se agruparán juntas. La agrupación recurre al Jaccard de shingles de 4 gramos cuando el modelo de embeddings no se carga; el campo clustering_method en cada respuesta muestra cuál se ejecutó.
contradictions enumera los casos en los que fragmentos de diferentes fuentes tratan el mismo tema pero ninguno implica al otro en ninguna dirección. El detector funciona en dos etapas: un prefiltro de coseno de embeddings (≥0.45) que requiere que los dos fragmentos estén discutiendo la misma afirmación, y luego una verificación NLI de no-implicación bidireccional (≤0.05 de implicación en ambas direcciones) sobre los supervivientes. Ambas deben cumplirse.
Cada contradicción incluye topical_similarity (el coseno real) y confidence (la señal NLI).
El prefiltro permite intencionalmente que pasen pares del mismo grupo: en la naturaleza, dos fuentes que hacen afirmaciones opuestas sobre el mismo hecho se parafrasean entre sí con un coseno muy alto (~0.9), por lo que se agrupan juntas. Excluir pares del mismo grupo significaría perder las contradicciones que más queremos sacar a la luz.
Evaluación del detector — medido, no aspiracional
Esto es lo que hemos medido que el detector realmente hace. Los scripts de evaluación están en scripts/demo-contradiction-*.ts para que los números sean reproducibles.
| Conjunto de evaluación | Fuentes recuperadas | Contradicciones detectadas | Qué significa |
|---|---|---|---|
| Positivo sintético ("el café reduce el riesgo cardiovascular" vs "el café aumenta el riesgo cardiovascular") | n/a | 1 (conf 0.9999, topical 0.91) | El detector se activa con desacuerdos inequívocos hechos a mano |
| 5 consultas en vivo (huevos, ayuno intermitente, café/corazón, velocidad de la luz, capital de Francia) | 18 | 0 | Cero falsos positivos. v0.4.0 produjo 3 falsos positivos en este mismo conjunto; v0.4.1 los eliminó todos. |
| 3 pares de URL seleccionados precisamente porque las fuentes deberían discrepar (prevención primaria con aspirina, vitamina D, grasa saturada) | 5 de 8 — NEJM/BMJ devolvieron 403 a la recuperación estática | 0 | Las fuentes que se recuperaron dieron una cautela matizada; las fuentes primarias donde la disputa es directa estaban tras muro de pago. |
| 3 barridos de búsqueda con depth=8 sobre temas con divisiones conocidas entre popular y evidencia (estiramientos, desayuno, correr y rodillas) | 23 de 24 | 0 | Los motores de búsqueda devuelven el consenso actual; la disputa vive en otro lugar. |
En ~30 fuentes reales recuperadas, el detector se activó exactamente cero veces. También produjo cero falsos positivos.
La afirmación veraz sobre v0.4.x: el detector tiene una tasa de falsos positivos casi nula en la web real y se activa de forma fiable ante afirmaciones opuestas léxicamente explícitas. No detecta, en nuestras pruebas, disputas que son reales pero expresadas con prosa matizada o calificada — que es como la mayor parte de la web indexada habla del desacuerdo. Tres razones:
- Los motores de búsqueda (DDG, Google) devuelven contenido generalista homogeneizado; la disputa vive en artículos académicos o fuentes contrarias que no rankean bien.
- La prosa web generalista califica su desacuerdo ("algunos estudios sugieren", "para ciertas poblaciones", "investigaciones recientes han mostrado"). La no-implicación bidireccional del NLI no se activa con el contraste matizado.
- Muchas fuentes primarias donde la disputa sí es directa (NEJM, BMJ, ScienceDirect, Britannica) bloquean las recuperaciones estáticas con HTTP 403.
Si quieres que el detector capture el desacuerdo matizado, necesitarías aflojar el techo de implicación y aceptar algunos falsos positivos. Si quieres un acceso más amplio a las fuentes, necesitarías renderizado de navegador y (para revistas con muro de pago) credenciales. v0.4.x no hace ninguna de las dos cosas; se mantiene de solo lectura, local y honesto sobre lo que ve.
Advertencia honesta sobre la señal de acuerdo: cuando agreement_count=N entre N fuentes, eso significa que N fuentes de los resultados de búsqueda se corroboraron entre sí — no que la afirmación sea verdadera. Los motores de búsqueda tienden a devolver la visión generalista actual, que puede ocultar disputas genuinas (la consulta de huevos/colesterol devolvió 4 fuentes modernas que coincidían todas con el consenso moderno, aunque el tema estuvo en disputa durante décadas).
web_context
Recupera una URL, la limpia, la divide en fragmentos, clasifica los fragmentos contra una tarea del agente con un algoritmo local de estilo BM25 y devuelve solo el mejor presupuesto de evidencia. Esta es la principal herramienta de reducción de tokens.
Entrada:
{
"url": "https://example.com/docs",
"task": "find installation steps and configuration details",
"max_tokens": 1800
}
Forma de salida:
{
"task": "find installation steps and configuration details",
"title": "Documentation",
"context": "[chunk 2 | score 1]\\nInstall the package with npm install example...",
"evidence_chunks": [
{
"id": 2,
"score": 1,
"score_breakdown": {
"bm25": 2.4,
"phrase": 0,
"heading": 0.5,
"metadata": 0.35,
"structured_data": 0,
"position": 0
},
"provenance": {
"char_start": 182,
"char_end": 348,
"section": "Installation",
"section_path": ["Installation"],
"source_blocks": [
{
"block_id": 4,
"tag": "p",
"dom_path": "body:nth-of-type(1) > main:nth-of-type(1) > section:nth-of-type(1) > p:nth-of-type(1)",
"line_start": 22,
"line_end": 22,
"overlap_score": 1,
"text_preview": "Install the package with npm install example."
}
]
},
"matched_terms": ["install", "config"],
"text": "Install the package with npm install example..."
}
],
"structured_data": {
"metadata": {
"description": "..."
},
"json_ld": [],
"microdata": []
},
"safety": {
"risk": "low",
"score": 0,
"warnings": []
},
"priority_capsule": {
"tldr": "Install with npm install example. Configure via the MCP client config file.",
"top_sections": ["Installation", "Configuration"],
"highlight_chunk_ids": [2, 3]
},
"retrieval_confidence": {
"level": "high",
"score": 0.78,
"reasons": [],
"suggestion": null
},
"provenance": {
"content_fingerprint": "9f2a1c6e7b0d3a11",
"clean_text_fingerprint": "3d41e2f0780a5c19"
},
"ranking": {
"algorithm": "hybrid-bm25-lite",
"signals": ["bm25", "phrase", "heading", "metadata", "structured_data", "position"],
"total_chunks": 12,
"selected_chunks": 3,
"selected_tokens": 940
},
"token_savings_estimate": {
"raw_tokens": 42000,
"returned_tokens": 1100,
"saved_tokens": 40900,
"savings_ratio": 0.9738
}
}
web_read
Recupera una URL, elimina el ruido de la interfaz de la página, extrae el contenido principal y devuelve texto limpio más metadatos de ahorro de tokens.
Entrada:
{
"url": "https://example.com/docs",
"query": "installation configuration",
"mode": "compact",
"max_tokens": 4000
}
web_search
Busca en la web y devuelve resultados compactos clasificados por fuente.
Si BRAVE_SEARCH_API_KEY está configurado, usa Brave Search. De lo contrario, recurre a la búsqueda HTML de DuckDuckGo.
Entrada:
{
"query": "Model Context Protocol TypeScript SDK docs",
"limit": 5
}
web_verify
Comprueba si una afirmación está respaldada, refutada o no está clara a partir de una o más URL de fuentes. Recupera cada fuente, clasifica los fragmentos contra la afirmación y busca respaldo o contradicción explícitos (con detección simple de negación cerca de los términos coincidentes). Devuelve un veredicto combinado más los fragmentos de evidencia de respaldo y refutación por fuente.
Entrada:
{
"claim": "the server is read-only",
"sources": [
"https://example.com/docs",
"https://example.com/safety"
],
"max_tokens_per_source": 1400
}
Forma de salida:
{
"claim": "the server is read-only",
"verdict": "supported",
"confidence": 0.82,
"reasons": ["2_sources_support"],
"sources": [
{
"requested_url": "https://example.com/docs",
"final_url": "https://example.com/docs",
"title": "Documentation",
"verdict": "supported",
"confidence": 0.74,
"supporting_chunks": [
{
"chunk_id": 3,
"section": "Safety",
"score": 0.91,
"matched_terms": ["server", "read", "only"],
"contains_negation": false,
"text_preview": "The default tools are read-only and never submit forms or modify remote data."
}
],
"refuting_chunks": []
}
]
}
web_extract
Extracción genérica de campos de mejor esfuerzo a partir del texto limpio de la página. Esto es intencionalmente secundario a web_context; en muchos agentes, el flujo mejor es llamar a web_context y dejar que el modelo del host razone sobre los fragmentos de evidencia devueltos.
Entrada:
{
"url": "https://example.com/docs",
"schema": {
"title": "string",
"install_command": "string",
"configuration_file": "string"
},
"query": "installation command configuration file"
}
Instalación
npm install
npm run build
npm test
Prueba de estrés con sitios reales
El repositorio incluye un conjunto de estrés de 100 URL con datos reales en data/real-sites.json. Ejercita el pipeline completo contra páginas en vivo:
npm run stress:real
Opciones útiles:
npm run stress:real -- --limit=20 --concurrency=3 --timeout=15000 --maxTokens=1500
El script escribe un informe compacto en:
reports/stress-real-sites-latest.json
Mide el éxito de recuperación en vivo, el ahorro de tokens, los fragmentos seleccionados, la detección de datos estructurados, las advertencias de seguridad y la cobertura de procedencia de fuentes.
Evaluación de inyección de prompts
El repositorio incluye un conjunto adversarial de 54 casos en evals/prompt-injection.json que cubre anulación de instrucciones visible, texto oculto (display:none / visibility:hidden / opacity:0 / aria-hidden / fuera de pantalla), inyección en comentarios HTML, solicitudes de credenciales, prompts de exfiltración y páginas de control benignas.
npm run eval:injection
Números reportados para v0.4.0 (escáner regex, sin LLM):
{
"true_positive_rate": 0.925,
"false_positive_rate": 0,
"precision": 1,
"recall": 0.925,
"by_category": {
"instruction_override_visible": 0.80,
"hidden_text": 0.90,
"html_comment": 1.00,
"credential_request": 1.00,
"exfiltration": 1.00,
"benign_control": 1.00
}
}
Fallos conocidos: "ignora las instrucciones anteriores" (artículo interpuesto), marco de "ya no es válido", posicionamiento fuera de pantalla mediante position:absolute;left:-9999px. Fallos reales, expuestos intencionalmente en lugar de encubiertos.
Evaluación de relevancia
El repositorio incluye un conjunto de relevancia etiquetado en evals/relevance.json. Comprueba si las cápsulas comprimidas preservan los hechos requeridos, evitan términos basura, se mantienen dentro del presupuesto de tokens de evidencia e incluyen procedencia de fuentes.
npm run eval:relevance
La última ejecución de 20 casos en v0.3.0 (reranker activado por defecto, tokenizador real) pasó todos los casos:
{
"all_pass_rate": 1,
"included_pass_rate": 1,
"excluded_pass_rate": 1,
"provenance_pass_rate": 1,
"token_budget_pass_rate": 1,
"average_token_savings_ratio": 0.9192
}
El informe se escribe en:
reports/eval-relevance-latest.json
Ejecución
npm run dev
Para uso compilado:
npm run build
node dist/index.js
Configuración del cliente MCP
Para clientes que aceptan configuración JSON del servidor MCP:
{
"mcpServers": {
"internet-context": {
"command": "node",
"args": ["C:/Users/domai/internet-context-mcp/dist/index.js"],
"env": {
"BRAVE_SEARCH_API_KEY": ""
}
}
}
}
Restricciones de diseño
- Solo lectura primero: sin clics, inicios de sesión, compras, envíos de formularios ni acciones que cambien el estado.
- Salida compacta primero: los agentes deberían recibir contexto útil, no volcados de página.
- Ranking local primero: reducir tokens sin requerir una segunda clave API de LLM.
- Evidencia primero: el contexto devuelto debería incluir el texto utilizado para respaldar las afirmaciones.
- Contenido web no confiable primero: las páginas se escanean en busca de texto similar a instrucciones antes de que el agente razone sobre ellas.
- Límites honestos: la extracción débil debería marcarse como débil en lugar de pretender ser fiable.
Estado actual
Este es un prototipo temprano de código abierto. La parte más sólida es web_context: limpieza local, fragmentación, ranking, descubrimiento de datos estructurados, escaneo de seguridad y reducción de tokens. La parte más débil es la extracción estructurada genérica sin un LLM, por lo que esa herramienta debería seguir siendo secundaria hasta que tenga cobertura de evaluación real.
Configuración
Variables de entorno:
BRAVE_SEARCH_API_KEY— si está definida,web_searchutiliza Brave Search en lugar del respaldo HTML de DuckDuckGo.INTERNET_CONTEXT_MCP_RERANK=1— habilita el reranker cross-encoder local de forma global. Desactivado por defecto.INTERNET_CONTEXT_MCP_CACHE_DIR— sobrescribe la ubicación de la caché SQLite. El valor predeterminado es~/.cache/internet-context-mcp.
Para habilitar el renderizado del navegador (solo necesario para SPAs renderizadas con JS):
npm install playwright
npx playwright install chromium
Luego llama a cualquier herramienta con render: "browser".
Próximos hitos
- Descomposición de afirmaciones en múltiples oraciones en
web_verifypara que las afirmaciones compuestas devuelvan veredictos por cláusula. - Anclas estables de fragmentos de texto (
#:~:text=...) en la procedencia de los fragmentos para enlazar en profundidad de vuelta a la página. - Soporte de PDF para el pipeline de obtención y limpieza.
robots.txt+ conocimiento del retraso de rastreo para una obtención responsable de solo lectura.- Ampliar la evaluación de inyección de prompts más allá de los 54 casos seleccionados manualmente: integrar conjuntos de datos adversariales disponibles públicamente.
- Cerrar las brechas de expresiones regulares que la evaluación de inyección sacó a la luz (artículos intermedios, "ya no válido", posicionamiento fuera de pantalla).