Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

0.0.16 - 2026-10-27

Python 3.11 floor and release-evidence release, in lockstep with pacs008 0.0.16. Python 3.10 is no longer supported (see Removed).

Changed

  • pacs008 >= 0.0.16, < 1 and pacs008-loader-mt103 >= 0.0.16, < 1: the suite members of the same release, so an older core or loader and the advisory floors they declare cannot resolve under this one. The hash-pinned requirements/ trees are re-locked against them.

  • The GitHub release is titled pacs008-mcp X.Y.Z (the project name and the bare version), not the tag.

  • validate_xml answers a document whose date has a year past what the validator can represent with {"message_type": ..., "is_valid": false}, not an {"error": "refused XML: OverflowError(...)"} payload: pacs008 0.0.16 reports that date as invalid instead of letting elementpath’s OverflowError escape, so the tool no longer catches it.

Security

  • The release SBOMs (CycloneDX and the pip-licenses manifest) describe the shipped runtime set: the hash-pinned requirements/docker.txt the image installs, plus the package as a local wheel, in a venv of their own. poetry install had resolved afresh (no poetry.lock is committed here), and poetry run pointed the SBOM tools at their own interpreter, so the SBOM listed the tooling, not the runtime set.

  • The MCP Inspector conformance job pins @modelcontextprotocol/inspector to 2.10.1, so a new npm publish cannot change what CI runs.

Removed

  • Python 3.10 is no longer supported: the floor is 3.11 (python = ">=3.11,<4.0"), in step with the pacs008 core, as 3.10 reaches end of life in October 2026. CI tests 3.11 to 3.14; ruff, black and mypy target 3.11. The tomli fallback in scripts/check_suite_consistency.py and tests/test_package_version.py goes, with its dev dependency, and requirements/ci.txt and sbom.txt are compiled for 3.11, dropping their 3.10-only lines (exceptiongroup, toml, tomli in sbom.txt, and the older httpx2, httpcore2, hypothesis, starlette, websockets, platformdirs and rpds-py pins).

0.0.15 - 2026-10-24

Security

  • mcp is constrained to >=1.30.0,!=2.0.*,!=2.1.*,<3, so no release affected by GHSA-84m7-p3x7-pcfv (unbounded streamable HTTP session accumulation: mcp >=1.8.0,<1.30.0 and >=2.0.0,<2.2.0) can resolve. The previous >=1.2,<3 admitted them.

  • Security floors on mcp’s transitive stack, matching pain001-mcp: anyio >= 4.14.2, httpx2 >= 2.12.0, httpcore2 >= 2.10.0, pyjwt[crypto] >= 2.15.0, opentelemetry-api >= 1.45.1 (GHSA-xcq7-p3pf-p88h) and certifi >= 2026.10.19 (PYSEC-2026-4320).

  • The release toolchain (requirements/build.txt, installed only by the release workflow on Python 3.12) floors filelock >= 4.2.1 (GHSA-9vhv-jxww-xfm5, GHSA-567q-5qvw-pf92, GHSA-v4x8-3wr5-3wq8); it had pinned 4.0.10. That release needs Python 3.11, so the tree is compiled for 3.12.

  • The same tree floors virtualenv >= 21.14.13 (GHSA-cx6f-vjhg-g3cc: from 21.14.9, the bash activator leaves --prompt unescaped in PS1); it had pinned 21.14.11.

Fixed

  • validate_xml answers a document that declares DTD entities (XXE, billion laughs) with an {"error": "refused XML: EntitiesForbidden(...)"} payload. defusedxml’s refusal used to escape the tool, so the client saw only an opaque “Error executing tool validate_xml”.

  • validate_xml answers a date whose year is past what the validator can represent (for example <CreDtTm>99999999999-01-15T10:30:00</CreDtTm>) with an {"error": "refused XML: OverflowError('year overflow')"} payload. elementpath’s OverflowError used to escape the tool; the Atheris job found it.

  • scripts/check_suite_consistency.py compares this tree with the published pacs008-mcp, not with the pacs008 core it was copied from, so the scheduled Suite Consistency job no longer reports a never-released bump that does not exist.

