Content
<!-- mcp-name: io.github.atomno-mcp/mcp-egrul -->
# Tool List
> MCP Server (Model Context Protocol — open protocol for connecting AI assistants to external tools) for working with the Unified State Register of Legal Entities of the Russian Federation (USRLE) and the Unified State Register of Individual Entrepreneurs (USRE). Source — official open-data dumps from the Federal Taxation Service of Russia.
**Status:** `v0.1.2` — open version (self-host via SQLite) is fully ready + client-side hosted Pro (HTTP client `HostedClient` for `api.atomno-mcp.ru`). Published on [PyPI](https://pypi.org/project/atomno-mcp-egrul/), indexed in [Glama](https://glama.ai/mcp/servers) and [Smithery](https://smithery.ai/). Hosted Pro infrastructure is under active development. **Coverage `100.00%`** (345 tests, ruff clean, fastmcp 3.2.4, enforced via `--cov-fail-under=100`).
**Pair Project:** [`mcp-fns-check`](https://github.com/atomno-mcp/mcp-fns-check) (risk-check layer on top of USRLE).
---
## What is this
Seven MCP tools visible to AI assistants (Cursor, Claude Desktop, Cline, any MCP client):
| Tool | Description | Arguments |
|---|---|---|
| `search_by_inn` | Search by INN (10 digits — legal entity, 12 — individual entrepreneur) | `inn: str` |
| `search_by_ogrn` | Search by OGRN (13) or OGRNIP (15) | `ogrn: str` |
| `search_by_name` | Fuzzy search by name (FTS5) | `query: str, limit?: int, only_active?: bool` |
| `get_full_card` | Full card with all sections | `inn?: str, ogrn?: str` |
| `get_founders` | Only founders with shares | `inn: str` |
| `get_director` | Only current head | `inn: str` |
| `bulk_cards` | Mass check (up to 100 INNs) | `inns: list[str]` |
Plus diagnostic `ping` to check if the server is alive.
Full payload specification is in `src/mcp_egrul/schemas.py` (Pydantic models `CompanyCard`, `IECard`, `SearchResult`, `BulkResult`).
---
## Installation
### Option 1 — via PyPI (recommended for users)
```bash
# Without local clone — works out of the box
uvx atomno-mcp-egrul
# Or global installation
pipx install atomno-mcp-egrul
atomno-mcp-egrul
# Or classic pip in venv
pip install atomno-mcp-egrul
atomno-mcp-egrul
```
### Option 2 — dev mode (for developers)
Requires Python 3.11+ and [`uv`](https://docs.astral.sh/uv/) (fast replacement for pip, optional).
```bash
git clone https://github.com/atomno-mcp/mcp-egrul
cd mcp-egrul
uv venv
uv pip install -e ".[dev]"
```
Alternatively via pip:
```bash
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux/macOS
pip install -e ".[dev]"
```
---
## Launch
```bash
atomno-mcp-egrul
```
Default transport is **stdio** (standard input/output JSON-RPC). Suitable for connecting to Cursor / Claude Desktop / Claude Code.
### Claude Desktop (`claude_desktop_config.json`)
```json
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"]
}
}
}
```
### Cursor (`.cursor/mcp.json` in project or `~/.cursor/mcp.json` globally)
```json
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"]
}
}
}
```
> If you don't use `uv`, replace `"command": "uvx", "args": ["atomno-mcp-egrul"]` with `"command": "atomno-mcp-egrul"` (requires `pip install atomno-mcp-egrul` or `pipx install atomno-mcp-egrul`).
---
## Docker (self-host) — quick start
```bash
# 1. Download dumps from the Federal Taxation Service of Russia (acceptance on the FTS website — once in a lifetime).
# Sources:
# USRLE — https://www.nalog.gov.ru/opendata/7707329152-egrul/
# USRE — https://www.nalog.gov.ru/opendata/7707329152-egrip/
# Place them in the structure:
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. Initial full import (once, ~30-60 minutes):
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. Run server + background cron daemon:
docker compose up -d
docker compose logs -f mcp-egrul-scheduler
```
About ~10 minutes after import, all tools (`search_by_inn`, `search_by_name`, etc.) respond with data from the local FTS dump.
Container internal `/data` volume structure:
```
/data/
├── mcp_egrul_data.sqlite # SQLite + FTS5
└── dumps/ # read-only mounted from ./dumps
├── egrul/
│ └── YYYY-MM-DD/*.zip
└── egrip/
└── YYYY-MM-DD/*.zip
```
Cron daemon (`atomno-mcp-egrul-scheduler`) fetches the latest dump after you place it in `dumps/<registry>/<YYYY-MM-DD>/` — at night at 03:00 Europe/Moscow. If there's nothing new, the job completes with `nothing_to_import` and doesn't make any extra records in `import_log`.
---
## Importing FTS dumps (manual mode)
Sources:
- **USRLE open-data:** `https://www.nalog.gov.ru/opendata/7707329152-egrul/`
- **USRE open-data:** `https://www.nalog.gov.ru/opendata/7707329152-egrip/`
Format: daily XML archives in ZIP, ~15 GB per full snapshot. Legally, you need to download them **from the FTS website after accepting the license** — the server doesn't download archives itself (strictly).
CLI:
```bash
# Full initial import (once):
atomno-mcp-egrul-import --registry egrul --full
atomno-mcp-egrul-import --registry egrip --full
# Incremental (cron / manual): loads only if a newer YYYY-MM-DD folder appears than the last successful `import_log.source_dump_date`.
# If there's nothing newer — exit code 5 and `nothing_to_import` message.
atomno-mcp-egrul-import --registry egrul --incremental
# Background cron daemon with daily 03:00 MSK (call manually rarely;
# usually launched by the mcp-egrul-scheduler service in docker-compose).
atomno-mcp-egrul-scheduler --run-now
```
Exit codes `atomno-mcp-egrul-import`:
| Code | Meaning |
|---|---|
| 0 | Import successful |
| 2 | Invalid config / CLI argument |
| 4 | Ingestion error (corrupted XML, no dump directory, DB error) |
| 5 | `nothing_to_import` — latest date is already in DB (incremental) |
---
## Pro / hosted mode (proxy to `api.atomno-mcp.ru`)
When a user sets `ATOMNO_API_KEY`, **all seven tools** are automatically proxied to the hosted Pro API (SPEC §5.4, §5.4.1). Local SQLite is not used in this mode — hosted Pro provides:
- **Up-to-date data for today** (without a daily delay of open-data dump): direct scrape `egrul.nalog.ru` + Dadata fallback on the server side.
- **Bulk endpoint without rate limit** (`POST /companies/bulk`) — one request instead of N local gathers.
- **AI summary card**, change history, search by director's full name (Pro-only tools — come with hosted server in Phase 2, see §5.4.1).
**Price**: Pro — $10/month separately or $15/month in a bundle with `mcp-fns-check` (bundle key). Free tier: 30 requests/day/IP without registration (SPEC §1).
**Configuration in Cursor** (`.cursor/mcp.json`):
```json
{
"mcpServers": {
"egrul": {
"command": "uvx",
"args": ["atomno-mcp-egrul"],
"env": {
"ATOMNO_API_KEY": "your-pro-key-here"
}
}
}
}
```
**Behavior and errors** — no silent fallback: if the hosted API is unavailable, the client raises a typed exception instead of silently returning data from an outdated local dump. HTTP ↔ MCP error code mapping is in SPEC §5.4.1:
| HTTP response hosted API | Client exception | `error.code` |
|---|---|---|
| 200 | — | — |
| 400 | `ValidationError` | `invalid_input` |
| 401 | `HostedAuthError` | `auth_required` |
| 403 | `ProRequiredError` | `pro_required` |
| 404 (code=not_found) | `NotFoundError` | `not_found` |
| 404 (wrong route) | `SourceUnavailableError` | `source_unavailable` |
| 413 | `BulkTooLargeError` | `bulk_too_large` |
| 429 | `RateLimitedError` (+ `Retry-After`) | `rate_limit` |
| 5xx | `SourceUnavailableError` | `source_unavailable` |
| timeout / DNS fail | `SourceUnavailableError` (cause=`timeout`/`ConnectError`) | `source_unavailable` |
INN/OGRN validation remains **client-side** (check digits are verified before HTTP request — saving a round-trip on invalid identifiers).
---
## Configuration (environment variables)
| Variable | Description | Default |
|---|---|---|
| `MCP_EGRUL_DB` | Path to SQLite file with USRLE/USRE snapshot | `./mcp_egrul_data.sqlite` |
| `MCP_EGRUL_USER_AGENT` | User-Agent of HTTP client | `mcp-egrul/0.1 (+https://github.com/atomno-mcp/mcp-egrul)` |
| `MCP_EGRUL_HTTP_TIMEOUT` | HTTP timeout in seconds | `30` |
| `MCP_EGRUL_DUMPS_DIR` | Directory with FTS dumps, structure `<dir>/<registry>/<YYYY-MM-DD>/*.zip` | `./dumps` |
| `MCP_EGRUL_LOG_LEVEL` | Logging level | `INFO` |
| `TZ` | Timezone for scheduler (cron 03:00) | `Europe/Moscow` |
| `ATOMNO_API_KEY` | (Pro) hosted subscription key — enables proxying to `api.atomno-mcp.ru` | not set |
| `ATOMNO_API_BASE` | (Pro) base URL of hosted API | `https://api.atomno-mcp.ru/mcp-egrul/v1` |
Example — see `.env.example`.
## Structure
```
apps/mcp-egrul/
├── pyproject.toml
├── LICENSE # MIT
├── README.md # THIS FILE
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── .gitignore
├── src/mcp_egrul/
│ ├── __init__.py
│ ├── server.py # FastMCP entrypoint, registration of 7 tools + ping
│ ├── context.py # ServiceContext (DI: SQLiteStore + HTTP-client)
│ ├── config.py # Reading env-vars into typed fields
│ ├── constants.py # All magic numbers and enums
│ ├── validators.py # Control numbers of TIN (10/12) and OGRN (13/15)
│ ├── schemas.py # Pydantic models CompanyCard/IECard/SearchResult/...
│ ├── errors.py # McpEgrulError and subclasses
│ ├── db/
│ │ ├── __init__.py
│ │ └── sqlite.py # Async client (aiosqlite), init/query/upsert/search + import_log
│ ├── sources/
│ │ ├── __init__.py
│ │ ├── base.py # Abstract interface Source
│ │ ├── opendata.py # Federal Taxation Service open-data adapter (read-local → SQLite upsert)
│ │ ├── opendata_parser.py # Streaming lxml.iterparse parser of ЕГРЮЛ/ЕГРИП XML
│ │ └── hosted_adapter.py # HTTP-client 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` (manual / one-time)
│ └── scheduler.py # CLI `atomno-mcp-egrul-scheduler` (apscheduler cron 03:00 MSK)
└── tests/
├── __init__.py
├── conftest.py
├── fixtures/
│ ├── egrul_sample.xml # Mini-ЕГРЮЛ (2 valid + 1 skip for unknown status)
│ └── egrip_sample.xml # Mini-ЕГРИП (active + closed)
├── test_validators.py
├── test_schemas.py
├── test_config.py # Config.from_env + _parse_float_env (validation)
├── 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 tools: happy-path + validation + not_found
├── test_opendata_parser.py # XML parser (zip, xml, skip for unknown status)
├── test_opendata_source.py # OpenDataSource.run_ingest (full/incremental)
├── test_integration_import.py # Full cycle 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 + routing tools (respx-mocks)
```
---
## Tests
```bash
pytest -v --cov=src/mcp_egrul
```
Current coverage: **100.00%** (`345 tests passed`, ruff clean, 1529 statements + 382 branches,
**0 misses**). Enforced policy `--cov-fail-under=100` — any regression will break CI. Tests cover:
* TIN/OGRN/OGRNIP validators (control numbers);
* `Config.from_env` + float-env-variables parser (validation, not silent fallback);
* all 7 MCP tools (happy-path + validation + not_found + bulk partial);
* SQLite store + FTS5 + `import_log`;
* XML parser of ЕГРЮЛ/ЕГРИП (zip, xml, skip records with unknown status);
* `OpenDataSource.run_ingest` (full/incremental/`nothing_to_import`);
* full integration cycle `import fixture → search → get_card → bulk`;
* both CLI (`atomno-mcp-egrul-import`, `atomno-mcp-egrul-scheduler`) — cron job registration, argument parsing,
`_run_daily_ingest` on all-happy/`nothing_to_import`/`McpEgrulError`, full cycle
`_run_scheduler` with mock-ed `asyncio.Event`;
* FastMCP tool-layer via `mcp.call_tool()` — error serialization into structured dicts,
`server.main()` with valid and invalid env;
* `HostedClient` (hosted Pro API proxy) — happy-path of all 7 methods, all HTTP errors from
SPEC §5.4.1 (401/403/404/413/429/5xx), timeout/ConnectError, invalid JSON/payload from
server, client validation bulk, `async with`-context; plus routing from tools
to hosted mode (when `ATOMNO_API_KEY` is set — request goes to `api.atomno-mcp.ru`, not to SQLite,
TIN validation before HTTP);
* edge cases of XML parser (75 separate unit tests for `_parse_company`/`_parse_ie`/
`_parse_share`/`_parse_director`/`_parse_founders`/address fallbacks/legacy attributes/
invalid TIN/OGRN/KPP lengths);
* private helpers of SQLite store (`_wrap`, `_prepare_row`, `_row_to_dict`, `_normalize_bm25`,
auto-init via `_ensure`, rejecting invalid `finish_import` statuses);
* `ServiceContext` reentry-idempotence, `atexit`-cleanup, `Config.from_env` ValidationError
→ exit code 2 from `atomno-mcp-egrul-import` CLI.
External APIs **are never called directly** from tests — only via `respx` (HTTP mocking) and
local XML fixtures (`tests/fixtures/`).
---
## Security and Legal Status
- All sources are **publicly available data from the Federal Taxation Service** (ЕГРЮЛ / ЕГРИП open-datasets),
the distribution of which is permitted by the Federal Law "On Information..." and ЕГРЮЛ-specific norms (see SPEC §8).
- Legal entities do not fall under the 152-FZ (On Personal Data).
- Full names of individual entrepreneurs and founders are published by the Federal Taxation Service in an open registry —
the transfer of this data is legal.
- No write operations to any external API.
- Secrets are only passed through environment variables, in the repository — `.env.example` without values.
---
## Disclaimer
The service is **an aggregator and a convenient interface over public data from the Federal Taxation Service**.
It is not affiliated with the Federal Taxation Service. Use it at your own risk. The information in the service responses
is not a substitute for a full-fledged legal or financial assessment.
---
## License
MIT. `LICENSE` file in the root of the folder.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available