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.

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):

{
    "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:

{ "type": "iban", "value": "DE88370400440532013000" }
{
    "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:

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:

{ "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:

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. Prices for every tool are published at /pricing.json and in the tool catalog.

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:

{
    "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 and at /pricing.json.

Sources#