Added

  • Coverage-guided fuzzing: fuzz/fuzz_tools.py (Atheris) feeds arbitrary text to validate_xml, parse_message and convert_mt103, run by the Fuzz workflow on every change and for 30 minutes weekly; tests/test_untrusted_text.py holds the fixed and property-based cases of the same contract.

  • A complexity gate in the lint job: scripts/check_complexity.py (cyclomatic, Halstead, function and file length) and complexipy (cognitive), each against a committed baseline of the offenders that predate it, which may only shrink.

  • docs/POLICIES.md, the toolchain policy the README’s Python badge links to.

Changed

  • Requires pacs008 >= 0.0.15, < 1 and pacs008-loader-mt103 >= 0.0.15, < 1 (were >= 0.0.10 and >= 0.0.1): the suite releases in lockstep, and the loader’s 0.0.15 parses SWIFT block 4 in linear time (GHSA-4f6g-425r-ff5p). The hash-pinned trees move both from 0.0.12 to 0.0.15.

  • Every pip install in CI, the docs build and the container image is hash-pinned: requirements/ci.txt, docs.txt, fuzz.txt and docker.txt. The package goes on top with --no-deps --no-build-isolation, so its build backend (poetry-core) comes from those trees too instead of an unpinned isolated build; where the install is not editable (the docs build, the MCP Inspector job and the image), a wheel is built first and installed with --no-deps.

  • The container image runs Python 3.14 (python:3.14-slim, pinned by digest) and drops the PACS008_PIP_SPEC build argument and the git client it needed; the runtime dependencies come from requirements/docker.txt.

  • GitHub Actions updated and pinned by commit SHA: checkout 7.0.1, setup-python 7.0.0, setup-node 7.1.0, upload-artifact 7.0.2, attest-build-provenance 4.2.2, gh-action-pypi-publish 1.14.3, sbom-action 0.24.3, scorecard-action 2.5.0, codeql-action 4.39.0.

  • Development tools: ruff >=0.1,<0.18, mypy >=1.11,<3.0; the release tooling floors virtualenv>=21.14.8.

  • The README badge row follows the portfolio template: Build, Registry, Docs, OpenSSF Scorecard, License and Python floor, then Glama.

  • scripts/verify_versions.py checks each manifest in its own helper; the output and exit codes are unchanged.

  • CITATION.cff and SECURITY.md carry the current version again (both had been left at 0.0.12).

0.0.14 - 2026-10-04

Changed

  • Standardize license section heading in README to License for Glama directory and registry matcher consistency.

  • Update locked dependencies to resolve transitive vulnerabilities.

0.0.13 - 2026-10-04

Added

  • Added framework adapters (as_langchain_tools, as_crewai_tools, as_llamaindex_tools) in pacs008_mcp.adapters for seamless agent integration.

  • Added animated terminal demo (.github/demo.gif) and .github/demo.tape with make demo target.

  • Added normalized dual-licensing structure with LICENSES/Apache-2.0.txt and LICENSES/MIT.txt.

  • Mutation testing of the tool handlers (mutmut 3): make mutate and the Mutants of the tool handlers workflow run every mutant of pacs008_mcp against the fast handler tests and fail below the floor in the Makefile (90%; measured 98.6%, 479 of 486 checked mutants, on 2026-09-19).

  • Property-based tests (tests/test_properties.py, Hypothesis) over the invariants the handlers keep for any input their schemas admit: error envelopes always carry a non-empty error, every result is JSON-serialisable, report summaries agree with their rows, the BIC normaliser’s parts re-join to the code, and the repaired address is itself a valid classify_address input. Deterministic, with a trimmed mutation profile for the mutant runs.

  • A docstring gate: interrogate at 100% over the package, run by make doc-coverage and the lint job.

  • A rendered documentation site (Sphinx, Furo, MyST) at https://sebastienrousseau.github.io/pacs008-mcp/: README, API reference, benchmarks, ADRs, roadmap and changelog. Built with warnings as errors on every pull request and deployed from main.

  • tests/test_examples.py executes examples/mcp_tools.py, so the example fails the build instead of rotting.

  • ADR 0002 records why tools are registered explicitly.

Fixed

  • validate_addresses returned the library’s ValueError as an exception when a row’s address column broke a field rule (an over-length bldg_nb, say) instead of the {"error": ...} envelope its docstring promises. Found by the property tests.

  • --transport streamable-http and --transport sse, with --host and --port. Streamable HTTP serves both current protocol revisions (2026-07-28 stateless with server/discover, and 2025-11-25 with the initialize handshake) on one endpoint and streams responses as server-sent events; sse serves the older HTTP+SSE transport. stdio stays the default and is unchanged. --version prints the version. ADR 0001 records the decision.

