AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified Apache-2.0 Self-run

Pain001

mcp-sebastienrousseau-pain001 · by sebastienrousseau

Generate and validate ISO 20022 payment files (pain.001 v03-v12, pain.008) from CSV, SQLite, JSON, or Parquet. XSD and SEPA scheme validation, pain.002 and camt.053 parsers and builders, plus a CLI, REST API, MCP server, and LSP server for editor diagnostics. 🐍

No reviews yet
0 installs
34 views
0.0% view→install

Install

$ agentstack add mcp-sebastienrousseau-pain001

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-sebastienrousseau-pain001)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pain001? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Pain001

Generate ISO 20022-compliant payment files from CSV, SQLite, JSON, or Parquet data.


Contents

Getting started

  • [What is Pain001?](#what-is-pain001) — the problem it solves and how
  • [Install](#install) — PyPI, extras, Docker, source
  • [Quick start](#quick-start) — one command from CSV to validated XML

Library reference

  • [Supported messages](#supported-messages) — every bundled ISO 20022 message type
  • [Input formats](#input-formats) — CSV, SQLite, JSON, JSONL, Parquet
  • [Usage](#usage) — CLI, scheme validation, dry-run, streaming, REST API, Python API
  • [Companion packages](#companion-packages) — MCP server, Language Server
  • [Production users](#production-users) — who's running it, how to be listed

Operational

  • [When not to use Pain001](#when-not-to-use-pain001) — honest boundaries
  • [Deployment cookbook](docs/deployment-cookbook.md) — copy-pasteable docker-compose with TLS + Redis + Prometheus + Grafana
  • [Development](#development) — gates, make targets, CI matrix
  • [Security](#security) — hardening posture and reporting
  • [Documentation](#documentation) — guides, API reference, examples
  • [Contributing](#contributing) — how to get changes in
  • [License](#license) — dual Apache-2.0 / MIT

What is Pain001?

Banks reject malformed payment files. Pain001 takes the payment data you already have — a CSV export, a SQLite table, a JSON feed, a Parquet file — and turns it into ISO 20022 XML that validates against the official XSD schema before it ever reaches your bank.

It handles the parts that are easy to get wrong:

| Concern | How Pain001 handles it | | :--- | :--- | | Schema compliance | Every file is validated against the official XSD before it is written | | Monetary precision | Amounts flow through decimal.Decimal end to end — no float rounding | | Control totals | NbOfTxs and CtrlSum are computed from the data, never trusted from input | | Template drift | Bundled template/XSD pairs are guard-railed; mismatches fail loudly | | XML attacks | All XML parsing goes through defusedxml — XXE and entity expansion are blocked | | Large batches | Streaming mode chunks input and emits one file per chunk | | Scheme rules | --scheme sepa-sct\|sepa-sdd\|sepa-inst\|sepa-b2b\|xborder-ct layers per-rulebook checks on top of XSD |

Templates and schemas for every supported message type ship inside the package — point Pain001 at your data and it resolves the rest.


Install

| Channel | Command | Notes | | :--- | :--- | :--- | | PyPI | pip install pain001 | Core library and CLI | | PyPI + REST API | pip install "pain001[api]" | Adds FastAPI + Uvicorn server | | PyPI + Parquet | pip install "pain001[parquet]" | Adds PyArrow for Parquet input | | PyPI + Redis | pip install "pain001[redis]" | Distributed job store + rate limiter | | PyPI + MCP | pip install "pain001[mcp]" | In-tree MCP server for LLM clients | | PyPI + LSP | pip install "pain001[lsp]" | In-tree language server for CSV diagnostics | | Source | git clone https://github.com/sebastienrousseau/pain001 && cd pain001 && poetry install | For development | | Docker (GHCR) | docker pull ghcr.io/sebastienrousseau/pain001:latest | Multi-arch (linux/amd64, linux/arm64); CLI + api extra preinstalled |

Requires Python 3.10 or later.

Docker

The image ships the CLI and the api extra so the REST surface works out of the box:

# CLI: generate a payment file
docker run --rm -v "$PWD:/data" -w /data \
  ghcr.io/sebastienrousseau/pain001:latest \
  generate -t pain.001.001.03 -d payments.csv -o out.xml

# REST API: launch the server
docker run --rm -p 8000:8000 \
  ghcr.io/sebastienrousseau/pain001:latest \
  serve --host 0.0.0.0 --port 8000

The image runs as a non-root pain001 user; bind-mount the directory you want the CLI to read or write.


Quick start

-t (message type) and -d (data file) are the only required flags — the template and XSD auto-resolve from the bundled registry:

pain001 -t pain.001.001.03 -d payments.csv
# -> writes pain.001.001.03.xml in the current directory (override with -o)

Override the template or schema only when you need a customised one:

pain001 -t pain.001.001.03 -m my-template.xml -s my-schema.xsd -d payments.csv

Validate without generating anything (CI pre-flight) — here the template and schema are auto-resolved from the bundled registry:

pain001 -t pain.001.001.03 -d payments.csv --dry-run
# -> exit 0 if the data would generate a valid file, 1 otherwise

Exit codes: 0 success, 1 validation or processing error, 2 invalid arguments.

One binary, a whole workflow

pain001 is a command suite. A bare invocation (or pain001 generate …) still produces XML exactly as before — every flag above is unchanged — and the sibling subcommands cover the rest of the lifecycle:

| Command | Purpose | | :--- | :--- | | pain001 generate … | Generate payment XML (default; accepts bare flags for backwards compatibility) | | pain001 validate -t … -d … | Validate data without generating XML — a named --dry-run for CI pre-flight | | pain001 versions [--json] | List the supported ISO 20022 message types | | pain001 inspect [--json] | Show a bundled template's schema, category, and accepted formats | | pain001 init [-o file] | Scaffold a starter CSV from the bundled example | | pain001 serve [--host --port] | Launch the REST API (requires pain001[api]) | | pain001 mcp | Launch the in-tree MCP server over stdio (requires pain001[mcp]) |

pain001 init pain.001.001.03 -o my-payments.csv         # scaffold a starter CSV
pain001 validate -t pain.001.001.03 -d my-payments.csv  # pre-flight in CI
pain001 generate -t pain.001.001.03 -d my-payments.csv  # ship it

Supported messages

| Message type | Description | | :--- | :--- | | pain.001.001.03pain.001.001.12 | Customer Credit Transfer Initiation, all ten ISO 20022 versions | | pain.008.001.02 | Customer Direct Debit Initiation |

Each bundled message type ships with a Jinja2 template, the official XSD schema, and registry metadata. List them from the CLI:

pain001 versions                 # supported message types
pain001 inspect pain.001.001.12  # template + schema + accepted formats

Related tooling included in the package:

  • Version migration — map payment data between pain.001 versions via

pain001.migration.VersionMapper().migrate_rows(rows, from_v, to_v).

  • pain.002 parser + builder — read the payment status reports your bank

sends back, and build_pain002_report(...) to generate one (e.g. to simulate a bank in tests); the two round-trip.

  • camt.053 parser — read end-of-day bank statements.

Input formats

| Format | Extension | Notes | | :--- | :--- | :--- | | CSV | .csv | Header row maps columns to template fields | | SQLite | .db, .sqlite | Reads from a named table you specify (set the table via --config) | | JSON | .json | Array of payment objects | | JSON Lines | .jsonl | One payment object per line | | Parquet | .parquet | Requires the parquet extra |

All loaders normalise into the same internal representation, so the rest of the pipeline — validation, totals, rendering — is identical regardless of source.


Usage

CLI reference

These are the options of the generate command (the default), so they apply equally to pain001 … and pain001 generate …:

pain001 [generate] [OPTIONS]

  -t, --xml-message-type   ISO 20022 message type (e.g. pain.001.001.03)
  -m, --template           Jinja2 XML template (auto-resolved when omitted)
  -s, --schema             XSD schema for validation (auto-resolved when omitted)
  -d, --data               Payment data file (CSV, SQLite, JSON, JSONL, Parquet)
  -c, --config             Configuration file (YAML, TOML, or INI)
  -o, --output-dir         Output directory (default: current directory)
      --dry-run            Validate inputs without generating XML
      --streaming          Process input in chunks, one XML file per chunk
      --chunk-size         Rows per streaming chunk (default: 1000)
      --profile            Configuration profile or built-in preset
      --show-config        Print the resolved configuration and exit
      --emit-metrics       Emit timing and lifecycle metrics to stdout
      --scheme             Validate rows against a scheme rulebook
                           (sepa-sct, sepa-sdd, sepa-inst, sepa-b2b, xborder-ct)
      --explain            With --scheme, print a remediation hint per finding
      --scheme-format      Scheme output format: text (default) or json
  -v, --verbose            Detailed logging output
  -h, --help               Show help and exit

Scheme-aware validation (SEPA + cross-border)

XSD validation proves a file is well-formed; it does not prove the payment obeys the rules of the scheme it will clear through. --scheme layers a rulebook on top of XSD validation and reports structured, per-row violations:

pain001 -t pain.001.001.03 -d payments.csv --scheme sepa-sct --dry-run

Five profiles ship today — sepa-sct (SEPA Credit Transfer, pain.001), sepa-sdd (SEPA Direct Debit, pain.008), sepa-inst (SEPA Instant Credit Transfer, pain.001), sepa-b2b (SEPA Business-to-Business Direct Debit, FRST/RCUR-only + mandatory creditor identifier), and xborder-ct (generic cross-border, multi-currency, BIC-mandatory). Each profile checks currency, valid debtor/creditor IBANs (ISO 13616 / mod-97), BICs, the amount ceiling (100,000 EUR instant cap for sepa-inst), ISO 20022 character-set and field-length limits, and (for SDD/B2B) mandate id and sequence type. Add --explain for remediation hints, or --scheme-format json for machine-readable output. The REST API accepts a scheme field on /api/v1/validate and /api/v1/generate too. See [SCHEMES.md](SCHEMES.md) for the full rule catalogue. From Python:

from pain001 import validate_scheme

rows = [{
    "payment_currency": "USD",                       # not EUR -> SEPA-CCY
    "debtor_account_IBAN": "DE89370400440532013000",
    "creditor_account_IBAN": "FR1420041010050500013M02606",
    "payment_amount": "100.00",
}]

result = validate_scheme(rows, profile="sepa-sct")
print(result.is_valid)             # -> False
for v in result.violations:
    print(v.rule, v.field, v.message)
    # -> SEPA-CCY payment_currency Currency must be EUR for sepa-sct

Need to clean spreadsheet text first? sanitize_to_charset transliterates to the ISO 20022 set (CaféCafe).

Dry-run validation in CI

--dry-run runs the full validation pipeline — file existence, schema resolution, data loading, field checks — and stops before XML generation. It is designed as a pre-flight gate:

pain001 -t pain.001.001.03 -d payments.csv --dry-run || exit 1

Exit code 0 means the data would generate a valid file; 1 means it would not, with the failures printed.

Streaming large batches

For batches too large to hold in memory, streaming mode chunks the input and writes one XML file per chunk, each with its own computed NbOfTxs and CtrlSum:

pain001 -t pain.001.001.03 -d payments.csv --streaming --chunk-size 500

REST API

Install the api extra and start the server:

pip install "pain001[api]"
pain001 serve --host 0.0.0.0 --port 8000   # or: uvicorn pain001.api.app:app

Endpoints are versioned under /api/v1; the unversioned /api/* paths remain as a backwards-compatible alias.

| Method | Endpoint | Purpose | | :--- | :--- | :--- | | GET | /api/v1/health | Liveness check | | POST | /api/v1/validate | Validate payment data without generating | | POST | /api/v1/generate | Generate a payment file synchronously | | POST | /api/v1/generate/async | Queue generation as a background job | | GET | /api/v1/status/{job_id} | Poll an async job | | GET | /api/v1/download/{job_id} | Download a finished file | | DELETE | /api/v1/jobs/{job_id} | Cancel or clean up a job |

Operational controls (all environment-driven, all off by default):

| Variable | Effect | | :--- | :--- | | PAIN001_API_KEY | Require Authorization: Bearer on every endpoint | | PAIN001_RATE_LIMIT | Per-client cap (e.g. 100/minute); pair with PAIN001_RATE_LIMIT_BACKEND=redis for cross-replica enforcement | | PAIN001_RATE_LIMIT_BACKEND | memory (default, in-process) or redis | | PAIN001_RATE_LIMIT_REDIS_URL | Redis URL for the distributed limiter (falls back to PAIN001_JOB_STORE_URL if unset) | | PAIN001_JOB_STORE_DIR | Persist async jobs to disk so they survive restarts | | PAIN001_JOB_STORE_URL | Redis URL for a fully distributed job store (use instead of _DIR) |

Documentation surfaces: Swagger UI at /api/docs, ReDoc at /api/redoc, an interactive Scalar reference at /api/reference, and the raw OpenAPI document at /openapi.json. The same reference is hosted publicly: .

Operability: liveness probe at /api/v1/health and Prometheus metrics at /metrics (build info, supported-type/scheme gauges, per-status job gauges, and HTTP request counters). See [OPERATIONS.md](OPERATIONS.md) for the runbook — config, scrape config, alerts, scaling, and incident playbook.

Client SDKs — generate a typed client in any language from the OpenAPI document:

python scripts/export_openapi.py openapi.json      # dump the schema
npx @openapitools/openapi-generator-cli generate \
    -i openapi.json -g python -o ./pain001-client   # or -g typescript-axios, go, ...

Python API — generate in memory (serverless)

For Lambdas, APIs, and queues, generate_xml_string returns the validated XML as a string instead of writing to disk. This snippet is fully self-contained — it uses the template, schema, and sample data that ship inside the package, so it runs as-is with no external files:

from pain001 import generate_xml_string
from pain001.constants import TEMPLATES_DIR
from pain001.csv.load_csv_data import load_csv_data

message_type = "pain.001.001.03"
bundled = TEMPLATES_DIR / message_type  # templates ship inside the package

# Load the bundled sample dataset; swap in your own list[dict] of rows.
payments = load_csv_data(str(bundled / "template.csv"))

xml = generate_xml_string(
    payments,
    message_type,
    str(bundled / "template.xml"),
    str(bundled / f"{message_type}.xsd"),
)

# `xml` is validated ISO 20022 XML, ready to return from a handler.
print(xml[:38])  # -> 

Python API — generate to a file

process_files loads your data, renders the template, validates against the XSD, and writes the file — returning the path it wrote:

from pain001.core.core import process_files

output_path = process_files(
    xml_message_type="pain.001.001.03",
    xml_template_file_path="template.xml",
    xsd_schema_file_path="schema.xsd",
    data_file_path="payments.csv",  # path, or a list[dict] of payment rows
)

print(output_path)  # -> "pain.001.001.03.xml" — validated and on disk

Companion packages

Pain001 ships two interchangeable install paths for both its MCP and LSP integrations: an in-tree implementation that comes with pain001 itself (smaller feature set, no extra package), and a standalone PyPI package (richer surface, independently versioned).

MCP server

A Model Context Protocol server lets AI agents call Pain001 as first-class tools.

  • In-tree (pip install "pain001[mcp]", run pain001 mcp or

pain001-mcp-builtin): the original server in pain001.mcp.server. Tools include list_supported_versions, inspect_template, generate_payment_file, validate_payment_data, plus a pain001://schema/{message_type} resource and a build_payment_batch prompt.

  • Standalone (pip install pain001-mcp, run pain001-mcp): the

[pain001-mcp](https://gi

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.