Skip to main content
Glama
Tomi5037

ares-mcp

by Tomi5037

ares-mcp

CI Python 3.11+ License: MIT

An MCP server that gives an LLM agent grounded access to ARES, the Czech public business register: look a company up by its registration number (IČO), search the register by name, check VAT registration, and validate an IČO offline.

Why it exists. Ask a model about a Czech company and it will happily invent an address and a VAT number. That is fine in a chat and unacceptable in a back-office workflow — onboarding a client, checking a counterparty, filling a contract header. This server replaces the guess with a citable record from the state register: every answer carries a source_url a human can open.


Tools

Tool

What it does

Network

lookup_company(ico)

Full company profile: name, seat, legal form, incorporation date, VAT ID, CZ-NACE activities, registers

ARES

search_companies(name, limit=10)

Full-text search by business name, capped at 50 hits

ARES

check_vat_registration(ico)

Whether the subject is an active VAT payer, plus its VAT ID

ARES

validate_ico_number(ico)

Modulo-11 checksum validation and normalisation

none — pure logic

Every tool returns a flat, documented JSON object. Failures come back as {"error": "...", "message": "..."} instead of an exception, so the agent can recover rather than abort the turn.

Related MCP server: cz-agents-mcp

Quick start

git clone https://github.com/Tomi5037/ares-mcp.git
cd ares-mcp
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest

Run the server directly — it speaks MCP over stdio, so it waits for a client:

ares-mcp                              # stdio (default)
ares-mcp --transport streamable-http  # run it as an HTTP service instead

Built on the official MCP Python SDK 2.x. All four tools are annotated read_only_hint, so a client can auto-approve them without prompting on every call.

Use it from Claude Code

claude mcp add ares -- /absolute/path/to/.venv/bin/ares-mcp

Use it from Claude Desktop

claude_desktop_config.json:

{
  "mcpServers": {
    "ares": {
      "command": "/absolute/path/to/.venv/bin/ares-mcp"
    }
  }
}

Then ask, in plain language:

"Check IČO 04084063 — who is it, are they VAT registered, and what do they do?"

{
  "ico": "04084063",
  "name": "CETIN a.s.",
  "legal_form": "Joint-stock company (a.s.)",
  "address": { "city": "Praha", "region": "Hlavní město Praha", "postal_code": "19000" },
  "established_on": "2015-06-01",
  "vat_number": "CZ04084063",
  "vat_registered": true,
  "is_active": true,
  "activities": [{ "code": "6110", "section": "J", "section_label": "Information and communication" }],
  "source_url": "https://ares.gov.cz/ekonomicke-subjekty?ico=04084063"
}

How it works

LLM client (Claude Code / Desktop)
        │  MCP over stdio
        ▼
   server.py      4 tools, input validation, error contract
        ▼
   client.py      httpx async, timeouts, retry + backoff, TTL cache
        ▼
   models.py      raw ARES JSON  ->  flat pydantic schema
        ▼
   ARES REST API v3 (public, no API key)

Three decisions worth calling out:

  1. Validate before you call. An IČO is checked locally with its modulo-11 checksum. A hallucinated or mistyped number never becomes an HTTP request, and the agent gets a specific error instead of a generic 404.

  2. Normalise the payload. ARES answers with deeply nested Czech keys and the same record repeated once per source register. models.py flattens that into one documented schema — fewer tokens for the model, and one place to change when the upstream API moves.

  3. Cache and retry. A TTL cache keeps a repeated lookup off the state API, and transient 5xx/timeout responses are retried with exponential backoff. A public service you do not own is a dependency you should be polite to.

Configuration

All optional — ARES is a public API and needs no key.

Variable

Default

Meaning

ARES_BASE_URL

https://ares.gov.cz/ekonomicke-subjekty-v-be/rest

API root

ARES_TIMEOUT_SECONDS

10

Per-request timeout

ARES_MAX_RETRIES

3

Attempts before giving up

ARES_CACHE_TTL_SECONDS

900

Cache lifetime

Tests

pytest --cov=ares_mcp        # 45 tests, no network access
ruff check . && mypy src     # lint and strict typing