Changed

  • Tools, resources and the prompt are registered in one explicit block before main() instead of with decorators: mutmut never mutates a decorated function, so the decorator form kept every handler outside mutation testing. The catalogue a client sees is unchanged (dumped and diffed before and after; ADR 0002).

  • The server is built through a small compatibility shim so it runs on both supported majors of the mcp SDK: 2.x (MCPServer, the 2026-07-28 stateless revision with server/discover) and 1.x (FastMCP). The dependency range is now mcp>=1.2,<3, so a fresh install gets 2.x and speaks the current protocol revision.

0.0.12 - 2026-08-29

Aligns the pacs008 suite on one version number, and adds the gates this repository was missing.

Added

  • benches/bench_tool_dispatch.py measures what an agent waits for: the dispatch floor, the payment pipeline across batch sizes, and the address tools. Two results are worth having on the record — validation costs about seven times what generation does, and address screening is roughly 155× cheaper per item than record validation, which is what makes a million-address cutover check a twelve-second job rather than a scheduled one.

  • docs/benchmarks.md with the measured tables.

  • scripts/check_suite_consistency.py and a scheduled Suite Consistency workflow comparing this tree, and every published member, against PyPI.

  • tests/test_suite_conformance.py, the shared suite conformance gate.

  • SECURITY.md, written for an MCP server: stdio has no authentication of its own, tool inputs arrive from a model rather than someone who read the docs, and verify_bic_online is the one tool that egresses.

Changed

  • Version aligned to 0.0.12 across pacs008, pacs008-mcp and pacs008-loader-mt103, which had drifted to 0.0.11, 0.0.9 and 0.0.3.

0.0.9 - 2026-08-28

Swift deferred the CBPR+ structured-address start date; the descriptions a model reads still named it as in force.

Fixed

  • The address_policy parameter description, the classify_address docstring and the workflow guidance all said the CBPR+ structured-address rule takes force on 14 November 2026. Swift deferred that on 27 August 2026 and confirms replacement timing by December at the latest. These strings are what an assistant reads to decide what to tell a user, so a stale date here is repeated to whoever asked as though it were authoritative.

  • The descriptions now say the deferral is Swift’s and covers CBPR+; HVPS+, T2 RTGS, CHAPS, Lynx and Fedwire set their own timing and are unaffected.

Unchanged

  • No tool behaviour changes. Descriptions only.

0.0.8 - 2026-08-16

Release the work that accumulated on main after v0.0.7 was tagged, and take the pacs008 release that makes the cryptography floor resolvable.

Fixed

  • pacs008 is now >=0.0.9. Both 0.0.7 and 0.0.8 of pacs008 cap cryptography<50.0.0, so requiring the patched cryptography 50.0.0 against them is not merely unsatisfied — it is unresolvable:

    ERROR: Cannot install cryptography>=50.0.0, pacs008==0.0.7 and
    pacs008==0.0.8 because these package versions have conflicting
    dependencies.
    
  • cryptography is floored at >=50.0.0. It arrives transitively via mcp -> pyjwt[crypto] and nothing constrained it, so a resolver could pick a version in the range GHSA flags as high severity.

