# How to validate IBANs, ISBNs, GTINs, UUIDs and Luhn numbers in AI agents

> Language models guess check digits. How IBAN MOD97, ISBN, GTIN, Luhn and UUID validation works, and how to give your AI agent an exact validator over MCP.

Published 2026-10-04, updated 2026-10-04 by MCP Toolbelt. Canonical URL: https://mcptoolbelt.com/blog/validate-iban-isbn-gtin-uuid-ai-agents

## The short answer

An AI agent should never decide on its own whether an IBAN, ISBN, barcode or UUID is valid. A large language model predicts likely text; it does not reliably run modulo arithmetic over 20 to 34 characters. Give the agent a deterministic validation tool instead and let it act on the result.

MCP Toolbelt's **`identifier_validate`** tool does exactly that. It checks six identifier types (`iban`, `isbn10`, `isbn13`, `gtin`, `luhn` and `uuid`) locally, without network calls. It returns `valid`, the check digit it expected, the check digit it found and a stable error code. Agents call it over MCP (Model Context Protocol) or a plain REST endpoint, one call per identifier or in batches.

- **IBAN:** country length, national BBAN structure from the SWIFT IBAN Registry (release 103, September 2026, all 89 countries) and ISO 7064 MOD97-10.
- **ISBN-10 / ISBN-13:** modulo 11 and the 978/979-prefixed modulo 10 check.
- **GTIN:** GTIN-8 (EAN-8), GTIN-12 (UPC-A), GTIN-13 (EAN-13) and GTIN-14 with the GS1 modulo 10 check digit.
- **Luhn:** the generic mod 10 algorithm used by payment card numbers, IMEIs and many national IDs.
- **UUID:** RFC 9562 format, variant and versions 1 to 8, plus the Nil and Max UUIDs.

## Why language models get check digits wrong

Check digits exist to catch typos: one wrong or swapped digit changes the checksum. That is precisely the kind of exact, character-by-character arithmetic a language model is weakest at.

- **The model pattern-matches instead of calculating.** `DE89370400440532013000` and `DE88370400440532013000` look equally plausible as text. Only the arithmetic tells them apart.
- **MOD97 needs big numbers.** An IBAN is checked as a single number that often runs to more than 30 digits. Models routinely lose or repeat a digit along the way.
- **Models "helpfully" repair input.** Ask a chatbot whether an IBAN is valid and it may quietly suggest a corrected one. In a payment flow, a confidently invented account number is worse than an error.
- **The answer is not reproducible.** Ask twice and you can get two different verdicts. A tool returns the same answer for the same input every time.

The fix is simple: treat validation as a tool call, not a reasoning step. Agents are good at deciding _when_ to validate and _what to do_ with the result. The tool does the arithmetic.

## What identifier_validate checks

| Type     | Identifier                                         | What is checked                                                                                                       |
| -------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `iban`   | International Bank Account Number                  | Two-letter country code, registered national length, BBAN pattern, canonical check digits 02–98 and ISO 7064 MOD97-10 |
| `isbn10` | ISBN-10                                            | Nine digits plus a digit or `X`, weights 10 to 1, sum divisible by 11                                                 |
| `isbn13` | ISBN-13                                            | Thirteen digits, prefix 978 or 979, alternating weights 1 and 3, modulo 10                                            |
| `gtin`   | GTIN-8, GTIN-12 (UPC-A), GTIN-13 (EAN-13), GTIN-14 | Length 8, 12, 13 or 14 and the GS1 modulo 10 check digit                                                              |
| `luhn`   | Card numbers, IMEI and other Luhn identifiers      | 2 to 256 digits and the generic Luhn (mod 10) algorithm                                                               |
| `uuid`   | UUID / GUID                                        | `8-4-4-4-12` hexadecimal form, RFC 9562 variant bits, versions 1 to 8, Nil and Max                                    |

Every check runs on the server in plain code, with a pinned copy of the SWIFT IBAN Registry. There are no lookups, no third-party APIs and no model in the loop. The same input always produces the same output.

## Validate an IBAN with one API call

Send the identifier type and value as JSON. Keep `value` a string so leading zeros survive, and set `normalize: true` to accept the way people usually type IBANs: lowercase with spaces.

```bash
curl -s https://mcptoolbelt.com/v1/tools/identifier_validate \
  -H "Authorization: Bearer $MCP_TOOLBELT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "iban", "value": "de89 3704 0044 0532 0130 00", "normalize": true}'
```

The result (the REST API wraps it in `data.output`):