HTTP is mocked with httpx.MockTransport against payloads recorded from the live API, so the suite is deterministic and runs offline in CI. Covered: checksum edge cases, cache hits, retry on 503 and on timeout, 404 mapping, missing upstream fields, and the exact JSON contract of every tool.

Limitations

  • ARES exposes public register data only; personal data of natural persons is returned by the upstream API in a limited form and this server does not enrich or store it.

  • The insolvency register (ISIR) is reported only as a presence flag, not with case detail.

  • Data is as fresh as ARES itself (data_updated_on is passed through).

Roadmap

  • Streamable HTTP transport in addition to stdio

  • check_insolvency backed by ISIR

  • Bulk lookup for a list of IČO with concurrency limits

  • Optional Redis cache for multi-process deployments

License

MIT — see LICENSE.

Data comes from ARES, operated by the Czech Ministry of Finance. This project is not affiliated with the Ministry.

Available Tools

4 tools
check_vat_registrationA
Read-onlyIdempotent

Check whether a company is an active VAT payer and return its VAT ID.

Args: ico: The company's IČO.

Returns: A vat_registered flag, the VAT ID and a link to the ARES record.

ParametersJSON Schema
NameRequiredDescriptionDefault
icoYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, covering the safety profile. The description adds useful behavioral context beyond that: it returns a vat_registered flag, the VAT ID, and a link to the ARES record, making the tool's behavior and output nature clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-structured: it front-loads the primary purpose, then clearly separates Args and Returns. Every sentence contributes useful information with no repetition or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity of the tool (one required parameter, a straightforward output schema, and safe annotations), the description is complete. It explains the input, the output fields, and the tool's core behavior without leaving significant gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only defines 'ico' as a string with no description, so schema coverage is 0%. The description compensates by explaining that ico is the company's IČO, which gives essential semantic meaning. It could provide more format or validation details, but for a single identifier parameter this is a solid clarification.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: checking whether a company is an active VAT payer and returning its VAT ID. This distinguishes it from siblings like validate_ico_number (which likely validates IČO format) and lookup_company (which probably retrieves broader company data).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use this tool: when you need VAT registration status or a VAT ID for an IČO. However, it does not explicitly mention alternatives or state conditions under which another sibling tool would be more appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

lookup_companyA
Read-onlyIdempotent

Look up a Czech company profile by its registration number (IČO).

Args: ico: The company's IČO; spaces and missing leading zeros are fine.

Returns: Name, registered seat, legal form, incorporation date, VAT ID, CZ-NACE activities and the registers the subject is listed in.

ParametersJSON Schema
NameRequiredDescriptionDefault
icoYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, covering the safety profile. The description adds useful behavioral context beyond annotations: the input-tolerant behavior ('spaces and missing leading zeros are fine') and the exact set of returned fields. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, well-structured with Args and Returns sections, and leads with the core purpose. Each sentence adds value, and there is no redundant or filler content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter, read-only lookup tool with a declared output schema, the description is complete. It covers the purpose, the input format tolerance, and the expected return contents. Nothing essential for invoking the tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema provides only a string parameter named 'ico' with no description, so schema coverage is 0%. The description compensates fully by explaining that the parameter is the company's IČO and that spaces and missing leading zeros are acceptable. This gives agents the information needed to normalize user input correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Look up'), a specific resource ('Czech company profile'), and a precise input ('registration number (IČO)'). This clearly differentiates it from sibling tools like validate_ico_number, search_companies, and check_vat_registration, which address different needs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies when to use the tool: when you have a Czech IČO and want a company profile. It does not explicitly exclude alternatives or mention siblings, but the context is unambiguous enough for an agent to choose this tool over the listed siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_companiesA
Read-onlyIdempotent

Find Czech companies by a full or partial business name.

Args: name: The name to search for, e.g. "Renomia". limit: How many results to return (1-50).