Added

  • Online BIC directory verification tool (#10).

  • MCP prompts and resources for Trinity parity (#9).

  • tests/test_package_version.py, tying __version__ to pyproject.toml and the newest CHANGELOG.md heading. Its absence is why 0.0.7 shipped without a changelog entry and why the cryptography bump in #12 — whose commit message says (v0.0.7) — landed after the v0.0.7 tag and was never published.

0.0.7 - 2026-07-30

Backfilled: this version was released and tagged, but no changelog entry was written at the time.

Changed

  • Required pacs008>=0.0.7 (was >=0.0.5) for the 0.0.7 validation bug fix.

0.0.6 - 2026-07-26

Added

  • PEP 561 py.typed marker (pacs008_mcp/py.typed), wired into the Poetry include list and verified present in the built wheel. The server is mypy --strict clean but previously shipped no marker, so downstream consumers received none of its annotations.

  • Regression test (tests/test_py_typed_marker.py) that fails before a release ships if the marker is dropped from source or packaging.

Changed

  • Version 0.0.5 → 0.0.6.

0.0.5 - 2026-07-18

Changed

  • chore(deps): require pacs008>=0.0.7 (was >=0.0.5) to pick up the 0.0.7 validation bug fix. No tool signatures or behaviour change in this server.

0.0.4 - 2026-07-12

Added

  • convert_mt103 tool — convert a legacy SWIFT MT103 (single customer credit transfer) into pacs.008-ready flat records that can be fed straight into validate_records / generate_message. This is the SWIFT MT-to-MX migration path (correspondent-banking MT103 coexistence with ISO 20022 ends November 2025). The tool is a thin wrapper over the pacs008-loader-mt103 library’s parse_mt103; it does no file I/O and returns {"message_type": "pacs.008.001.08", "records": [...]}, or an {"error": ...} payload on a missing-mandatory-field / malformed MT103.

Changed

  • The MCP server now exposes 15 tools (was 14).

  • Added a runtime dependency on pacs008-loader-mt103 (>=0.0.1).

0.0.3 - 2026-07-12

Security

  • Require pacs008 >= 0.0.5, which relaxes the cryptography constraint to <49.0.0 (patched >=48.0.1), clearing the inherited Dependabot advisory.

0.0.2 - 2026-07-12

Added

  • Four November 2026 structured-address cliff tools, each a thin wrapper over the pacs008 library’s standards.address module. From 14 November 2026, fully unstructured postal addresses are rejected across SWIFT CBPR+, HVPS+, T2 RTGS, CHAPS, Fedwire and Lynx; these tools let agents get ahead of the deadline:

    • classify_address — classify a postal address as structured / hybrid / unstructured (wraps PostalAddress.classify and its is_* predicates)

    • validate_address — validate one address against an address policy, defaulting to the 14 November 2026 cliff rule (wraps PostalAddress.validate)

    • repair_address — country-aware upgrade of legacy unstructured address lines toward hybrid/structured form (wraps from_unstructured)

    • validate_addresses — batch-validate every party address across payment rows (wraps validate_addresses)

  • policy value-constraint enum on the address tools, surfaced as JSON Schema enum metadata derived from the library’s AddressPolicy enum (unstructured_ok, hybrid_or_structured, structured_only), defaulting to hybrid_or_structured — the cliff policy.

Changed

  • The MCP server now exposes 14 tools (was 10).

  • Bumped the minimum pacs008 dependency to >=0.0.4 — the first published PyPI release whose standards.address module backs the new tools.

0.0.1 - 2026-07-11

Added

  • Initial release of pacs008-mcp, a Model Context Protocol (MCP) server that exposes the pacs008 ISO 20022 FI-to-FI Customer Credit Transfer library as tools for AI agents and assistants.

  • pacs008-mcp console script that runs the FastMCP server over stdio.

  • Ten MCP tools, each a thin wrapper over the pacs008 library so they behave identically to the CLI and REST API:

    • list_message_types — list the 20 supported pacs message types with names

    • list_schemes — list the registered scheme / usage-guideline profiles

    • get_scheme — inspect a scheme profile’s rules

    • get_required_fields — required input fields for a message type

    • get_input_schema — full input JSON Schema for a message type

    • validate_records — validate flat records against a message type’s schema

    • validate_scheme — validate records against a scheme’s usage guidelines

    • generate_message — generate a validated pacs XML message

    • validate_xml — validate a raw XML string against the bundled XSD

    • parse_message — parse & classify an inbound ISO 20022 message

  • Value-constraint enums on closed-set tool parameters (message_type, scheme), surfaced as JSON Schema enum metadata derived from the pacs008 library’s own constants and profile registry so accepted values never drift.

  • MCP tool annotations (readOnlyHint/idempotentHint/…) and tool titles for richer client/Glama introspection.

  • Graceful error handling: tools return an {"error": ...} payload on failure rather than raising.

  • 100% statement + branch test coverage, enforced inline via --cov-fail-under=100.

  • Python 3.10+ support; depends on pacs008 (>=0.0.1) and mcp (>=1.2).

  • Runnable example (examples/mcp_tools.py) invoking the tools in-process.

  • glama.json, server.json (MCP Registry metadata), and a Dockerfile for directory listing, registry publication, and container deployment.