mcp-egrul
MCP-servidor para verificação de contrapartes via egrul.nalog.ru: obtenção de extrato EGRUL/EGRIP por INN/OGRN.
Documentação
mcp-egrul
Servidor MCP (Model Context Protocol — protocolo aberto para conectar assistentes de IA a ferramentas externas) para trabalhar com EGRUL (Registro Estadual Unificado de Pessoas Jurídicas da RF) e EGRIP (Registro Estadual Unificado de Empreendedores Individuais). Fonte — dumps oficiais de open-data da FNS (Serviço Fiscal Federal).
Status: v0.1.2 — versão open (self-host via SQLite) totalmente pronta + parte cliente do hosted Pro (cliente HTTP HostedClient para api.atomno-mcp.ru). Publicado no PyPI, indexado no Glama e Smithery. A própria infraestrutura hosted Pro — em desenvolvimento ativo. Cobertura 100.00% (345 testes, ruff limpo, fastmcp 3.2.4, aplicado via --cov-fail-under=100).
Projeto parceiro: mcp-fns-check (camada de verificação de risco sobre EGRUL).
O que é
Sete ferramentas MCP, visíveis ao assistente de IA (Cursor, Claude Desktop, Cline, qualquer cliente MCP):
| Ferramenta | Descrição | Argumentos |
|---|---|---|
search_by_inn | Busca por INN (10 dígitos — pessoa jurídica, 12 — empreendedor individual) | inn: str |
search_by_ogrn | Busca por OGRN (13) ou OGRNIP (15) | ogrn: str |
search_by_name | Busca difusa por nome (FTS5) | query: str, limit?: int, only_active?: bool |
get_full_card | Cartão completo com todas as seções | inn?: str, ogrn?: str |
get_founders | Apenas fundadores com participações | inn: str |
get_director | Apenas o diretor atual | inn: str |
bulk_cards | Verificação em massa (até 100 INN) | inns: list[str] |
Além disso, o ping de diagnóstico para verificar se o servidor está ativo.
Especificação completa dos payloads — em src/mcp_egrul/schemas.py (modelos Pydantic CompanyCard, IECard, SearchResult, BulkResult).
Instalação
Opção 1 — via PyPI (recomendado para usuários)
# Без локального clone — работает «из коробки»
uvx atomno-mcp-egrul
# Или установка глобально
pipx install atomno-mcp-egrul
atomno-mcp-egrul
# Или классический pip в venv
pip install atomno-mcp-egrul
atomno-mcp-egrul
Opção 2 — modo dev (para desenvolvedores)
Requer Python 3.11+ e uv (substituto rápido para pip, opcional).
git clone https://github.com/atomno-mcp/mcp-egrul
cd mcp-egrul
uv venv
uv pip install -e ".[dev]"
Alternativamente via pip:
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux/macOS
pip install -e ".[dev]"
Execução
atomno-mcp-egrul
Transporte padrão — stdio (entrada/saída padrão JSON-RPC). Adequado para conectar ao Cursor / Claude Desktop / Claude Code.
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"]
}
}
}
Cursor (.cursor/mcp.json no projeto ou ~/.cursor/mcp.json globalmente)
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"]
}
}
}
Se você não usa
uv, substitua"command": "uvx", "args": ["atomno-mcp-egrul"]por"command": "atomno-mcp-egrul"(requerpip install atomno-mcp-egruloupipx install atomno-mcp-egrul).
Docker (self-host) — início rápido
# 1. Скачайте дампы ФНС (acceptance на сайте ФНС — раз в жизни).
# Источники:
# ЕГРЮЛ — https://www.nalog.gov.ru/opendata/7707329152-egrul/
# ЕГРИП — https://www.nalog.gov.ru/opendata/7707329152-egrip/
# Положите их в структуру:
mkdir -p dumps/egrul/2026-04-24 dumps/egrip/2026-04-24
cp ~/Downloads/EGRUL_*.zip dumps/egrul/2026-04-24/
cp ~/Downloads/EGRIP_*.zip dumps/egrip/2026-04-24/
# 2. Первоначальный полный импорт (однократно, ~30-60 минут):
docker compose --profile import run --rm \
mcp-egrul-import atomno-mcp-egrul-import --registry egrul --full
docker compose --profile import run --rm \
mcp-egrul-import atomno-mcp-egrul-import --registry egrip --full
# 3. Запустите сервер + фоновый cron-демон:
docker compose up -d
docker compose logs -f mcp-egrul-scheduler
Após ~10 minutos da importação, todas as ferramentas (search_by_inn, search_by_name etc.) já respondem com dados do snapshot local da FNS.
Esquema do volume /data dentro do contêiner:
/data/
├── mcp_egrul_data.sqlite # SQLite + FTS5
└── dumps/ # read-only монтируется из ./dumps
├── egrul/
│ └── YYYY-MM-DD/*.zip
└── egrip/
└── YYYY-MM-DD/*.zip
O daemon cron (atomno-mcp-egrul-scheduler) baixa automaticamente a exportação mais recente depois
que você a coloca em dumps/<registry>/<YYYY-MM-DD>/ — à noite às 03:00
Europe/Moscow. Se não houver nada novo — o job termina com nothing_to_import
e não faz nenhum registro extra em import_log.
Importação de dumps da FNS (modo manual)
Fontes:
- EGRUL open-data:
https://www.nalog.gov.ru/opendata/7707329152-egrul/ - EGRIP open-data:
https://www.nalog.gov.ru/opendata/7707329152-egrip/
Formato: arquivos XML diários em ZIP, ~15 GB para um snapshot completo. Legalmente, eles precisam ser baixados do site da FNS após aceitar a licença — o servidor não baixa os arquivos por conta própria (estritamente).
CLI:
# Полный первоначальный импорт (однократно):
atomno-mcp-egrul-import --registry egrul --full
atomno-mcp-egrul-import --registry egrip --full
# Инкремент (cron / ручной): загружается только если появилась более
# свежая YYYY-MM-DD-папка, чем последний успешный `import_log.source_dump_date`.
# Если новее нет — exit-code 5 и сообщение `nothing_to_import`.
atomno-mcp-egrul-import --registry egrul --incremental
# Фоновой cron-демон с ежедневным 03:00 MSK (вызывать вручную редко;
# обычно запускается сервисом mcp-egrul-scheduler в docker-compose).
atomno-mcp-egrul-scheduler --run-now
Códigos de saída atomno-mcp-egrul-import:
| Código | Significado |
|---|---|
| 0 | Importação concluída com sucesso |
| 2 | Configuração / argumento CLI inválido |
| 4 | Erro de ingestão (XML corrompido, diretório de dumps ausente, erro de banco de dados) |
| 5 | nothing_to_import — a data mais recente já está no banco de dados (incremental) |
Modo Pro / hosted (proxy para api.atomno-mcp.ru)
Quando o usuário define ATOMNO_API_KEY, todas as sete ferramentas são automaticamente
proxadas para a API hosted Pro (SPEC §5.4, §5.4.1). O SQLite local neste
modo não é usado — o hosted Pro oferece:
- Dados atualizados para hoje (sem o atraso diário do dump open-data): scrape direto
egrul.nalog.ru+ fallback Dadata no lado do servidor. - Endpoint bulk sem rate-limit (
POST /companies/bulk) — uma única requisição em vez de N coletas locais. - Resumo de cartão com IA, histórico de alterações, busca por nome do diretor (ferramentas exclusivas do Pro — chegam junto com o servidor hosted na Fase 2, veja §5.4.1).
Preço: Pro — US$ 10/mês separadamente ou US$ 15/mês em conjunto com mcp-fns-check (chave bundle). Nível gratuito: 30 requisições/dia/IP sem registro (SPEC §1).
Configuração no Cursor (.cursor/mcp.json):
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"],
"env": {
"ATOMNO_API_KEY": "your-pro-key-here"
}
}
}
}
Comportamento e erros — sem fallback silencioso: se a API hosted estiver indisponível, o cliente levanta uma exceção tipada, em vez de silenciosamente retornar dados do dump local desatualizado. Mapeamento de erro HTTP ↔ código MCP — na SPEC §5.4.1:
| Resposta HTTP da API hosted | Exceção do cliente | error.code |
|---|---|---|
| 200 | — | — |
| 400 | ValidationError | invalid_input |
| 401 | HostedAuthError | auth_required |
| 403 | ProRequiredError | pro_required |
| 404 (code=not_found) | NotFoundError | not_found |
| 404 (rota errada) | SourceUnavailableError | source_unavailable |
| 413 | BulkTooLargeError | bulk_too_large |
| 429 | RateLimitedError (+ Retry-After) | rate_limit |
| 5xx | SourceUnavailableError | source_unavailable |
| timeout / falha de DNS | SourceUnavailableError (cause=timeout/ConnectError) | source_unavailable |
A validação de INN/OGRN permanece no lado do cliente (os dígitos de verificação são validados antes da requisição HTTP — economia de round-trip em identificadores inválidos).
Configuração (variáveis de ambiente)
| Variável | Descrição | Padrão |
|---|---|---|
MCP_EGRUL_DB | Caminho para o arquivo SQLite com o snapshot EGRUL/EGRIP | ./mcp_egrul_data.sqlite |
MCP_EGRUL_USER_AGENT | User-Agent do cliente HTTP | mcp-egrul/0.1 (+https://github.com/atomno-mcp/mcp-egrul) |
MCP_EGRUL_HTTP_TIMEOUT | Timeout HTTP em segundos | 30 |
MCP_EGRUL_DUMPS_DIR | Diretório com dumps da FNS, estrutura <dir>/<registry>/<YYYY-MM-DD>/*.zip | ./dumps |
MCP_EGRUL_LOG_LEVEL | Nível de log | INFO |
TZ | Fuso horário para o agendador (cron 03:00) | Europe/Moscow |
ATOMNO_API_KEY | (Pro) chave da assinatura hosted — ativa o proxy para api.atomno-mcp.ru | não definido |
ATOMNO_API_BASE | (Pro) URL base da API hosted | https://api.atomno-mcp.ru/mcp-egrul/v1 |
Exemplo — veja .env.example.
Estrutura
apps/mcp-egrul/
├── pyproject.toml
├── LICENSE # MIT
├── README.md # ЭТОТ ФАЙЛ
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── .gitignore
├── src/mcp_egrul/
│ ├── __init__.py
│ ├── server.py # FastMCP entrypoint, регистрация 7 тулзов + ping
│ ├── context.py # ServiceContext (DI: SQLiteStore + HTTP-клиент)
│ ├── config.py # Чтение env-vars в типизированные поля
│ ├── constants.py # Все магические числа и enum'ы
│ ├── validators.py # Контрольные цифры ИНН (10/12) и ОГРН (13/15)
│ ├── schemas.py # Pydantic-модели CompanyCard/IECard/SearchResult/...
│ ├── errors.py # McpEgrulError и подклассы
│ ├── db/
│ │ ├── __init__.py
│ │ └── sqlite.py # Async-клиент (aiosqlite), init/query/upsert/search + import_log
│ ├── sources/
│ │ ├── __init__.py
│ │ ├── base.py # Абстрактный интерфейс Source
│ │ ├── opendata.py # ФНС open-data адаптер (read-local → SQLite upsert)
│ │ ├── opendata_parser.py # Потоковый lxml.iterparse парсер ЕГРЮЛ/ЕГРИП XML
│ │ └── hosted_adapter.py # HTTP-клиент hosted Pro API (SPEC §5.4.1)
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── search_by_inn.py
│ │ ├── search_by_ogrn.py
│ │ ├── search_by_name.py
│ │ ├── get_full_card.py
│ │ ├── get_founders.py
│ │ ├── get_director.py
│ │ └── bulk_cards.py
│ └── scripts/
│ ├── __init__.py
│ ├── import_opendata.py # CLI `atomno-mcp-egrul-import` (ручной / одноразовый)
│ └── scheduler.py # CLI `atomno-mcp-egrul-scheduler` (apscheduler cron 03:00 MSK)
└── tests/
├── __init__.py
├── conftest.py
├── fixtures/
│ ├── egrul_sample.xml # Мини-ЕГРЮЛ (2 валидных + 1 skip на неизвестный статус)
│ └── egrip_sample.xml # Мини-ЕГРИП (active + closed)
├── test_validators.py
├── test_schemas.py
├── test_config.py # Config.from_env + _parse_float_env (валидация env)
├── test_sqlite_store.py
├── test_cards.py # _cards.py: parse_iso_date/datetime + build_*card
├── test_server_ping.py # FastMCP tool-layer + server.main()
├── test_tools.py # 7 тулзов: happy-path + validation + not_found
├── test_opendata_parser.py # XML-парсер (zip, xml, skip-на-неизвестный-статус)
├── test_opendata_source.py # OpenDataSource.run_ingest (full/incremental)
├── test_integration_import.py # Полный цикл import → search → get_card
├── test_import_cli.py # CLI `atomno-mcp-egrul-import`
├── test_scheduler_cli.py # CLI `atomno-mcp-egrul-scheduler` + _run_scheduler
└── test_hosted_adapter.py # HostedClient + маршрутизация тулзов (respx-моки)
Testes
pytest -v --cov=src/mcp_egrul
Cobertura atual: 100.00% (345 tests passed, ruff limpo, 1529 declarações + 382 ramos,
0 falhas). Aplicado pela política --cov-fail-under=100 — qualquer regressão quebrará o CI. Os testes cobrem:
- validadores de INN/OGRN/OGRNIP (dígitos de verificação);
Config.from_env+ parser de variáveis float-env (validação, não fallback silencioso);- todas as 7 ferramentas MCP (caminho feliz + validação + not_found + bulk parcial);
- armazenamento SQLite + FTS5 +
import_log; - parser XML EGRUL/EGRIP (zip, xml, registro ignorado com status desconhecido);
OpenDataSource.run_ingest(completo/incremental/nothing_to_import);- ciclo de integração completo
import fixture → search → get_card → bulk; - ambas as CLIs (
atomno-mcp-egrul-import,atomno-mcp-egrul-scheduler) — registro de jobs cron, parsing de argumentos,_run_daily_ingestem all-happy/nothing_to_import/McpEgrulError, ciclo completo_run_schedulercomasyncio.Eventsimulado; - camada de ferramentas FastMCP via
mcp.call_tool()— serialização de erros em dicts estruturados,server.main()com env válido e inválido; HostedClient(proxy da API hosted Pro) — caminho feliz de todos os 7 métodos, todos os erros HTTP da SPEC §5.4.1 (401/403/404/413/429/5xx), timeout/ConnectError, JSON/payload inválido do servidor, validação bulk no cliente, contextoasync with; além do roteamento das ferramentas no modo hosted (quandoATOMNO_API_KEYestá definido — a requisição vai paraapi.atomno-mcp.ru, não para o SQLite, validação de INN antes do HTTP);- casos extremos do parser XML (75 testes unitários separados em
_parse_company/_parse_ie/_parse_share/_parse_director/_parse_founders/fallbacks de endereço/atributos legados/ comprimentos inválidos de INN/OGRN/KPP); - helpers privados do armazenamento SQLite (
_wrap,_prepare_row,_row_to_dict,_normalize_bm25, auto-init via_ensure, rejeitando statusfinish_importinválidos); - idempotência de reentrada
ServiceContext, limpezaatexit,Config.from_envValidationError → código de saída 2 da CLIatomno-mcp-egrul-import.
APIs externas nunca são chamadas diretamente nos testes — apenas via respx (mock HTTP) e
fixtures XML locais (tests/fixtures/).
Segurança e status legal
- Todas as fontes — dados publicamente abertos da FNS (open-datasets EGRUL / EGRIP), cuja distribuição é permitida pela Lei Federal "Sobre Informação…" e normas específicas do EGRUL (veja SPEC §8).
- Pessoas jurídicas não se enquadram na 152-FZ (Sobre Dados Pessoais).
- Nomes de diretores e fundadores pessoas físicas são publicados pela própria FNS no registro aberto — o repasse desses dados é legal.
- Nenhuma operação de escrita em qualquer API externa.
- Segredos — apenas via variáveis de ambiente, no repositório —
.env.examplesem valores.
Aviso legal
O serviço é um agregador e interface conveniente sobre dados públicos da FNS. Não é afiliado à FNS. Use por sua conta e risco. As informações nas respostas do serviço não substituem uma avaliação jurídica ou financeira completa.
Licença
MIT. Arquivo LICENSE na raiz da pasta.