{
  "markdown": "<!-- SPDX-License-Identifier: Apache-2.0 OR MIT>\n\n<p align=\"center\">\n  <img\n    src=\"https://cloudcdn.pro/pain001/v1/logos/pain001.svg\"\n    alt=\"pain001-mcp logo\"\n    width=\"120\"\n    height=\"120\"\n  />\n</p>\n\n<h1 align=\"center\">pain001-mcp</h1>\n\n<p align=\"center\">\n  <b>Model Context Protocol server exposing the pain001 ISO 20022 payment library as 17 first-class agent tools.</b>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/pain001-mcp/\"><img src=\"https://img.shields.io/pypi/v/pain001-mcp?style=for-the-badge\" alt=\"PyPI version\" /></a>\n  <a href=\"https://pypi.org/project/pain001-mcp/\"><img src=\"https://img.shields.io/pypi/pyversions/pain001-mcp.svg?style=for-the-badge\" alt=\"Python versions\" /></a>\n  <a href=\"https://pypi.org/project/pain001-mcp/\"><img src=\"https://img.shields.io/pypi/dm/pain001-mcp.svg?style=for-the-badge\" alt=\"PyPI downloads\" /></a>\n  <a href=\"https://github.com/sebastienrousseau/pain001-mcp/actions/workflows/ci.yml\"><img src=\"https://img.shields.io/github/actions/workflow/status/sebastienrousseau/pain001-mcp/ci.yml?branch=main&label=Tests&style=for-the-badge\" alt=\"Tests\" /></a>\n  <a href=\"https://github.com/sebastienrousseau/pain001-mcp/actions/workflows/ci.yml\"><img src=\"https://img.shields.io/github/actions/workflow/status/sebastienrousseau/pain001-mcp/ci.yml?branch=main&label=Coverage&style=for-the-badge\" alt=\"Coverage\" /></a>\n  <a href=\"#license\"><img src=\"https://img.shields.io/pypi/l/pain001-mcp?style=for-the-badge\" alt=\"License\" /></a>\n  <a href=\"https://glama.ai/mcp/servers/sebastienrousseau/pain001-mcp\"><img src=\"https://glama.ai/mcp/servers/sebastienrousseau/pain001-mcp/badges/score.svg\" alt=\"Glama MCP server score\" /></a>\n</p>\n\n---\n\n## Contents\n\n**Getting started**\n\n- [What is pain001-mcp?](#what-is-pain001-mcp) — the problem it solves\n- [Install](#install) — PyPI, virtualenv, Docker\n- [Quick start](#quick-start) — register with Claude Desktop in 30 seconds\n\n**Library reference**\n\n- [Tools](#tools) — the 17 tools, one resource, one prompt\n- [Using the tools](#using-the-tools) — call them in-process from Python\n- [The pain001 suite](#the-pain001-suite) — core lib, MCP server, LSP server\n\n**Operational**\n\n- [When not to use pain001-mcp](#when-not-to-use-pain001-mcp) — honest boundaries\n- [Development](#development) — gates, make targets\n- [Security](#security) — sandboxing posture\n- [Documentation](#documentation) — examples, guides\n- [Contributing](#contributing) — how to get changes in\n- [License](#license) — Apache-2.0\n\n---\n\n## What is pain001-mcp?\n\nThe [Model Context Protocol](https://modelcontextprotocol.io) (MCP) is\nan open standard that lets AI agents discover and call external tools in\na uniform way. **pain001-mcp** is the MCP server that turns the\n[`pain001`](https://github.com/sebastienrousseau/pain001) ISO 20022\npayment library into 17 first-class agent tools — so an assistant can\ngenerate and validate **`pain.001` Customer Credit Transfer Initiation**\nand **`pain.008` Customer Direct Debit Initiation** messages (the\nstandardised payment instructions behind SEPA and cross-border credit\ntransfers) directly from a conversation.\n\nEvery tool is a thin, typed wrapper over the `pain001` public API\n(validators, schema loaders, `generate_xml_string`, parsers, the version\nmapper, the ISO 20022 charset sanitiser), so all interfaces behave\nidentically to the CLI, REST API, and in-tree MCP server. Tools return\nJSON-serialisable data; on a validation error they return an\n`{\"error\": ...}` payload rather than raising.\n\n| Concern | How pain001-mcp handles it |\n| :--- | :--- |\n| Transport | stdio (FastMCP default); zero config beyond the client manifest |\n| Schema fidelity | Tools delegate to `pain001`'s XSD-validated generator |\n| Identifier validation | `validate_identifier` checks IBAN (ISO 13616 / mod-97) and BIC |\n| Cross-version mapping | `migrate_records` round-trips data between pain.001.001.03 and .12 |\n| Charset compliance | `sanitize_to_iso20022_charset` transliterates outside-set characters |\n| Message-type aliases | Bare family names `pain.001` / `pain.008` resolve to `pain.001.001.09` / `pain.008.001.02` |\n| Error surface | Failures return structured `{\"error\": ...}`, never tracebacks — listing every missing or invalid field at once |\n\n---\n\n## Install\n\n| Channel | Command | Notes |\n| :--- | :--- | :--- |\n| PyPI | `pip install pain001-mcp` | Pulls in `pain001 >= 0.0.54` + MCP SDK |\n| Source | `git clone https://github.com/sebastienrousseau/pain001-mcp && cd pain001-mcp && poetry install` | For development |\n| Docker (GHCR) | `docker pull ghcr.io/sebastienrousseau/pain001-mcp:latest` | Multi-arch (linux/amd64, linux/arm64); runs `pain001-mcp` over stdio |\n\nRequires Python 3.10 or later. Works on macOS, Linux, and Windows.\n\n<details>\n<summary>Using an isolated virtual environment (recommended)</summary>\n\n```sh\npython -m venv venv\nsource venv/bin/activate        # macOS/Linux\nvenv\\Scripts\\activate           # Windows\npython -m pip install -U pain001-mcp\n```\n\n</details>\n\n---\n\n## Quick start\n\nRegister the server with any MCP client (Claude Desktop shown):\n\n```json\n{\n  \"mcpServers\": {\n    \"pain001\": { \"command\": \"pain001-mcp\" }\n  }\n}\n```\n\nThat's it. Restart the client and the 17 tools are available to the\nagent. To check the server starts cleanly before wiring an editor:\n\n```bash\npain001-mcp --help\n# -> usage: pain001-mcp [-h] ...\n```\n\nThe server speaks LSP-style JSON-RPC over stdin/stdout — it is meant to\nbe launched by an MCP client, not used interactively.\n\n---\n\n## Tools\n\nAll 17 tools delegate to the `pain001` public API, so they behave\nidentically to the CLI and REST API.\n\n- `list_message_types` — List the supported `pain.001` / `pain.008` message types\n- `get_required_fields` — Required input fields for a message type\n- `get_input_schema` — Full input JSON Schema for a message type\n- `inspect_template` — Template metadata + accepted formats for a message type\n- `validate_records` — Validate flat records against a message type\n- `validate_payment_scheme` — Run a scheme rulebook (`sepa-sct`, `sepa-sdd`, `sepa-inst`, `sepa-b2b`, `xborder-ct`)\n- `validate_identifier` — Validate an IBAN or BIC\n- `validate_xml_against_schema` — Validate an XML payload against its bundled XSD without writing to disk\n- `generate_message` — Generate a validated XML message and return the string\n- `generate_message_async` — Async variant of `generate_message` for long batches\n- `generate_message_from_file` — Render directly from a CSV path on disk\n- `list_supported_formats` — List the data formats `pain001` can load (CSV, SQLite, JSON, JSONL, Parquet)\n- `parse_camt053` — Parse a `camt.053` bank statement XML into structured data\n- `parse_pain002` — Parse a `pain.002` payment-status report XML into structured data\n- `migrate_records` — Migrate flat records between pain.001 schema versions\n- `sanitize_to_iso20022_charset` — Transliterate text to the ISO 20022 Latin set\n- `convert_mt101` — Convert a legacy SWIFT MT101 (Request for Transfer) into pain.001 records (one per transaction)\n\nPlus one resource and one prompt:\n\n- Resource `pain001://schema/{message_type}` — Read-only access to the bundled XSD for any supported message type\n- Prompt `build_payment_batch` — Guided multi-step prompt that walks an agent through building a valid batch\n\n### First-try ergonomics\n\nThe generate path is designed so an agent's first natural call succeeds:\n\n- **Records field guide in the tool schema** — the `records` parameter of\n  `generate_message` / `generate_message_async` carries a field-by-field\n  guide in its `inputSchema` description (key fields, accepted aliases,\n  defaults, computed totals), so an agent can build a correct call\n  without a discovery round-trip.\n- **Message-type aliases** — the bare family names `pain.001` and\n  `pain.008` are accepted wherever a `message_type` is, resolving to\n  `pain.001.001.09` and `pain.008.001.02`; an invalid type error lists\n  every accepted value.\n- **validate/generate key coherence** — `validate_records` canonicalizes\n  alias keys (`amount`, `currency`, lower-case IBAN/BIC spellings)\n  exactly as `generate_message` does, so a record that generates cleanly\n  also validates cleanly. Values keep their JSON types; only key names\n  are rewritten.\n- **Structured, complete error payloads** — generation failures return\n  an `{\"error\": ...}` payload (never a traceback) that lists every\n  missing or invalid field at once, with row numbers; XSD failures\n  report each violation as element path plus reason (via\n  `pain001 >= 0.0.54`).\n- **Computed totals and defaults** — `nb_of_txs` / `ctrl_sum` are\n  computed from the records and may be omitted; `payment_method`\n  defaults to `TRF` and `charge_bearer` to `SLEV`. IBAN and BIC values\n  are strictly validated and never coerced.\n\n---\n\n## Using the tools\n\nYou can invoke the tools in-process — without a transport — straight\nthrough the FastMCP instance. This mirrors what an agent receives over\nstdio:\n\n```python\nimport asyncio\n\nfrom pain001_mcp.server import server\n\n# A single flat payment record satisfying pain.001.001.09.\nrecord = [\n    {\n        \"id\": \"MSG-0001\",\n        \"date\": \"2026-01-15T10:30:00\",\n        \"nb_of_txs\": 1,\n        \"ctrl_sum\": 100.00,\n        \"initiator_name\": \"Acme Embedded Finance Ltd\",\n        \"payment_information_id\": \"PMT-INFO-0001\",\n        \"payment_method\": \"TRF\",\n        \"batch_booking\": False,\n        \"service_level_code\": \"SEPA\",\n        \"requested_execution_date\": \"2026-01-20\",\n        \"debtor_name\": \"Acme Embedded Finance Ltd\",\n        \"debtor_account_IBAN\": \"DE89370400440532013000\",\n        \"debtor_agent_BIC\": \"DEUTDEFFXXX\",\n        \"charge_bearer\": \"SLEV\",\n        \"payment_id\": \"PAY-0001\",\n        \"payment_amount\": 100.00,\n        \"currency\": \"EUR\",\n        \"creditor_agent_BIC\": \"NWBKGB2LXXX\",\n        \"creditor_name\": \"National Westminster Bank\",\n        \"creditor_account_IBAN\": \"GB29NWBK60161331926819\",\n        \"remittance_information\": \"Invoice 0001\",\n    }\n]\n\n\nasync def main() -> None:\n    async def call(name, args):\n        result = await server.call_tool(name, args)\n        # mcp 2.x returns a CallToolResult (read .content); 1.x\n        # returns the content list, or a (content, meta) tuple.\n        content = getattr(result, \"content\", None)\n        if content is None:\n            # mcp 2.x returns a CallToolResult (read .content); 1.x\n            # returns the content list, or a (content, meta) tuple.\n            content = getattr(result, \"content\", None)\n            if content is None:\n                content = result[0] if isinstance(result, tuple) else result\n        return content[0].text if content else \"\"\n\n    # 1. Validate an identifier.\n    print(await call(\"validate_identifier\",\n                     {\"kind\": \"iban\", \"value\": \"DE89370400440532013000\"}))\n    # -> {\"kind\": \"iban\", \"value\": \"DE89370400440532013000\", \"valid\": true}\n\n    # 2. Sanitise text to the ISO 20022 Latin set.\n    print(await call(\"sanitize_to_iso20022_charset\",\n                     {\"value\": \"Café Müller\"}))\n    # -> {\"value\": \"Café Müller\", \"sanitised\": \"Cafe Muller\",\n    #     \"was_valid\": false, \"changed\": true}\n\n    # 3. Generate a validated Customer Credit Transfer Initiation.\n    xml = await call(\"generate_message\",\n                     {\"message_type\": \"pain.001.001.09\", \"records\": record})\n    print(xml[:46])\n    # -> <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    #    <Document ...\n\n\nasyncio.run(main())\n```\n\nThe runnable version of this snippet lives in\n[`examples/01_mcp_tools.py`](examples/01_mcp_tools.py). See the\n[`examples/`](examples/) folder for a validation pipeline\n([`02_validate_pipeline.py`](examples/02_validate_pipeline.py)) and a\nbank-reply parser walkthrough\n([`03_parse_bank_replies.py`](examples/03_parse_bank_replies.py)).\n\n---\n\n## The pain001 suite\n\n`pain001-mcp` is part of a set of independently installable packages\nbuilt around the [`pain001`](https://github.com/sebastienrousseau/pain001)\nlibrary — pick whichever ones your stack needs:\n\n| Package | Role |\n| :--- | :--- |\n| [`pain001`](https://pypi.org/project/pain001/) | Core library + CLI + FastAPI REST API |\n| [`pain001-mcp`](https://pypi.org/project/pain001-mcp/) | **MCP server for AI agents (this package)** |\n| [`pain001-lsp`](https://pypi.org/project/pain001-lsp/) | Language Server Protocol server for editors |\n\n```mermaid\nflowchart LR\n    A[\"MCP client<br/>(Claude Desktop, IDE, agent)\"] -->|stdio| B[\"pain001-mcp\"]\n    B -->|delegates to| C[\"pain001\"]\n    C -->|render + validate| D[\"ISO 20022 pain.001 XML\"]\n```\n\n---\n\n## When not to use pain001-mcp\n\n- **You're not driving an MCP-aware agent.** Use the CLI\n  (`pain001 …`) or the REST API (`pain001 serve`) directly — both expose\n  the same surface with less indirection.\n- **You need editor diagnostics, not agent tools.** Use\n  [`pain001-lsp`](https://pypi.org/project/pain001-lsp/) — it speaks\n  the Language Server Protocol to VS Code, Neovim, Helix, Emacs, etc.\n- **You need to extend the tool surface in-tree.** The companion\n  [`pain001[mcp]`](https://github.com/sebastienrousseau/pain001) extra\n  exposes the same FastMCP instance and is easier to fork inside an\n  organisation's pain001 install.\n\n---\n\n## Development\n\n`pain001-mcp` uses [Poetry](https://python-poetry.org/) and\n[mise](https://mise.jdx.dev/).\n\n```bash\ngit clone https://github.com/sebastienrousseau/pain001-mcp.git\ncd pain001-mcp\nmise install\npoetry install\n```\n\nA `Makefile` orchestrates the quality gates (kept in lockstep with CI):\n\n| Target | What it runs |\n| :--- | :--- |\n| `make check` | All gates (REQUIRED before commit) |\n| `make test` | `pytest --cov=pain001_mcp --cov-branch --cov-fail-under=100` |\n| `make lint` | `ruff check` + `black --check` |\n| `make type-check` | `mypy --strict` |\n| `make docs` | `interrogate --fail-under=100` (docstring coverage) |\n\nCurrent state (v0.0.57): **71 tests passing, 100% line + branch\ncoverage** against a 100% enforced floor, mypy `--strict` clean,\ninterrogate 100%.\n\n---\n\n## Security\n\n- **No filesystem writes from tools.** `generate_message` and\n  `generate_message_from_file` return the XML as a string; no tool\n  writes to disk.\n- **XML parsing** of `camt.053` and `pain.002` is routed through\n  `defusedxml` (via the core `pain001` library); XXE and entity\n  expansion are rejected.\n- **Validation failures** are returned as structured `{\"error\": ...}`\n  payloads — never as stack traces — so the agent never sees an\n  internal path leak.\n- **Dependencies** are pinned via `poetry.lock` and audited by\n  `pip-audit` and Bandit in CI.\n\nTo report a vulnerability, please use\n[GitHub private vulnerability reporting](https://github.com/sebastienrousseau/pain001-mcp/security)\nrather than a public issue.\n\n---\n\n## Documentation\n\n- **Runnable examples:** [`examples/`](https://github.com/sebastienrousseau/pain001-mcp/tree/main/examples)\n- **Release history:** [CHANGELOG.md](https://github.com/sebastienrousseau/pain001-mcp/blob/main/CHANGELOG.md)\n- **Core library docs:** [docs.pain001.com](https://docs.pain001.com)\n- **MCP specification:** [modelcontextprotocol.io](https://modelcontextprotocol.io)\n\n---\n\n## Contributing\n\nContributions are welcome — see the\n[contributing instructions](https://github.com/sebastienrousseau/pain001-mcp/blob/main/CONTRIBUTING.md).\nThanks to all the\n[contributors](https://github.com/sebastienrousseau/pain001-mcp/graphs/contributors)\nwho have helped build `pain001-mcp`.\n\n---\n\n## Related MCP Servers\n\nPart of the **ISO 20022 MCP Suite** — open-source, Apache-2.0 licensed MCP servers for banking and financial-services AI agents:\n\n| Server | Purpose |\n|---|---|\n| [`pacs008-mcp`](https://github.com/sebastienrousseau/pacs008-mcp) | Generate, validate, parse & scheme-check ISO 20022 pacs.008 FI-to-FI credit transfers + Nov-2026 address linting |\n| [`camt053-mcp`](https://github.com/sebastienrousseau/camt053-mcp) | Parse & reconcile ISO 20022 camt.053 bank-to-customer statements — CBPR+/HVPS+ ready |\n| [`acmt001-mcp`](https://github.com/sebastienrousseau/acmt001-mcp) | Generate & validate ISO 20022 acmt account-management messages |\n| [`bankstatementparser-mcp`](https://github.com/sebastienrousseau/bankstatementparser-mcp) | Parse bank statements (BAI2, MT940/MT942, CAMT.053, OFX, CSV) into structured transactions |\n| [`noyalib-mcp`](https://github.com/sebastienrousseau/noyalib) | Lossless YAML 1.2 parsing, formatting & validation (Rust, 100% spec compliance) |\n\n---\n\n## MCP Registry\n\n`mcp-name: io.github.sebastienrousseau/pain001-mcp`\n\n---\n\n## License\n\nLicensed under the [Apache License, Version 2.0](https://opensource.org/license/apache-2-0/).\nAny contribution submitted for inclusion shall be licensed as above,\nwithout additional terms.\n\n---\n\n<p align=\"center\">\n  <a href=\"https://pain001.com\">pain001.com</a> ·\n  <a href=\"https://pypi.org/project/pain001-mcp/\">PyPI</a> ·\n  <a href=\"https://github.com/sebastienrousseau/pain001-mcp\">GitHub</a>\n</p>\n",
  "bytes": 16892,
  "sha": "65a00e59fe93a1c8c8518c8fa6002e2df5dca0f7228f769e6086bae687ac3678",
  "repo_slug": "sebastienrousseau/pain001-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_sebastienrousseau_pain001_mcp_e94be79c/readme"
}