pactus-mcp 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. pactus_mcp-1.0.0/.gitattributes +6 -0
  2. pactus_mcp-1.0.0/.github/dependabot.yml +40 -0
  3. pactus_mcp-1.0.0/.github/workflows/ci.yml +150 -0
  4. pactus_mcp-1.0.0/.github/workflows/release.yml +96 -0
  5. pactus_mcp-1.0.0/.gitignore +17 -0
  6. pactus_mcp-1.0.0/.python-version +1 -0
  7. pactus_mcp-1.0.0/.xsdata.xml +18 -0
  8. pactus_mcp-1.0.0/LICENSE +21 -0
  9. pactus_mcp-1.0.0/PKG-INFO +264 -0
  10. pactus_mcp-1.0.0/README.md +238 -0
  11. pactus_mcp-1.0.0/SECURITY.md +34 -0
  12. pactus_mcp-1.0.0/architecture.md +24 -0
  13. pactus_mcp-1.0.0/pyproject.toml +98 -0
  14. pactus_mcp-1.0.0/scripts/generate_models.py +71 -0
  15. pactus_mcp-1.0.0/src/pactus/__init__.py +1 -0
  16. pactus_mcp-1.0.0/src/pactus/core/__init__.py +1 -0
  17. pactus_mcp-1.0.0/src/pactus/core/domain/__init__.py +26 -0
  18. pactus_mcp-1.0.0/src/pactus/core/domain/camt053.py +173 -0
  19. pactus_mcp-1.0.0/src/pactus/core/domain/common.py +103 -0
  20. pactus_mcp-1.0.0/src/pactus/core/domain/pacs002.py +126 -0
  21. pactus_mcp-1.0.0/src/pactus/core/domain/pacs008.py +99 -0
  22. pactus_mcp-1.0.0/src/pactus/core/domain/pain001.py +136 -0
  23. pactus_mcp-1.0.0/src/pactus/core/parsers.py +515 -0
  24. pactus_mcp-1.0.0/src/pactus/generated/.gitkeep +0 -0
  25. pactus_mcp-1.0.0/src/pactus/generated/GENERATED_HASHES.txt +5 -0
  26. pactus_mcp-1.0.0/src/pactus/generated/__init__.py +1031 -0
  27. pactus_mcp-1.0.0/src/pactus/generated/camt_053_001_08.py +6228 -0
  28. pactus_mcp-1.0.0/src/pactus/generated/pacs_002_001_10.py +3322 -0
  29. pactus_mcp-1.0.0/src/pactus/generated/pacs_008_001_08.py +3302 -0
  30. pactus_mcp-1.0.0/src/pactus/generated/pain_001_001_09.py +3293 -0
  31. pactus_mcp-1.0.0/src/pactus/mcp_server.py +170 -0
  32. pactus_mcp-1.0.0/src/pactus/py.typed +0 -0
  33. pactus_mcp-1.0.0/src/pactus/schemas/SCHEMAS.md +76 -0
  34. pactus_mcp-1.0.0/src/pactus/schemas/camt.053.001.08.xsd +2070 -0
  35. pactus_mcp-1.0.0/src/pactus/schemas/pacs.002.001.10.xsd +1127 -0
  36. pactus_mcp-1.0.0/src/pactus/schemas/pacs.008.001.08.xsd +1120 -0
  37. pactus_mcp-1.0.0/src/pactus/schemas/pain.001.001.09.xsd +1114 -0
  38. pactus_mcp-1.0.0/tests/__init__.py +0 -0
  39. pactus_mcp-1.0.0/tests/conftest.py +75 -0
  40. pactus_mcp-1.0.0/tests/fixtures/test_camt053.xml +102 -0
  41. pactus_mcp-1.0.0/tests/fixtures/test_camt053_balances.xml +34 -0
  42. pactus_mcp-1.0.0/tests/fixtures/test_camt053_batched.xml +58 -0
  43. pactus_mcp-1.0.0/tests/fixtures/test_camt053_single.xml +55 -0
  44. pactus_mcp-1.0.0/tests/fixtures/test_mt103.txt +14 -0
  45. pactus_mcp-1.0.0/tests/fixtures/test_pacs002_multi.xml +30 -0
  46. pactus_mcp-1.0.0/tests/fixtures/test_pacs002_rejected.xml +33 -0
  47. pactus_mcp-1.0.0/tests/fixtures/test_pacs002_single.xml +21 -0
  48. pactus_mcp-1.0.0/tests/fixtures/test_pacs008.xml +38 -0
  49. pactus_mcp-1.0.0/tests/fixtures/test_pacs008_multi.xml +88 -0
  50. pactus_mcp-1.0.0/tests/fixtures/test_pain001.xml +78 -0
  51. pactus_mcp-1.0.0/tests/fixtures/test_pain001_minimal.xml +26 -0
  52. pactus_mcp-1.0.0/tests/fixtures/test_pain001_multi_pmt_inf.xml +65 -0
  53. pactus_mcp-1.0.0/tests/fixtures/test_pain001_single.xml +71 -0
  54. pactus_mcp-1.0.0/tests/test_camt053_parser.py +237 -0
  55. pactus_mcp-1.0.0/tests/test_mcp_integration.py +163 -0
  56. pactus_mcp-1.0.0/tests/test_pacs002_parser.py +122 -0
  57. pactus_mcp-1.0.0/tests/test_pacs008_parser.py +237 -0
  58. pactus_mcp-1.0.0/tests/test_pacs008_security.py +85 -0
  59. pactus_mcp-1.0.0/tests/test_pain001_parser.py +141 -0
  60. pactus_mcp-1.0.0/uv.lock +2606 -0
