internet-context-mcp

Servidor MCP somente leitura que fornece a agentes de IA a web como evidência compacta, ranqueada e verificada. Reranker local de codificador cruzado, verificação de alegações NLI, detecção semântica de concordância e contradição entre fontes. Sem chaves de API.

Documentação

internet-context-mcp

Um servidor MCP somente leitura que dá a agentes de IA a web como evidência compacta, ranqueada e verificada — sem chaves de API, sem recuperação em nuvem, todos os modelos locais.

MCP Node License: MIT Version

Seis ferramentas somente leitura — web_research, web_context, web_search, web_read, web_verify, web_extract — além de recursos MCP, prompts e metadados outputSchema / readOnlyHint adequados. Por trás de cada ferramenta: ranqueamento BM25 local, um reranker cross-encoder local, um classificador NLI local, embeddings de sentença locais, um scanner regex de injeção de prompt e um cache de busca em duas camadas (em memória + SQLite).

Medido, não aspiracional: 20/20 na avaliação de relevância, 92,5% de recall de injeção de prompt com 0% de taxa de falso positivo em páginas benignas, detector de contradição com 0 falsos positivos na web real (veja a tabela de avaliação abaixo).

Início rápido

Adicione isto ao seu claude_desktop_config.json (ou configuração equivalente de host MCP):

{
  "mcpServers": {
    "internet-context": {
      "command": "npx",
      "args": ["-y", "internet-context-mcp"]
    }
  }
}

Reinicie o host. Só isso.

A primeira chamada baixa preguiçosamente três modelos locais do HuggingFace (~125 MB no total, em cache): o reranker cross-encoder, o classificador NLI e o modelo de embedding de sentenças. Uma vez em cache, o servidor roda totalmente offline. Nenhuma chave de API é necessária em nenhum momento.

Variáveis de ambiente opcionais

{
  "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
  }
}

Executar localmente a partir do código-fonte (desenvolvedores)

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

O que há na v0.4.x

  • web_research — busca única + busca multi-fonte + ranqueamento entre fontes + citações por bloco + sinal de concordância baseado em redundância + detecção de contradição com suporte NLI.
  • web_verify — verificação de afirmação versus fontes, com suporte do classificador NLI (implicação / neutro / contradição), fallback por regex.
  • web_context — busca + ranqueamento + retorno de blocos de evidência ranqueados com cápsula de prioridade (TL;DR), sinal de confiança de recuperação, extração de dados estruturados, varredura de injeção de prompt, proveniência da fonte com caminhos DOM.
  • web_read — texto de página limpo e compacto com metadados de economia de tokens.
  • web_search — Brave quando BRAVE_SEARCH_API_KEY estiver definido; fallback DuckDuckGo HTML caso contrário.
  • web_extract — extração orientada por esquema de melhor esforço.

Além disso:

  • Anotações readOnlyHint: true + openWorldHint: true em todas as ferramentas. Claude Desktop / Code pode pular prompts de permissão.
  • outputSchema em todas as ferramentas. Hosts recebem JSON tipado (structuredContent) em vez de reanalisar texto livre.
  • Template de recurso MCP internet-context://page/{fingerprint} — o host pode referenciar novamente páginas buscadas por URI sem chamar uma ferramenta novamente.
  • Templates de prompt MCP verify_with_sources, summarize_from_context, research_a_topic.
  • Cache de busca em duas camadas: em memória + uma camada SQLite persistente em ~/.cache/internet-context-mcp/cache.sqlite. Sobrevive a reinicializações do host.
  • Tokenizer js-tiktoken real (cl100k_base). Chega de aproximações chars / 4.
  • Renderização Playwright opcional para SPAs com muito JS via render: "browser", distribuído como um optionalDependency.

Veja CHANGELOG.md para o detalhe por versão.

O que há dentro de uma cápsula de contexto

