Skip to main content
Glama

Gumi-MCP

Servidor MCP de consulta ao workspace, com política de leitura de código e documentação.

server.py e tunnel_server.py oferecem leitura. A entrada opcional desktop_tunnel.py agrega o Desktop-Gumi e inclui execução/escrita autorizadas. A política de arquivos do leitor não restringe o shell. Consulte PERFIS_E_RECUPERACAO.md para identificar a entrada, os limites e as verificações de recuperação.

testes python licença


O problema

Conectar um agente de IA ao seu repositório parece simples: aponte um servidor de filesystem para a pasta e pronto. Só que um projeto pessoal raramente é só código. Ao lado dele moram dados de uso, exports, mídia, bancos locais — e leitura via MCP sai da sua máquina.

Este servidor nasceu de um projeto pessoal de IA, e a primeira medição foi desconfortável:

Dos 8.970 arquivos do workspace, 8.034 eram dados pessoais e apenas 936 eram código. O servidor entregava os 8.970.

O Gumi-MCP existe para resolver isso: o agente enxerga o código, e só o código.

Related MCP server: repo-inspector-mcp

Como resolve

Uma política, três camadas, um caminho só. Nenhuma tool manipula Path por conta própria — todas passam por security.py:

resolver()            o caminho está dentro da raiz? (resolve symlinks e `..` antes)
    ↓
esta_ignorado()       é pasta gerada? (.git, .venv, node_modules…)
    ↓
motivo_privacidade()  é zona de dados? (pasta — data, backups, temp… — ou padrão de
                      nome: .env, *.local.*, *.db, *.jsonl, *.jks, *-wal…)
    ↓
extensao_legivel()    é formato de texto conhecido? (allowlist, não denylist)

Duas decisões que valem explicar:

Indexar não é servir. Um .png entra no índice porque o metadado é útil; o conteúdo nunca é lido. Já um caminho dentro de uma pasta privada não entra nem como metadado — o nome de um arquivo de dados já é um dado.

O que é bloqueado continua aparecendo, marcado como bloqueado: "privado". Esconder criaria um mapa mentiroso do repositório; o que não pode é o conteúdo sair.

O que o agente ganha

Cursor sem perda

get_changes_since(cursor) no modelo do Watchman: "o que mudou desde X", sem perder nem repetir evento. Cursor inválido devolve fresh_instance em vez de mentir.

Verificação de integridade

verify_integrity() no modelo de FIM (AIDE/Tripwire): filesystem × baseline → {iguais, modificados, sumidos, novos}.

Índice que não mente

Watcher em tempo real e scanner de reconciliação periódico — o mesmo par que o Wazuh usa, porque watcher sozinho perde o que acontece enquanto o servidor está desligado.

Busca com contexto

FTS5 com BM25 devolvendo {path, linha, trecho} — não uma lista de caminhos para o agente reler.

O índice não é palavra final

Toda linha do índice volta a passar pela política na hora da consulta, como o Azure AI Search faz com ACL: retrato envelhece, permissão indexada envelhece junto. 0,42 ms por linha.

Reconciliação em setores

A varredura anuncia cada projeto assim que ele fica pronto, e a busca espera só o setor que ela cruza — o mesmo desenho do refresh=wait_for do Elasticsearch. Medido na raiz real: 10 dos 12 setores prontos em 2 s, de uma varredura fria de 41,5 s.

Erros que ensinam

ToolError dizendo o que fazer a seguir, não "Error executing tool X".

Proveniência

Todo JSON carrega fonte: workspace_observado — o consumidor sabe que aquilo é observação, não conclusão.

19 tools, 2 resources, 3 prompts. Todas com readOnlyHint e openWorldHint=false. Duas delas — search e fetch — são apelidos para o Deep Research (ver abaixo): não abrem caminho novo, chamam a mesma busca e a mesma leitura das outras.

Verificação

320 testes  ·  101 ataques adversariais bloqueados  ·  10 usos legítimos intactos

Alvo marcado com * é prova fraca. Um caminho que não existe sob a raiz servida volta como erro de qualquer jeito — bloqueio de verdade, mas sem a política ter sido consultada. O relatório separa os dois desde 18/09/2026, porque um número que soma as duas coisas afirma mais do que mediu.

E o arnês precisa medir a raiz certa. Medido em 18/09/2026 (lote O4): sem env=, o SDK do MCP entrega ao servidor-filho apenas doze variáveis de sistema — nenhuma GUMI_MCP_*. O filho subia na raiz padrão enquanto o relatório dizia estar testando outra, e o * de prova fraca, calculado no processo pai, saía trocado. Agora o arnês passa as GUMI_MCP_* adiante e pergunta ao próprio servidor que raiz ele serve, em vez de acreditar na variável que ele mesmo enxerga.