Returns: The total number of matches and a list of hits with IČO, name and address.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
limitNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnly and non-destructive behavior, and the description adds useful return details: total matches plus hits with IČO, name, and address. It does not hide side effects or contradict the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence states the core purpose, followed by a compact Args/Returns layout. Every line earns its place without repetition or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter, read-only search tool with an output schema, the description covers the input semantics, limit behavior, and result shape. Nothing essential is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 0% description coverage, but the description compensates fully by defining 'name' with an example and explaining 'limit' with a range (1-50). This is exactly the semantics an agent needs beyond the bare property names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb ('Find'), a specific resource ('Czech companies'), and the matching mode ('full or partial business name'). It is clearly distinct from siblings such as validate_ico_number or lookup_company, which do exact identifier-based lookups rather than name search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies use when the caller has a company name to search, but it does not explicitly state when to prefer this tool over lookup_company or when to use the validation siblings. There are no when-not conditions or alternative routing instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_ico_numberA
Read-only

Validate an IČO offline via its modulo-11 checksum, without calling ARES.

Useful for a quick sanity check on form input or on a number read out of a scanned document, where 0/8 and 1/7 are routinely confused.

Args: ico: A string that is supposed to be an IČO.

Returns: valid plus the normalised eight-digit form when the number is valid.

ParametersJSON Schema
NameRequiredDescriptionDefault
icoYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint=true annotation, the description discloses that validation is offline, based on modulo-11 checksum, and that it returns the normalized eight-digit form when valid. It also hints at the type of input errors it addresses (digit confusion in scanned documents), adding meaningful behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the most important purpose statement, and organized with clear Args/Returns sections. Every sentence adds value: purpose, use cases, parameter definition, and return behavior. No filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's low complexity (one parameter, output schema present, no nested objects), the description fully covers purpose, usage, behavior, and parameter semantics. The return value is summarized even though an output schema exists, and the offline/online distinction is highlighted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description compensates by defining 'ico' as 'A string that is supposed to be an IČO.' This adds semantic meaning beyond the bare string type, although it does not specify length or formatting constraints explicitly—those are mildly implied by the mention of a normalized eight-digit return.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('validate'), a precise resource ('IČO'), and the validation method ('modulo-11 checksum') while explicitly noting it does not call ARES. This distinguishes it from sibling tools like lookup_company and check_vat_registration without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides clear usage context: 'a quick sanity check on form input or on a number read out of a scanned document, where 0/8 and 1/7 are routinely confused.' It does not explicitly name alternatives or state when not to use them, but the offline/no-ARES framing implies a lighter-weight verification than the sibling lookup tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.2.0
    • First observedcheck_vat_registration
    • First observedlookup_company
    • First observedsearch_companies
    • First observedvalidate_ico_number

TDQS

A4.5/5.0

Scored across 4 tools

Disambiguation4/5

The tools are mostly distinct: validate_ico_number handles offline checksum validation, search_companies finds by name, and lookup_company returns a full profile. There is minor overlap between lookup_company and check_vat_registration since both accept an IČO and lookup_company already returns a VAT ID, but the descriptions make the focused VAT-status purpose clear.

Naming Consistency5/5

All tool names follow the same imperative verb_object pattern in snake_case: validate_, lookup_, search_, check_. This makes the tool set highly predictable and easy for an agent to reason about.

Tool Count5/5

Four tools is a well-scoped size for an ARES-focused MCP server. Each tool covers a distinct, useful operation without redundancy or unnecessary bloat.

Completeness5/5

The domain is Czech company registry lookups, and the set covers offline validation, name search, full company profile retrieval, and VAT registration status. There are no important dead ends for common workflows involving finding and validating Czech companies.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides access to the Czech ARES business registry API, enabling search and retrieval of official information about Czech companies, validation of IČO numbers, and filtering by various criteria like legal form, industry codes, and location.
    2
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    MCP servers for Czech government & business data: ARES (Business Register) + ČNB (FX rates). Native AI access to company lookups, VAT status, bank accounts, currency conversion.
    9
    6
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Provides verified UK company lookup and number validation for AI agents using official Companies House data. Enables lookup of registered details by number, validation of company number format, and search by company name.
    3
    35 npm
    Apache 2.0
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for the Czech business registry ARES, enabling company validation, lookup, and due diligence checks directly from AI clients.
    14
    28 npm
    MIT