@@ -0,0 +1,6 @@
1
+ * text=auto eol=lf
2
+ *.xsd binary
3
+ *.xml text eol=lf
4
+ *.py text eol=lf
5
+ *.toml text eol=lf
6
+ *.md text eol=lf
@@ -0,0 +1,40 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies via uv (Dependabot reads pyproject.toml + uv.lock natively)
4
+ - package-ecosystem: "uv"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ time: "07:00"
10
+ timezone: "Europe/Kyiv"
11
+ open-pull-requests-limit: 5
12
+ labels:
13
+ - "dependencies"
14
+ - "python"
15
+ commit-message:
16
+ prefix: "deps"
17
+ include: "scope"
18
+ groups:
19
+ # Group all dev-tooling updates into one PR per week
20
+ dev-tooling:
21
+ patterns:
22
+ - "ruff"
23
+ - "mypy"
24
+ - "pytest*"
25
+
26
+ # GitHub Actions — keeps the SHAs we pinned from rotting
27
+ - package-ecosystem: "github-actions"
28
+ directory: "/"
29
+ schedule:
30
+ interval: "weekly"
31
+ day: "monday"
32
+ time: "07:00"
33
+ timezone: "Europe/Kyiv"
34
+ open-pull-requests-limit: 5
35
+ labels:
36
+ - "dependencies"
37
+ - "github-actions"
38
+ commit-message:
39
+ prefix: "ci"
40
+ include: "scope"
@@ -0,0 +1,150 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ quality:
19
+ name: Lint, type-check, tests
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
24
+ with:
25
+ persist-credentials: false
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
29
+ with:
30
+ enable-cache: true
31
+
32
+ - name: Install Python 3.12
33
+ run: uv python install 3.12
34
+
35
+ - name: Check lockfile is in sync with pyproject.toml
36
+ run: uv lock --check
37
+
38
+ - name: Sync dependencies (including [semantic] extras)
39
+ run: uv sync --frozen --all-extras
40
+
41
+ - name: Generate xsdata Pydantic models
42
+ run: uv run python scripts/generate_models.py
43
+
44
+ - name: Verify generated models match canonical hashes
45
+ working-directory: src/pactus/generated
46
+ run: sha256sum -c GENERATED_HASHES.txt
47
+
48
+ - name: Ruff lint
49
+ run: uv run ruff check
50
+
51
+ - name: Ruff format check
52
+ run: uv run ruff format --check
53
+
54
+ - name: Mypy strict
55
+ run: uv run mypy
56
+
57
+ - name: Run pytest with coverage gate
58
+ run: uv run pytest -q
59
+
60
+ sbom:
61
+ name: SBOM generation
62
+ runs-on: ubuntu-latest
63
+ permissions:
64
+ contents: read
65
+ steps:
66
+ - name: Checkout
67
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
68
+ with:
69
+ persist-credentials: false
70
+
71
+ - name: Generate SBOM (CycloneDX 1.6)
72
+ uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
73
+ with:
74
+ format: cyclonedx-json
75
+ output-file: pactus-mcp.cdx.json
76
+ upload-artifact: false
77
+
78
+ - name: Generate SBOM (SPDX 3.0.1)
79
+ uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
80
+ with:
81
+ format: spdx-json
82
+ output-file: pactus-mcp.spdx.json
83
+ upload-artifact: false
84
+
85
+ - name: Upload SBOM artifacts
86
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
87
+ with:
88
+ name: sbom
89
+ path: |
90
+ pactus-mcp.cdx.json
91
+ pactus-mcp.spdx.json
92
+
93
+ vuln-scan:
94
+ name: Grype vulnerability scan
95
+ needs: sbom
96
+ runs-on: ubuntu-latest
97
+ permissions:
98
+ contents: read
99
+ steps:
100
+ - name: Download SBOM artifact
101
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
102
+ with:
103
+ name: sbom
104
+
105
+ # Grype scans the full SBOM including dev dependencies; there is no
106
+ # mechanism to exclude dev-only packages at this layer. A finding in a
107
+ # dev dep (e.g. pytest, ruff) will fail this job.
108
+ - name: Grype scan (fail on high+)
109
+ uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
110
+ with:
111
+ sbom: pactus-mcp.cdx.json
112
+ severity-cutoff: high
113
+ fail-build: true
114
+
115
+ workflow-audit:
116
+ name: Workflow audit (zizmor)
117
+ runs-on: ubuntu-latest
118
+ permissions:
119
+ contents: read
120
+ steps:
121
+ - name: Checkout
122
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
123
+ with:
124
+ persist-credentials: false
125
+
126
+ - name: Install zizmor
127
+ run: pipx install zizmor
128
+
129
+ - name: Verify zizmor version
130
+ run: zizmor --version
131
+
132
+ - name: Audit workflows
133
+ run: zizmor .github/workflows/
134
+
135
+ dependency-review:
136
+ name: Dependency review
137
+ if: github.event_name == 'pull_request'
138
+ runs-on: ubuntu-latest
139
+ permissions:
140
+ contents: read
141
+ steps:
142
+ - name: Checkout
143
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
144
+ with:
145
+ persist-credentials: false
146
+
147
+ - name: Dependency review
148
+ uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
149
+ with:
150
+ fail-on-severity: high
@@ -0,0 +1,96 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+ inputs:
9
+ target:
10
+ description: 'Publish target'
11
+ required: true
12
+ type: choice
13
+ options:
14
+ - testpypi
15
+ - pypi
16
+
17
+ # Deny all permissions at the workflow level; each job grants only what it needs.
18
+ permissions: {}
19
+
20
+ jobs:
21
+ build:
22
+ name: Build distribution
23
+ runs-on: ubuntu-latest
24
+ permissions:
25
+ contents: read
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29
+ with:
30
+ persist-credentials: false
31
+
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
34
+ with:
35
+ enable-cache: false
36
+
37
+ - name: Install Python 3.12
38
+ run: uv python install 3.12
39
+
40
+ - name: Build
41
+ run: uv build
42
+
43
+ - name: Upload dist artifact
44
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ publish-testpypi:
50
+ name: Publish to TestPyPI
51
+ needs: build
52
+ if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
53
+ runs-on: ubuntu-latest
54
+ environment: testpypi
55
+ permissions:
56
+ contents: read
57
+ id-token: write
58
+ steps:
59
+ - name: Download dist artifact
60
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+
65
+ - name: Publish to TestPyPI
66
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
67
+ with:
68
+ repository-url: https://test.pypi.org/legacy/
69
+
70
+ publish-pypi:
71
+ name: Publish to PyPI
72
+ needs: build
73
+ if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
74
+ runs-on: ubuntu-latest
75
+ environment: pypi
76
+ permissions:
77
+ contents: write
78
+ id-token: write
79
+ steps:
80
+ - name: Download dist artifact
81
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
82
+ with:
83
+ name: dist
84
+ path: dist/
85
+
86
+ # id-token: write above enables OIDC; pypa/gh-action-pypi-publish generates
87
+ # Sigstore attestations automatically when that permission is set.
88
+ - name: Publish to PyPI
89
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
90
+
91
+ - name: Create GitHub Release
92
+ if: github.event_name == 'push'
93
+ env:
94
+ GH_TOKEN: ${{ github.token }}
95
+ TAG: ${{ github.ref_name }}
96
+ run: gh release create "$TAG" dist/* --title "$TAG"
@@ -0,0 +1,17 @@
1
+ .idea/
2
+ .env
3
+ *.env
4
+ build/
5
+ .claude/settings.local.json
6
+ .venv/
7
+ __pycache__/
8
+ *.pyc
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+ .ty_cache/
13
+ .coverage
14
+ htmlcov/
15
+ legacy/
16
+ dist/
17
+ *.egg-info/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Config xmlns="http://pypi.org/project/xsdata">
3
+ <Output maxLineLength="100" subscriptableTypes="true" unionType="true">
4
+ <Package>pactus.generated</Package>
5
+ <Format repr="true" eq="true" order="false" unsafeHash="false" frozen="false" slots="false" kwOnly="false">pydantic</Format>
6
+ <Structure>filenames</Structure>
7
+ <DocstringStyle>reStructuredText</DocstringStyle>
8
+ <RelativeImports>true</RelativeImports>
9
+ <CompoundFields defaultName="choice"/>
10
+ </Output>
11
+ <Conventions>
12
+ <ClassName case="pascalCase" safePrefix="type"/>
13
+ <FieldName case="snakeCase" safePrefix="value"/>
14
+ <ConstantName case="screamingSnakeCase" safePrefix="value"/>
15
+ <ModuleName case="snakeCase" safePrefix="mod"/>
16
+ <PackageName case="snakeCase" safePrefix="pkg"/>
17
+ </Conventions>
18
+ </Config>
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Denis Karlinsky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: pactus-mcp
3
+ Version: 1.0.0
4
+ Summary: Production-grade MCP server for ISO 20022 payment message processing
5
+ Project-URL: Repository, https://github.com/deniskarlinsky/iso20022-mcp
6
+ Project-URL: Documentation, https://github.com/deniskarlinsky/iso20022-mcp#readme
7
+ Author: Denis Karlinsky
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: camt053,fintech,iso20022,mcp,pacs008,pain001,payments,swift
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Financial and Insurance Industry
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Office/Business :: Financial
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: fastmcp>=2.0
18
+ Requires-Dist: lxml>=5.0
19
+ Requires-Dist: pydantic>=2.6
20
+ Requires-Dist: xsdata-pydantic>=24.5
21
+ Requires-Dist: xsdata[cli,lxml]>=26.2
22
+ Provides-Extra: semantic
23
+ Requires-Dist: numpy>=1.26; extra == 'semantic'
24
+ Requires-Dist: sentence-transformers>=3.0; extra == 'semantic'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Pactus
28
+
29
+ [![CI](https://github.com/deniskarlinsky/iso20022-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/deniskarlinsky/iso20022-mcp/actions/workflows/ci.yml)
30
+
31
+ Pactus is an MCP server for parsing ISO 20022 payment messages directly from chat. It exposes five tools that let AI assistants inspect `pacs.008`, `pacs.002`, `pain.001`, and `camt.053` messages — the message types at the centre of the CBPR+ migration — without leaving the conversation. It is aimed at developers and bank-integration teams who need to read, debug, or explain ISO 20022 traffic during the transition away from MT messages.
32
+
33
+ ## Status
34
+
35
+ Stable. Version 1.0.0 is on PyPI.
36
+
37
+ ```bash
38
+ pip install pactus-mcp
39
+ # or
40
+ uv add pactus-mcp
41
+ ```
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ git clone https://github.com/deniskarlinsky/iso20022-mcp
47
+ cd iso20022-mcp
48
+ uv sync
49
+ uv run pytest
50
+ ```
51
+
52
+ ## Connecting to Claude Desktop
53
+
54
+ Add Pactus to your Claude Desktop config:
55
+
56
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
57
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
58
+ - **Linux:** `~/.config/Claude/claude_desktop_config.json`
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "pactus": {
64
+ "command": "uv",
65
+ "args": ["run", "--directory", "/absolute/path/to/iso20022-mcp", "pactus-mcp"]
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ Restart Claude Desktop. The Pactus tools will appear in the tools menu.
72
+
73
+ ## Available tools
74
+
75
+ | Tool | Message type | Purpose |
76
+ |---|---|---|
77
+ | `ping` | — | Health check; returns service name and version. |
78
+ | `parse_pacs008` | `pacs.008.001.08` | Parse a FI-to-FI Customer Credit Transfer. |
79
+ | `parse_pacs002` | `pacs.002.001.10` | Parse a FI-to-FI Payment Status Report. |
80
+ | `parse_pain001` | `pain.001.001.09` | Parse a Customer Credit Transfer Initiation. |
81
+ | `parse_camt053` | `camt.053.001.08` | Parse a Bank-to-Customer Account Statement. |
82
+
83
+ All four parse tools return a structured Pydantic model on success, or `{"error": "..."}` on failure. Errors never raise; the agent can explain what went wrong.
84
+
85
+ ## Tool reference
86
+
87
+ ### parse_pacs008
88
+
89
+ `pacs.008` is the primary interbank credit transfer message and the mandatory format for all CBPR+ cross-border traffic from November 2025. It carries one or more credit transfer instructions between financial institutions, each with settlement amount, charge bearer, debtor, and creditor agents.
90
+
91
+ <details>
92
+ <summary>Example response</summary>
93
+
94
+ ```json
95
+ {
96
+ "group_header": {
97
+ "message_id": "MSG20240508001",
98
+ "creation_datetime": "2024-05-08T10:00:00",
99
+ "number_of_transactions": 1,
100
+ "settlement_method": "CLRG"
101
+ },
102
+ "transactions": [
103
+ {
104
+ "end_to_end_id": "E2E20240508001",
105
+ "transaction_id": "TX20240508001",
106
+ "settlement_amount": {"value": "1000.00", "currency": "USD"},
107
+ "charge_bearer": "SHAR",
108
+ "debtor": {"name": "Acme Corporation"},
109
+ "debtor_agent": {"bic": "CHASUS33"},
110
+ "creditor": {"name": "Global Supplies Ltd"},
111
+ "creditor_agent": {"bic": "DEUTDEDB"}
112
+ }
113
+ ]
114
+ }
115
+ ```
116
+
117
+ </details>
118
+
119
+ ### parse_pacs002
120
+
121
+ `pacs.002` is the status report sent in response to a `pacs.008`. It reports whether each transaction was accepted, rejected, or is in an intermediate state. Rejections carry one or more structured reason codes (e.g. `AC01` incorrect account, `AG01` transaction forbidden) that explain the outcome.
122
+
123
+ <details>
124
+ <summary>Example response</summary>
125
+
126
+ ```json
127
+ {
128
+ "group_header": {
129
+ "message_id": "STS20260510001",
130
+ "creation_datetime": "2026-05-10T14:30:00"
131
+ },
132
+ "original_group_info": {
133
+ "original_message_id": "MSG20240508001",
134
+ "original_message_name_id": "pacs.008.001.08",
135
+ "original_creation_datetime": "2024-05-08T10:00:00",
136
+ "group_status": "ACSC"
137
+ },
138
+ "transaction_statuses": [
139
+ {
140
+ "original_end_to_end_id": "E2E-001",
141
+ "original_transaction_id": "TX-001",
142
+ "status": "ACSC",
143
+ "status_reasons": [],
144
+ "acceptance_datetime": "2026-05-10T14:29:45"
145
+ }
146
+ ]
147
+ }
148
+ ```
149
+
150
+ </details>
151
+
152
+ ### parse_pain001
153
+
154
+ `pain.001` is the initiating message in a credit transfer flow, sent by a corporate or customer to their bank. It groups transactions into one or more `PaymentInformation` batches that share a debtor account, execution date, and service level. The LLM sees the full hierarchy: group header → batches → transactions.
155
+
156
+ <details>
157
+ <summary>Example response</summary>
158
+
159
+ ```json
160
+ {
161
+ "group_header": {
162
+ "message_id": "PAIN20260510-001",
163
+ "creation_datetime": "2026-05-10T09:00:00",
164
+ "number_of_transactions": 1,
165
+ "control_sum": "1500.00",
166
+ "initiating_party_name": "ACME Corp"
167
+ },
168
+ "payment_informations": [
169
+ {
170
+ "payment_information_id": "BATCH-2026-05-10-A",
171
+ "payment_method": "TRF",
172
+ "requested_execution_date": "2026-05-12",
173
+ "debtor": {"name": "ACME Corp"},
174
+ "debtor_account_iban": "DE89370400440532013000",
175
+ "debtor_agent": {"bic": "DEUTDEFFXXX"},
176
+ "charge_bearer": "SLEV",
177
+ "service_level_code": "SEPA",
178
+ "transactions": [
179
+ {
180
+ "end_to_end_id": "E2E-PAIN-001",
181
+ "amount": {"value": "1500.00", "currency": "EUR"},
182
+ "creditor": {"name": "Acme Supplier SARL"},
183
+ "creditor_account_iban": "FR1420041010050500013M02606",
184
+ "remittance_info": ["Invoice 2026-0042"]
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ }
190
+ ```
191
+
192
+ </details>
193
+
194
+ ### parse_camt053
195
+
196
+ `camt.053` is the structured account statement sent by a bank to its customer. It reports opening and closing balances and individual debit/credit entries for a period, each optionally broken down to individual transaction details. It is the primary source for automated bank reconciliation.
197
+
198
+ <details>
199
+ <summary>Example response</summary>
200
+
201
+ ```json
202
+ {
203
+ "group_header": {
204
+ "message_id": "CAMT053-SINGLE-001",
205
+ "creation_datetime": "2026-05-10T08:00:00"
206
+ },
207
+ "statements": [
208
+ {
209
+ "statement_id": "STMT-2026-05-09-001",
210
+ "account_iban": "DE89370400440532013000",
211
+ "account_currency": "EUR",
212
+ "balances": [
213
+ {
214
+ "type_code": "OPBD",
215
+ "amount": {"value": "10000.00", "currency": "EUR"},
216
+ "credit_debit": "CRDT",
217
+ "balance_date": "2026-05-09"
218
+ }
219
+ ],
220
+ "entries": [
221
+ {
222
+ "entry_ref": "NTRY-001",
223
+ "amount": {"value": "1500.00", "currency": "EUR"},
224
+ "credit_debit": "DBIT",
225
+ "status": "BOOK",
226
+ "booking_date": "2026-05-09",
227
+ "bank_tx_domain": "PMNT",
228
+ "bank_tx_family": "ICDT",
229
+ "bank_tx_subfamily": "ESCT"
230
+ }
231
+ ]
232
+ }
233
+ ]
234
+ }
235
+ ```
236
+
237
+ </details>
238
+
239
+ ## Security model
240
+
241
+ - **XXE and entity-expansion hardening:** All four parsers reject input containing `<!DOCTYPE>` or `<!ENTITY>` declarations before any XML parsing occurs. This blocks XXE file-read, SSRF, and billion-laughs DoS patterns.
242
+ - **Build integrity:** Generated xsdata models are SHA-256 verified in CI on every commit (`sha256sum -c GENERATED_HASHES.txt`). Regeneration is reproducible from the vendored XSDs.
243
+ - **Supply chain:** GitHub Actions workflows use SHA-pinned action references (commit-hash `@` pins, not mutable tags).
244
+ - **Distribution integrity:** PyPI artifacts are published via OIDC Trusted Publishing and signed with Sigstore. SBOMs (CycloneDX and SPDX) are generated and vulnerability-scanned on every CI run; release artifacts include the SBOM.
245
+ - **Vulnerability disclosure:** See [SECURITY.md](SECURITY.md).
246
+
247
+ ## Architecture
248
+
249
+ Pactus uses a hexagonal layout: `pactus/core/` is pure business logic with no MCP awareness, `mcp_server.py` is a thin FastMCP wrapper, and the generated xsdata models live in `pactus/generated/` and never escape `parsers.py`. See [architecture.md](architecture.md) for the diagram.
250
+
251
+ ## Development
252
+
253
+ ```bash
254
+ uv run ruff check
255
+ uv run ruff format
256
+ uv run mypy
257
+ uv run pytest
258
+ ```
259
+
260
+ The project targets `mypy --strict`.
261
+
262
+ ## License
263
+
264
+ MIT