Além dos testes de unidade, tests/verificacao_adversarial.py sobe o servidor de verdade contra um workspace de verdade e tenta arrancar dados por todo caminho que consegui imaginar: .., caminho absoluto, maiúsculas (DATA/), barra dupla, . no meio, .. interno, leitura por trecho, por metadado, pelo índice, e pela busca. Mais sete formas de envenenar argumentos e seis de hostilidade que o servidor precisa digerir sem travar (cursor de outra instância, limites de 999.999).

Segurança que quebra a ferramenta não serve — por isso a suíte também exige que o uso legítimo continue de pé.

Instalação

git clone https://github.com/igv8101/gumi-mcp
cd gumi-mcp
python -m venv .venv && .venv/Scripts/activate      # Linux/macOS: source .venv/bin/activate
pip install -r requirements.txt

Defina a raiz a servir. Sem ela o servidor serve apenas a própria pasta — de propósito: melhor servir de menos do que servir demais em silêncio.

GUMI_MCP_ROOT=/caminho/do/seu/projeto

Se a raiz for a pasta que guarda vários projetos, "estar dentro da raiz" deixa de ser resposta suficiente: ao lado do que pode ser servido passa a morar o que não pode de jeito nenhum. Para esse caso existe uma lista branca de primeiro nível — o que não estiver nela fica de fora, inclusive projeto novo que apareça depois:

GUMI_MCP_PROJETOS=Projeto-A,Projeto-B

Vazia (o padrão), a regra fica desligada. Ligada, ela vale para leitura, índice e busca; o que ficou de fora continua aparecendo nas listagens, marcado bloqueado: "fora-do-escopo" — esconder criaria um mapa mentiroso.

A lista é de projetos, e projeto é pasta. Arquivo solto na raiz (um CLAUDE.md, um relatório que alguma automação grava ali) é servido sem precisar entrar nela: ele nunca foi um projeto não autorizado, só caía fora por a comparação ser pelo primeiro pedaço do caminho. Isso não afrouxa mais nada — privacidade, extensão liberada e o sniff de binário continuam por cima, e conteúdo de pasta não autorizada segue barrado.

Conectar a um cliente

scripts/integrar_ia.py registra o Gumi-MCP e o Desktop-Gumi com orquestração no Claude Desktop e no Codex (app e CLI), com backup, releitura e --conferir. Veja INTEGRACAO_IA.md para os provedores, credenciais e limites. O script anterior conectar_clientes.py é legado e não instala a orquestração. Também é possível configurar somente a leitura com comando+args:

{
  "mcpServers": {
    "gumi-workspace": {
      "command": "/caminho/gumi-mcp/.venv/Scripts/python.exe",
      "args": ["/caminho/gumi-mcp/server.py"],
      "env": { "GUMI_MCP_ROOT": "/caminho/do/seu/projeto" }
    }
  }
}

ChatGPT tem conexão separada: endpoint HTTPS autenticado ou Secure MCP Tunnel. O túnel pode conectar o servidor privado sem abrir uma porta pública. A associação depende das credenciais e da disponibilidade na conta.

Revisão 2.8.0

Varreduras incompletas preservam a baseline; o índice de texto remove conteúdo que virou binário; históricos voltam a passar pela política ao serem consultados. Linhas muito longas são divididas por next_line e next_column (reenvie como start_line e start_column). A saúde informa espaço SQLite alocado/reutilizável, e a trilha de auditoria coordena escrita e rotação entre processos.

Validação desta revisão: 382 testes aprovados, mais inicialização e chamadas reais por stdio dos dois servidores. As APIs pagas foram simuladas, sem consumo. Detalhes e limitações em REVISAO_2026-09-22.md.

Deep Research: os apelidos search e fetch

Em Developer Mode o ChatGPT chama qualquer tool pelo nome que ela tiver. O Deep Research não: ele procura duas tools chamadas exatamente search e fetch, com forma de resposta fixa — search(query) devolve {"results": [{id, title, url}]} e fetch(id) devolve {id, title, text, url, metadata}, como structuredContent e como JSON no content.

As duas existem aqui como apelido, não como caminho novo: search chama o mesmo miolo de search_project e fetch passa pelo mesmo portão de read_file. Tudo que uma bloqueia, a outra bloqueia — com a mesma frase, e há teste exigindo isso. A verificação adversarial espelha no fetch toda tentativa feita contra o read_file.

