pain001-mcp
Model Context Protocol server exposing the pain001 ISO 20022 payment library as 16 first-class agent tools.
Contents
Getting started
- What is pain001-mcp? — the problem it solves
- Install — PyPI, virtualenv, Docker
- Quick start — register with Claude Desktop in 30 seconds
Library reference
- Tools — the 16 tools, one resource, one prompt
- Using the tools — call them in-process from Python
- The pain001 suite — core lib, MCP server, LSP server
Operational
- When not to use pain001-mcp — honest boundaries
- Development — gates, make targets
- Security — sandboxing posture
- Documentation — examples, guides
- Contributing — how to get changes in
- License — Apache-2.0
What is pain001-mcp?
The Model Context Protocol (MCP) isan open standard that lets AI agents discover and call external tools ina uniform way. pain001-mcp is the MCP server that turns thepain001 ISO 20022payment library into 16 first-class agent tools — so an assistant cangenerate and validate pain.001 Customer Credit Transfer Initiationand pain.008 Customer Direct Debit Initiation messages (thestandardised payment instructions behind SEPA and cross-border credittransfers) directly from a conversation.
Every tool is a thin, typed wrapper over the pain001 public API(validators, schema loaders, generate_xml_string, parsers, the versionmapper, the ISO 20022 charset sanitiser), so all interfaces behaveidentically to the CLI, REST API, and in-tree MCP server. Tools returnJSON-serialisable data; on a validation error they return an{"error": ...} payload rather than raising.
| Concern | How pain001-mcp handles it |
|---|---|
| Transport | stdio (FastMCP default); zero config beyond the client manifest |
| Schema fidelity | Tools delegate to pain001's XSD-validated generator |
| Identifier validation | validate_identifier checks IBAN (ISO 13616 / mod-97) and BIC |
| Cross-version mapping | migrate_records round-trips data between pain.001.001.03 and .12 |
| Charset compliance | sanitize_to_iso20022_charset transliterates outside-set characters |
| Error surface | Validation failures return structured {"error": ...}, never tracebacks |
Install
| Channel | Command | Notes |
|---|---|---|
| PyPI | pip install pain001-mcp |
Pulls in pain001 >= 0.0.53 + MCP SDK |
| Source | git clone https://github.com/sebastienrousseau/pain001-mcp && cd pain001-mcp && poetry install |
For development |
| Docker (GHCR) | docker pull ghcr.io/sebastienrousseau/pain001-mcp:latest |
Multi-arch (linux/amd64, linux/arm64); runs pain001-mcp over stdio |
Requires Python 3.10 or later. Works on macOS, Linux, and Windows.
Using an isolated virtual environment (recommended)python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
python -m pip install -U pain001-mcp
Quick start
Register the server with any MCP client (Claude Desktop shown):
{
"mcpServers": {
"pain001": { "command": "pain001-mcp" }
}
}
That's it. Restart the client and the 16 tools are available to theagent. To check the server starts cleanly before wiring an editor:
pain001-mcp --help
# -> usage: pain001-mcp [-h] ...
The server speaks LSP-style JSON-RPC over stdin/stdout — it is meant tobe launched by an MCP client, not used interactively.
Tools
All 16 tools delegate to the pain001 public API, so they behaveidentically to the CLI and REST API.
| Tool | Purpose |
|---|---|
list_supported_versions |
List the supported pain.001 / pain.008 message versions |
get_required_fields |
Required input fields for a message type |
get_input_schema |
Full input JSON Schema for a message type |
inspect_template |
Template metadata + accepted formats for a message type |
validate_records |
Validate flat records against a message type |
validate_payment_data |
Same as above, JSON-RPC-friendly signature |
validate_payment_scheme |
Run a scheme rulebook (sepa-sct, sepa-sdd, sepa-inst, sepa-b2b, xborder-ct) |
validate_identifier |
Validate an IBAN or BIC |
validate_xml_against_schema |
Validate an XML payload against its bundled XSD without writing to disk |
generate_payment_file |
Generate a payment XML file from records + a path |
generate_message |
Generate a validated XML message and return the string |
generate_message_async |
Async variant of generate_message for long batches |
generate_message_from_file |
Render directly from a CSV path on disk |
list_supported_formats |
List the data formats pain001 can load (CSV, SQLite, JSON, JSONL, Parquet) |
parse_camt053 |
Parse a camt.053 bank statement XML into structured data |
parse_pain002 |
Parse a pain.002 payment-status report XML into structured data |
migrate_records |
Migrate flat records between pain.001 schema versions |
sanitize_to_iso20022_charset |
Transliterate text to the ISO 20022 Latin set |
Plus one resource and one prompt:
| Kind | Name | Purpose |
|---|---|---|
| Resource | pain001://schema/{message_type} |
Read-only access to the bundled XSD for any supported message type |
| Prompt | build_payment_batch |
Guided multi-step prompt that walks an agent through building a valid batch |
Using the tools
You can invoke the tools in-process — without a transport — straightthrough the FastMCP instance. This mirrors what an agent receives overstdio:
import asyncio
from pain001_mcp.server import server
# A single flat payment record satisfying pain.001.001.09.
record = [
{
"id": "MSG-0001",
"date": "2026-01-15T10:30:00",
"nb_of_txs": 1,
"ctrl_sum": 100.00,
"initiator_name": "Acme Embedded Finance Ltd",
"payment_information_id": "PMT-INFO-0001",
"payment_method": "TRF",
"batch_booking": False,
"service_level_code": "SEPA",
"requested_execution_date": "2026-01-20",
"debtor_name": "Acme Embedded Finance Ltd",
"debtor_account_IBAN": "DE89370400440532013000",
"debtor_agent_BIC": "DEUTDEFFXXX",
"charge_bearer": "SLEV",
"payment_id": "PAY-0001",
"payment_amount": 100.00,
"currency": "EUR",
"creditor_agent_BIC": "NWBKGB2LXXX",
"creditor_name": "National Westminster Bank",
"creditor_account_IBAN": "GB29NWBK60161331926819",
"remittance_information": "Invoice 0001",
}
]
async def main() -> None:
async def call(name, args):
result = await server.call_tool(name, args)
content = result[0] if isinstance(result, tuple) else result
return content[0].text if content else ""
# 1. Validate an identifier.
print(await call("validate_identifier",
{"kind": "iban", "value": "DE89370400440532013000"}))
# -> {"kind": "iban", "value": "DE89370400440532013000", "valid": true}
# 2. Sanitise text to the ISO 20022 Latin set.
print(await call("sanitize_to_iso20022_charset",
{"value": "Café Müller"}))
# -> {"value": "Café Müller", "sanitised": "Cafe Muller",
# "was_valid": false, "changed": true}
# 3. Generate a validated Customer Credit Transfer Initiation.
xml = await call("generate_message",
{"message_type": "pain.001.001.09", "records": record})
print(xml[:46])
# -> <?xml version="1.0" encoding="UTF-8"?>
# <Document ...
asyncio.run(main())
The runnable version of this snippet lives inexamples/01_mcp_tools.py. See theexamples/ folder for a validation pipeline(02_validate_pipeline.py) and abank-reply parser walkthrough(03_parse_bank_replies.py).
The pain001 suite
pain001-mcp is part of a set of independently installable packagesbuilt around the pain001library — pick whichever ones your stack needs:
| Package | Role |
|---|---|
pain001 |
Core library + CLI + FastAPI REST API |
pain001-mcp |
MCP server for AI agents (this package) |
pain001-lsp |
Language Server Protocol server for editors |
flowchart LR
A["MCP client<br/>(Claude Desktop, IDE, agent)"] -->|stdio| B["pain001-mcp"]
B -->|delegates to| C["pain001"]
C -->|render + validate| D["ISO 20022 pain.001 XML"]
When not to use pain001-mcp
- You're not driving an MCP-aware agent. Use the CLI(
pain001 …) or the REST API (pain001 serve) directly — both exposethe same surface with less indirection. - You need editor diagnostics, not agent tools. Use
pain001-lsp— it speaksthe Language Server Protocol to VS Code, Neovim, Helix, Emacs, etc. - You need to extend the tool surface in-tree. The companion
pain001[mcp]extraexposes the same FastMCP instance and is easier to fork inside anorganisation's pain001 install.
Development
pain001-mcp uses Poetry andmise.
git clone https://github.com/sebastienrousseau/pain001-mcp.git
cd pain001-mcp
mise install
poetry install
A Makefile orchestrates the quality gates (kept in lockstep with CI):
| Target | What it runs |
|---|---|
make check |
All gates (REQUIRED before commit) |
make test |
pytest --cov=pain001_mcp --cov-branch --cov-fail-under=100 |
make lint |
ruff check + black --check |
make type-check |
mypy --strict |
make docs |
interrogate --fail-under=100 (docstring coverage) |
Current state (v0.0.53): 54 tests passing, 100% line + branchcoverage against a 100% enforced floor, mypy --strict clean,interrogate 100%.
Security
- No filesystem writes from tools.
generate_messagereturns theXML as a string; onlygenerate_payment_filewrites, and only to acaller-supplied path. - XML parsing of
camt.053andpain.002is routed throughdefusedxml(via the corepain001library); XXE and entityexpansion are rejected. - Validation failures are returned as structured
{"error": ...}payloads — never as stack traces — so the agent never sees aninternal path leak. - Dependencies are pinned via
poetry.lockand audited bypip-auditand Bandit in CI.
To report a vulnerability, please useGitHub private vulnerability reportingrather than a public issue.
Documentation
- Runnable examples:
examples/ - Release history: CHANGELOG.md
- Core library docs: docs.pain001.com
- MCP specification: modelcontextprotocol.io
Contributing
Contributions are welcome — see thecontributing instructions.Thanks to all thecontributorswho have helped build pain001-mcp.
License
Licensed under the Apache License, Version 2.0.Any contribution submitted for inclusion shall be licensed as above,without additional terms.
pain001.com · PyPI · GitHub