Uma resposta web_context inclui:

  • uma cápsula de prioridade curta (TL;DR) antes da evidência longa
  • blocos de evidência ranqueados com deslocamentos de caracteres, caminhos de seção e caminhos DOM
  • um sinal de confiança de recuperação para que o agente possa pedir mais fontes quando necessário
  • dados estruturados da página, quando presentes (JSON-LD, microdata, metadados)
  • metadados da página e impressões digitais de conteúdo
  • avisos de risco de injeção de prompt (visível / oculto / comentário / metadados)
  • estimativas de economia de tokens

Ferramentas

web_research

Ferramenta de pesquisa única: busca na web, busca dos N principais resultados em paralelo, ranqueamento de blocos dentro de cada fonte (com o reranker local por padrão), depois ranqueamento cruzado global e retorno de um pacote de evidência unificado com citações de fonte por bloco e um sinal de concordância baseado em redundância.

Use isto quando você estaria chamando web_search e depois web_context várias vezes seguidas.

Entrada:

{
  "query": "What is the Model Context Protocol and who built it?",
  "depth": 4,
  "max_tokens_total": 3000
}

Formato de saída (abreviado):

{
  "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 e agreement_score usam similaridade semântica (cosseno all-MiniLM-L6-v2, ~22MB, carregado preguiçosamente) por padrão na v0.4.0+. Concordância parafraseada agora conta: três fontes dizendo independentemente o mesmo fato com palavras diferentes serão agrupadas. O agrupamento recorre a Jaccard de shingle de 4-gramas quando o modelo de embedding falha ao carregar; o campo clustering_method em cada resposta mostra qual foi usado.

contradictions lista casos em que blocos de fontes diferentes estão no mesmo tópico, mas nenhum implica o outro em nenhuma direção. O detector roda em duas etapas: um pré-filtro de cosseno de embedding (≥0,45) que exige que os dois blocos estejam discutindo a mesma afirmação, e depois uma verificação de não-implicação bidirecional NLI (≤0,05 de implicação em ambas as direções) nos sobreviventes. Ambas devem ser válidas.

Cada contradição inclui topical_similarity (o cosseno real) e confidence (o sinal NLI).

O pré-filtro intencionalmente permite pares do mesmo cluster: no mundo real, duas fontes fazendo afirmações opostas sobre o mesmo fato se parafraseiam com cosseno muito alto (~0,9), então elas se agrupam. Excluir pares do mesmo cluster significaria perder as contradições que mais queremos revelar.

Avaliação do detector — medido, não aspiracional

Isto é o que medimos que o detector realmente faz. Os scripts de avaliação estão em scripts/demo-contradiction-*.ts para que os números sejam reproduzíveis.

Conjunto de avaliaçãoFontes buscadasContradições detectadasO que significa
Positivo sintético ("café reduz risco cardiovascular" vs "café aumenta risco cardiovascular")n/a1 (conf 0,9999, tópico 0,91)O detector dispara em desacordo inequívoco feito à mão
5 consultas ao vivo (ovos, jejum intermitente, café/coração, velocidade da luz, capital da França)180Zero falsos positivos. A v0.4.0 produziu 3 falsos positivos neste conjunto exato; a v0.4.1 eliminou todos.
3 pares de URL selecionados precisamente porque as fontes deveriam discordar (aspirina em prevenção primária, vitamina D, gordura saturada)5 de 8 — NEJM/BMJ retornaram 403 para busca estática0As fontes que foram buscadas deram cautela matizada; fontes primárias onde a disputa é direta estavam atrás de paywall.
3 varreduras de busca com profundidade=8 em tópicos com divisões conhecidas entre popular e evidência (alongamento, café da manhã, corrida e joelhos)23 de 240Os mecanismos de busca retornam o consenso atual; a disputa vive em outro lugar.

Em cerca de 30 fontes reais buscadas, o detector disparou exatamente zero vezes. Também produziu zero falsos positivos.

A afirmação verdadeira sobre a v0.4.x: o detector tem taxa de falso positivo quase zero na web real e dispara de forma confiável em afirmações opostas lexicalmente explícitas. Ele não detecta, em nossos testes, disputas reais mas expressas com prosa cautelosa ou qualificada — que é como a maior parte da web indexada fala sobre desacordo. Três razões:

  1. Os mecanismos de busca (DDG, Google) retornam conteúdo mainstream homogeneizado; a disputa vive em artigos acadêmicos ou fontes contrárias que não ranqueiam bem.
  2. A prosa da web mainstream qualifica seu desacordo ("alguns estudos sugerem", "para certas populações", "pesquisas recentes mostraram"). A não-implicação bidirecional do NLI não dispara em contraste cauteloso.
  3. Muitas fontes primárias onde a disputa é direta (NEJM, BMJ, ScienceDirect, Britannica) bloqueiam buscas estáticas com HTTP 403.

Se você quiser que o detector capture desacordo cauteloso, precisaria afrouxar o teto de implicação e aceitar alguns falsos positivos. Se quiser acesso mais amplo a fontes, precisaria de renderização de navegador e (para periódicos com paywall) credenciais. A v0.4.x não faz nenhum dos dois; permanece somente leitura, local e honesta sobre o que vê.

Ressalva honesta sobre o sinal de concordância: quando agreement_count=N em N fontes, isso significa que N fontes dos resultados de busca se corroboraram — não que a afirmação seja verdadeira. Os mecanismos de busca tendem a retornar a visão mainstream atual, o que pode esconder disputas genuínas (a consulta ovos/colesterol retornou 4 fontes modernas todas concordando com o consenso moderno, mesmo que o tópico tenha sido contestado por décadas).

web_context

Busca uma URL, limpa-a, divide-a em blocos, ranqueia blocos contra uma tarefa de agente com um algoritmo local estilo BM25 e retorna apenas o melhor orçamento de evidência. Esta é a principal ferramenta de redução de tokens.

Entrada:

{
  "url": "https://example.com/docs",
  "task": "find installation steps and configuration details",
  "max_tokens": 1800
}

Formato de saída:

{
  "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

Busca uma URL, remove o ruído visual da página, extrai o conteúdo principal e retorna texto limpo além de metadados de economia de tokens.

Entrada:

{
  "url": "https://example.com/docs",
  "query": "installation configuration",
  "mode": "compact",
  "max_tokens": 4000
}

web_search

Busca na web e retorna resultados compactos e classificados por fonte.

Se BRAVE_SEARCH_API_KEY estiver definido, usa Brave Search. Caso contrário, recorre à busca HTML do DuckDuckGo.

Entrada:

{
  "query": "Model Context Protocol TypeScript SDK docs",
  "limit": 5
}

web_verify

Verifica se uma afirmação é apoiada, refutada ou incerta a partir de uma ou mais URLs de fonte. Busca cada fonte, ranqueia blocos contra a afirmação e procura por apoio ou contradição explícita (com detecção simples de negação perto dos termos correspondentes). Retorna um veredito combinado além de blocos de evidência de apoio e refutação por fonte.

Entrada:

{
  "claim": "the server is read-only",
  "sources": [
    "https://example.com/docs",
    "https://example.com/safety"
  ],
  "max_tokens_per_source": 1400
}

Formato de saída:

{
  "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

Extração genérica de campos de melhor esforço a partir do texto limpo da página. Isto é intencionalmente secundário ao web_context; em muitos agentes, o fluxo melhor é chamar web_context e deixar o modelo host raciocinar sobre os blocos de evidência retornados.

Entrada:

{
  "url": "https://example.com/docs",
  "schema": {
    "title": "string",
    "install_command": "string",
    "configuration_file": "string"
  },
  "query": "installation command configuration file"
}

Instalação

npm install
npm run build
npm test

Teste de Estresse em Sites Reais

O repositório inclui um conjunto de estresse com 100 URLs de dados reais em data/real-sites.json. Ele exercita o pipeline completo contra páginas ao vivo:

npm run stress:real

Opções úteis:

npm run stress:real -- --limit=20 --concurrency=3 --timeout=15000 --maxTokens=1500

O script escreve um relatório compacto em:

reports/stress-real-sites-latest.json

Ele mede sucesso de busca ao vivo, economia de tokens, blocos selecionados, detecção de dados estruturados, avisos de segurança e cobertura de proveniência de fonte.

Avaliação de Injeção de Prompt

O repositório inclui um conjunto adversarial de 54 casos em evals/prompt-injection.json cobrindo sobreposição visível de instruções, texto oculto (display:none / visibility:hidden / opacity:0 / aria-hidden / fora da tela), injeção em comentários HTML, solicitações de credenciais, prompts de exfiltração e páginas de controle benignas.

npm run eval:injection

Números relatados para a v0.4.0 (scanner regex, sem 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
  }
}

Falhas conhecidas: "desconsidere as instruções anteriores" (artigo interveniente), enquadramento "não é mais válido", posicionamento fora da tela via position:absolute;left:-9999px. Falhas reais, intencionalmente expostas em vez de encobertas.

Avaliação de Relevância

O repositório inclui um conjunto de relevância rotulado em evals/relevance.json. Ele verifica se cápsulas comprimidas preservam fatos necessários, evitam termos inúteis, permanecem sob o orçamento de tokens de evidência e incluem proveniência de fonte.

npm run eval:relevance

A execução mais recente de 20 casos na v0.3.0 (reranker ativado por padrão, tokenizer real) passou em todos os 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
}

O relatório é escrito em:

reports/eval-relevance-latest.json

Executar

npm run dev

Para uso compilado:

npm run build
node dist/index.js

Configuração do Cliente MCP

Para clientes que aceitam configuração JSON de servidor MCP:

{
  "mcpServers": {
    "internet-context": {
      "command": "node",
      "args": ["C:/Users/domai/internet-context-mcp/dist/index.js"],
      "env": {
        "BRAVE_SEARCH_API_KEY": ""
      }
    }
  }
}

Restrições de Design

  • Somente leitura primeiro: sem cliques, login, compras, envio de formulários ou ações que mudam estado.
  • Saída compacta primeiro: agentes devem receber contexto útil, não despejos de página.
  • Ranqueamento local primeiro: reduzir tokens sem exigir uma segunda chave de API de LLM.
  • Evidência primeiro: o contexto retornado deve incluir o texto usado para apoiar afirmações.
  • Conteúdo web não confiável primeiro: páginas são varridas em busca de texto semelhante a instruções antes que o agente raciocine sobre elas.
  • Limites honestos: extração fraca deve ser marcada como fraca em vez de fingir ser confiável.

Status Atual

Este é um protótipo open-source inicial. A parte mais forte é web_context: limpeza local, divisão em blocos, ranqueamento, descoberta de dados estruturados, varredura de segurança e redução de tokens. A parte mais fraca é a extração estruturada genérica sem um LLM, então essa ferramenta deve permanecer secundária até ter cobertura real de avaliação.

Configuração

Variáveis de ambiente:

  • BRAVE_SEARCH_API_KEY — se definida, web_search usa o Brave Search em vez do fallback HTML do DuckDuckGo.
  • INTERNET_CONTEXT_MCP_RERANK=1 — ativa o reranker cross-encoder local globalmente. Desativado por padrão.
  • INTERNET_CONTEXT_MCP_CACHE_DIR — substitui a localização do cache SQLite. O padrão é ~/.cache/internet-context-mcp.

Para habilitar a renderização no navegador (necessária apenas para SPAs renderizadas via JS):

npm install playwright
npx playwright install chromium

Em seguida, chame qualquer ferramenta com render: "browser".

Próximos Marcos

  1. Decomposição de afirmações com múltiplas sentenças em web_verify para que afirmações compostas retornem veredictos por cláusula.
  2. Âncoras estáveis de fragmentos de texto (#:~:text=...) na proveniência dos trechos para vinculação profunda de volta à página.
  3. Suporte a PDF no pipeline de busca + limpeza.
  4. Consciência de robots.txt + crawl-delay para busca responsável somente leitura.
  5. Expandir a avaliação de injeção de prompt além dos 54 casos selecionados manualmente — integrar conjuntos de dados adversariais disponíveis publicamente.
  6. Fechar as lacunas de regex que a avaliação de injeção revelou (artigos intervenientes, "não é mais válido", posicionamento fora da tela).