Duas escolhas que valem saber:

  • A URL de citação é gumi://workspace/arquivo/<caminho>, nunca file://. Caminho absoluto carrega o nome de usuário e o desenho das pastas da máquina, e a citação é justamente o que sai dela.

  • Documento acima do teto de resposta sai cortado, dizendo que foi (metadata.truncado, bytes_totais, bytes_servidos). O contrato do Deep Research não tem paginação; recusar o fim do documento perderia também o começo. Quem precisa do resto usa read_file_excerpt.

Quem puder escolher a ferramenta, prefira search_project: ela devolve também linha, trecho e paginação.

Ajustar a política ao seu projeto

Os padrões são conservadores. Para acrescentar as pastas do seu projeto sem versioná-las, crie um privado.local.json (já ignorado pelo git):

{ "diretorios": ["minha_pasta_de_dados"], "prefixos": ["_rascunho"] }

Ele soma aos padrões de config.py — nunca afrouxa. A única exceção é a chave "projetos", que alimenta a lista branca acima: como lista branca autoriza em vez de bloquear, acrescentar um nome ali abre aquele projeto. Está no mesmo arquivo porque é ele que descreve a máquina de quem usa, e não vai para o git.

Configuração

Variável

Padrão

O que faz

GUMI_MCP_ROOT

a própria pasta

raiz servida

GUMI_MCP_PROJETOS

vazio (sem restrição)

lista branca de projetos de primeiro nível, separados por vírgula

GUMI_MCP_DATABASE

data/gumi_state.db

índice SQLite

GUMI_MCP_LOGS

logs/

log e trilha de auditoria

GUMI_MCP_SCAN_HOURS

6

intervalo do scanner de reconciliação

GUMI_MCP_ORIGEM

desconhecida

rótulo de quem subiu este processo; vai em cada linha da trilha

O servidor escreve apenas no próprio banco e nos próprios logs. Nada no workspace é criado, alterado, movido ou executado — nunca.

Quem leu o quê

A trilha (logs/auditoria.jsonl) guarda uma linha JSON por chamada: quando, de onde (origem), qual tool, quais argumentos, quantos bytes saíram (bytes_resposta) e se foi negada. Nunca o conteúdo servido — só o tamanho dele.

origem vem de GUMI_MCP_ORIGEM e é declarada por quem sobe o processo: o servidor fala stdio, então cada cliente sobe o seu. É o que permite responder "o que o GPT leu do meu computador?" com um arquivo em vez de uma lembrança — get_audit_trail(origem="gpt") devolve só as leituras daquele lado.

Duas honestidades sobre ela. É declarada, não provada: separa clientes que se identificam, e não é defesa contra um cliente que minta. E não autoriza nada — a política de security.py não olha para a origem, e tem teste prendendo isso, porque no dia em que a origem virasse permissão, mentir nela viraria ataque. Quem não se declara entra como desconhecida; carimbar claude por padrão seria inventar procedência.

Notas de implementação

Três coisas que só apareceram construindo, e que valem para qualquer servidor MCP:

Em stdio, stdout pertence ao protocolo. Um print() de debug derruba a conexão JSON-RPC. Todo log aqui vai para arquivo ou stderr.

Só ToolError chega ao cliente. Qualquer outra exceção vira "Error executing tool X" genérico, e o modelo fica sem saber se errou o caminho, se o arquivo é grande demais ou se foi bloqueado.

Watcher em tempo real não basta. Se o servidor sobe e desce a cada consulta, ele não vê nada nos intervalos — e o scanner da subida grava depois que a primeira resposta foi montada, o que faz a percepção chegar com uma consulta de atraso. A correção é perguntar duas vezes na mesma sessão, usando o cursor da primeira.

Licença

MIT — veja LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    Provides LLMs with safe, read-only access to local codebases for searching, reading files, and finding function definitions. All source code remains local, ensuring privacy while enabling AI assistants to explore project structures and functionality.
    4
    -
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI clients to securely inspect local source repositories with read-only tools for listing files, reading files, and searching text, while enforcing path traversal protection and sensitive-file filtering.
    3
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables language models to explore and analyze source code repositories through read-only tools for listing directories, reading files, searching code, finding symbols, and inspecting git history, with strict sandboxing and security controls.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI coding assistants to inspect local and remote repositories through sandboxed tools that surface project overviews, structure, dependency audits, Git churn hotspots, and a heuristic health score. It also supports safe GitHub issue searching and creation with built-in duplicate preflight checks, while blocking sensitive files, binary content, and path traversal attacks.
    4 npm
    MIT