```json
{
    "type": "iban",
    "normalized": "DE89370400440532013000",
    "valid": true,
    "checksum_valid": true,
    "errors": [],
    "registry_version": "103 (September 2026)",
    "country_code": "DE",
    "expected_check_digit": "89",
    "actual_check_digit": "89"
}
```

`normalized` is the exact string that was checked. With `normalize: true` the tool only removes ASCII spaces and uppercases letters; it never drops, adds or replaces characters.

## Catch a typo without "fixing" it

Change one digit and the IBAN fails the MOD97 check:

```json
{ "type": "iban", "value": "DE88370400440532013000" }
```

```json
{
    "type": "iban",
    "normalized": "DE88370400440532013000",
    "valid": false,
    "checksum_valid": false,
    "errors": [{ "code": "checksum_mismatch", "message": "Check digit does not match the supplied identifier body." }],
    "registry_version": "103 (September 2026)",
    "country_code": "DE",
    "expected_check_digit": "89",
    "actual_check_digit": "88"
}
```

An invalid identifier is a **successful validation**, not an API error: HTTP 200, or `isError: false` over MCP. Your agent branches on `valid` and on the error `code`.

`expected_check_digit` is calculated from the rest of the identifier. It tells you the check digit _would_ be 89 if the other characters are right, but the typo may just as well be in the account number. Ask the user to re-enter the value instead of swapping in the expected digit.

Structural problems are reported before the checksum. A Dutch IBAN with one character missing returns `invalid_length` ("IBAN for NL must contain 18 characters.") and `checksum_valid: null`, because no meaningful checksum can be calculated.

## Use it from an MCP client

Any client that supports remote MCP servers (Claude, Claude Code, Cursor, VS Code with GitHub Copilot, ChatGPT developer mode, Gemini CLI, Codex and others) can connect to MCP Toolbelt with one URL:

```text
https://mcptoolbelt.com/mcp
```

Once connected, `identifier_validate` shows up next to the other tools with its full input and output schema. The model calls it with the same arguments as the REST API:

```json
{ "type": "isbn13", "value": "978-0-306-40615-7", "normalize": true }
```

and receives a structured result (`structuredContent`) it can act on directly. To make sure the model actually uses the tool, add a few lines to your system prompt or agent instructions:

```text
Never decide yourself whether an IBAN, ISBN, GTIN, card number or UUID is valid.
Call identifier_validate and act on its "valid" field and error "code".
Never correct an identifier yourself; ask the user to check it instead.
```

Agents without an account can register themselves through [/auth.md](https://mcptoolbelt.com/auth.md). Prices for every tool are published at [/pricing.json](https://mcptoolbelt.com/pricing.json) and in the [tool catalog](https://mcptoolbelt.com/tools).

## Validate many identifiers in one batch

Cleaning a CSV of bank details or a product feed? When batching is enabled for the tool, send many argument sets to `/v1/tools/identifier_validate/batch`:

```json
{
    "inputs": [
        { "type": "gtin", "value": "4006381333931" },
        { "type": "gtin", "value": "036000291452" },
        { "type": "luhn", "value": "4111 1111 1111 1111", "normalize": true }
    ]
}
```

Results come back in the same order in `data.output.results`. An invalid identifier does not abort the batch; only malformed arguments (a missing field, a number instead of a string, an unknown type) reject the batch before anything runs or is charged. Send an `Idempotency-Key` header so retries are never charged twice.

## How the check digits work

For reference, and for anyone who wants to check the tool's answers by hand. These are the worked examples the tool reproduces exactly.

### IBAN: ISO 7064 MOD97-10

1. Check the country code, total length and BBAN pattern against the SWIFT IBAN Registry. `DE` IBANs are 22 characters: `DE`, two check digits and 18 digits.
2. Move the first four characters to the end: `370400440532013000DE89`.
3. Replace each letter with two digits (A = 10, B = 11, … Z = 35): `370400440532013000131489`.
4. The remainder of that number divided by 97 must be 1.

The tool reduces the number one digit at a time, so it never needs big-integer or floating-point maths. It also requires the canonical check digits 02–98, so `00`, `01` and `99` variants that happen to pass MOD97 are rejected.

### ISBN-13 and GTIN: alternating weights, modulo 10

For ISBN `978-0-306-40615-7`, multiply the first twelve digits alternately by 1 and 3:

`9·1 + 7·3 + 8·1 + 0·3 + 3·1 + 0·3 + 6·1 + 4·3 + 0·1 + 6·3 + 1·1 + 5·3 = 93`

The check digit brings the total to the next multiple of ten: 100 − 93 = **7**. GTINs (EAN-13, UPC-A, GTIN-8, GTIN-14) use the same GS1 rule, with weights 3 and 1 counted from the right. EAN-13 `4006381333931` and UPC-A `036000291452` both pass.

### ISBN-10: modulo 11

Weight the nine body digits 10 down to 2 and the check digit 1; the total must be divisible by 11. A check value of 10 is written as `X`. `0-306-40615-2` is the ISBN-10 of the same book as above.

### Luhn: double every second digit

Starting from the rightmost digit before the check digit, double every second digit and subtract 9 from results above 9. Add everything, including the check digit; the total must end in 0. For `79927398713` the total is 70, so it is valid. The tool checks the generic algorithm only: it does not know card issuers or card lengths.

### UUID: format, variant and version

UUIDs have no checksum. The tool checks the `8-4-4-4-12` hexadecimal format, that the variant bits are `10xx` (the first character of the fourth group is 8, 9, a or b) and that the version is 1 to 8. `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` is a valid version 1 UUID; change the `a` in `a765` to `c` and the result is `invalid_variant`.

## What validation does not prove

A passing checksum means the identifier is well formed. It does **not** mean:

- the bank account exists, is open or belongs to the person who gave it to you;
- a book or product has actually been registered with that ISBN or GTIN;
- a card number is issued, active or yours to charge;
- a UUID is unique in your database.

IBAN validation also does not cover domestic bank checksums or bank and branch registration. Treat `valid: true` as "safe to pass on to the next step", not as proof of ownership.

## Error codes your agent can branch on

| Code                  | Meaning                                                         |
| --------------------- | --------------------------------------------------------------- |
| `invalid_format`      | Wrong characters or shape, including empty input                |
| `invalid_length`      | Wrong length for the type, or for the IBAN's country            |
| `unsupported_country` | The IBAN country code is not in the SWIFT registry              |
| `invalid_bban`        | The IBAN's national part does not match the country's structure |
| `invalid_prefix`      | An ISBN-13 that does not start with 978 or 979                  |
| `invalid_variant`     | A UUID without the RFC 9562 variant bits                        |
| `invalid_version`     | A UUID version outside 1 to 8                                   |
| `checksum_mismatch`   | Well formed, but the check digit is wrong                       |

Each result contains at most one error: the first rule the identifier broke.

## Frequently asked questions

### Can ChatGPT or Claude validate an IBAN on their own?

Not reliably. A language model can describe the MOD97 algorithm, but it often makes arithmetic slips on 20 to 34 character numbers and may invent a "corrected" IBAN. Connect a validation tool such as `identifier_validate` over MCP and let the model call it instead.

### Is a valid IBAN proof that the bank account exists?

No. IBAN validation proves the country code, length, national structure and check digits are consistent. Only the bank, or a payment scheme service such as a confirmation-of-payee check, can confirm that the account exists and who holds it.

### Which countries does the IBAN check support?

All 89 national formats in SWIFT IBAN Registry release 103 (September 2026). Every IBAN result reports the `registry_version` it was checked against. A country code outside the registry returns `unsupported_country`.

### Does the tool accept IBANs and ISBNs with spaces or hyphens?

Yes, with `"normalize": true`. IBANs then have ASCII spaces removed and letters uppercased; ISBNs have spaces and hyphens removed; GTIN and Luhn values have spaces and hyphens removed. Without `normalize`, the value is checked exactly as sent.

### Will the tool correct an invalid identifier?

No. It reports the expected and actual check digit, but never replaces a character. The error may be anywhere in the identifier, so the safe action is to ask for the value again.

### Does validation send my data to a third party?

No. All checks run inside MCP Toolbelt with a pinned registry snapshot; the tool makes no network calls.

### What does a validation cost?

Each identifier is one call unit, and an invalid result is billed like a valid one. Calls that fail because of malformed arguments are not charged. Current prices and any free monthly calls are listed in the [tool catalog](https://mcptoolbelt.com/tools) and at [/pricing.json](https://mcptoolbelt.com/pricing.json).

## Sources

- [SWIFT IBAN Registry](https://www.swift.com/swift-resource/9606/download), release 103 (September 2026): national IBAN lengths and BBAN structures.
- [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562.html): Universally Unique IDentifiers (UUIDs), format, variant and versions.
- [International ISBN Agency](https://www.isbn-international.org/index.php/content/what-isbn/10): ISBN structure and prefixes; [RFC 3187](https://www.rfc-editor.org/rfc/rfc3187.html): the ISBN-10 modulo 11 check.
- [GS1 check digit calculator](https://www.gs1.org/services/how-calculate-check-digit-manually): GTIN lengths and weights.
- [Model Context Protocol](https://modelcontextprotocol.io/): the open standard agents use to discover and call tools.
