Read-only MCP server that brings FirmaUY signature inspection to your AI assistant. Offline, no smart card, no signing, redacted by default.
Project description
firmauy-mcp-inspect
firmauy-mcp-inspect is a small, read-only MCP server that
lets an AI assistant inspect documents signed with a Uruguayan cédula de identidad using
FirmaUY. It verifies signatures (PDF/PAdES, XAdES XML,
or a detached CMS .p7s) and validates cédula check digits. Verification runs offline and does not
require a smart card.
It exists for the one task where an assistant genuinely beats the bare CLI, triaging a batch of signed documents in plain language. Point it at a folder and it verifies every file, groups the results by issuing CA, and flags anything that is not VALID or whose certificate chain is not trusted, leaving you a summary to act on.
By design it only ever inspects. It cannot sign, never sees a PIN, and redacts the signer's personal data by default, so identities stay out of the model's context.
Scope (inspection only, by design)
FirmaUY can also sign with the national ID card and read the cardholder's data. This server deliberately exposes neither of them.
- No signing. A signature with a national ID is a legally significant act that needs the physical card and a PIN. A model must never be able to trigger it. Signing stays a human, manual action.
- No identity or photo.
fetch-identityandfetch-photoreturn personal and biometric data. That must not flow into a model's context, so those commands are not exposed.
What the model sees from verification is redacted by default. The model receives the indication, the trust status, and the issuer (a public CA), but not the signer's name or document number.
Tools
| Tool | What it does |
|---|---|
verify(path, original=None, redact=True) |
Verify one signed file (PDF/PAdES, XAdES XML, detached CMS/.p7s). Returns the indication, per-signature trust and checks. |
verify_batch(paths, redact=True) |
Verify many files. Returns a summary count by indication plus a compact per-file result (indication, trusted, issuing CA). |
validate_ci(number) |
Validate a cédula's check digit (arithmetic consistency only, not an identity check). |
doctor() |
Report the local setup status (PC/SC, PKCS#11 module, card, bundled CAs). |
verify_batch handles self-contained signatures (PDF/PAdES, XAdES XML). A detached .p7s needs its
original file, so verify those one at a time with verify(path, original=...).
Verification is offline, with certificate-chain validation up to the Uruguayan national root. A
VALID result is a technical assessment, not a statement of legal validity. For authoritative
verification, use AGESIC's official validator. This is an independent, unofficial tool,
not affiliated with or endorsed by AGESIC.
Requirements
The firmauy CLI must be installed and on PATH.
uv tool install firmauy
firmauy --version
(Override the executable with the FIRMAUY_BIN environment variable if it lives elsewhere.)
Configuration
All are optional and configured through environment variables.
| Variable | Default | Purpose |
|---|---|---|
FIRMAUY_BIN |
(found on PATH) |
Path to the firmauy executable, if it is not on PATH. |
FIRMAUY_MCP_TIMEOUT |
60 |
Per-call timeout, in seconds, for each firmauy invocation. |
FIRMAUY_MCP_MAX_WORKERS |
8 |
Max concurrent verifications in verify_batch. Set to 1 for sequential. |
FIRMAUY_MCP_ALLOWED_ROOTS |
none (no limit) | Directories the tools may read from, separated by the OS path separator (: on Linux/macOS, ; on Windows). When set, any path outside them (after resolving symlinks and ..) is refused. |
FIRMAUY_MCP_ALLOWED_EXTENSIONS |
none (no limit) | Comma-separated types allowed for the signed file, e.g. .pdf,.xml,.p7s. Does not restrict a detached .p7s's original. |
A malformed numeric override is ignored (it falls back to the default) rather than failing startup.
Sandboxing (recommended)
This server hands file paths from the model to firmauy, which opens them. Confining what it can
read matters for a tool that touches national-ID signatures, so set both allowlists. They are
opt-in and fail closed.
export FIRMAUY_MCP_ALLOWED_ROOTS="/srv/inbox/signed"
export FIRMAUY_MCP_ALLOWED_EXTENSIONS=".pdf,.xml,.p7s"
Containment is checked on the resolved, canonical path (symlinks and .. followed), so it is not
fooled by traversal, symlink escapes, or sibling directories sharing a name prefix. The root
allowlist also covers the original of a detached .p7s. The extension allowlist does not, because
that original is arbitrary content.
Install
uv tool install firmauy-mcp-inspect
This puts the firmauy-mcp-inspect command on your PATH, which starts the server over stdio. You
can also run it without installing, straight from PyPI, with uvx firmauy-mcp-inspect.
Try it interactively with the MCP Inspector.
npx @modelcontextprotocol/inspector firmauy-mcp-inspect
To work on it from a clone instead, run uv sync and then uv run firmauy-mcp-inspect.
Docker
A prebuilt image is published to the GitHub Container Registry on every release, so you can run
the server without installing Python or the firmauy CLI.
docker pull ghcr.io/carlosplanchon/firmauy-mcp-inspect:latest
Mount a directory with signed documents and pass any configuration via environment variables:
docker run -i --rm \
-v /srv/inbox:/srv/inbox:ro \
-e FIRMAUY_MCP_ALLOWED_ROOTS=/srv/inbox \
-e FIRMAUY_MCP_ALLOWED_EXTENSIONS=.pdf,.xml,.p7s \
ghcr.io/carlosplanchon/firmauy-mcp-inspect:latest
Paths you ask the tools to inspect must exist inside the container, so mount the documents
(read-only via :ro, since the server only ever reads them, at the same path on both sides) and point
FIRMAUY_MCP_ALLOWED_ROOTS at the container path. The container speaks the MCP stdio transport
(hence -i), so let your MCP client manage it.
Claude Code
claude mcp add firmauy-inspect -- \
docker run -i --rm \
-v /srv/inbox:/srv/inbox:ro \
-e FIRMAUY_MCP_ALLOWED_ROOTS=/srv/inbox \
-e FIRMAUY_MCP_ALLOWED_EXTENSIONS=.pdf,.xml,.p7s \
ghcr.io/carlosplanchon/firmauy-mcp-inspect:latest
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"firmauy-inspect": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/srv/inbox:/srv/inbox:ro",
"-e", "FIRMAUY_MCP_ALLOWED_ROOTS=/srv/inbox",
"-e", "FIRMAUY_MCP_ALLOWED_EXTENSIONS=.pdf,.xml,.p7s",
"ghcr.io/carlosplanchon/firmauy-mcp-inspect:latest"
]
}
}
}
To build the image yourself instead (for example from a clone), a Dockerfile is included:
docker build -t firmauy-mcp-inspect .
Configure your client
Both forms below assume firmauy-mcp-inspect is on your PATH from uv tool install. If a GUI
client cannot find it, use uvx as the command ("command": "uvx", "args": ["firmauy-mcp-inspect"])
or give the absolute path.
Claude Code
claude mcp add firmauy-inspect -- firmauy-mcp-inspect
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"firmauy-inspect": {
"command": "firmauy-mcp-inspect"
}
}
}
Then ask it things such as "Verify every signed PDF in this folder, group them by issuing CA, and flag anything that is not VALID or whose chain is not trusted."
Tests
uv run pytest
The tests mock the firmauy subprocess, so they need neither the CLI nor a card. The
path-sandboxing tests additionally create temporary files and a symlink on the local filesystem.
License
Apache-2.0.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file firmauy_mcp_inspect-0.1.6.tar.gz.
File metadata
- Download URL: firmauy_mcp_inspect-0.1.6.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00d5fd13f6e5905a76182c0657972bb7fdb79ca64cd6ef5c41a682b6c2e4892d
|
|
| MD5 |
5be48cc77bd3179f2a8ba9a14954a0dd
|
|
| BLAKE2b-256 |
c1ec992b8d3dd10fe18d34e198186be4c4d53a874135e2d4bb586244b98ed8e9
|
Provenance
The following attestation bundles were made for firmauy_mcp_inspect-0.1.6.tar.gz:
Publisher:
release.yml on carlosplanchon/firmauy-mcp-inspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firmauy_mcp_inspect-0.1.6.tar.gz -
Subject digest:
00d5fd13f6e5905a76182c0657972bb7fdb79ca64cd6ef5c41a682b6c2e4892d - Sigstore transparency entry: 2050644774
- Sigstore integration time:
-
Permalink:
carlosplanchon/firmauy-mcp-inspect@1b9cd8353ba3ed5740aeeb30859309bd6628305c -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/carlosplanchon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1b9cd8353ba3ed5740aeeb30859309bd6628305c -
Trigger Event:
push
-
Statement type:
File details
Details for the file firmauy_mcp_inspect-0.1.6-py3-none-any.whl.
File metadata
- Download URL: firmauy_mcp_inspect-0.1.6-py3-none-any.whl
- Upload date:
- Size: 13.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e0985314b82cc66d2644d0422d06e1785625823e97e98929b14ab53f93de4fe
|
|
| MD5 |
b1a33f146e662833e199164ffbed910d
|
|
| BLAKE2b-256 |
30106289b596adb5f2c0047629a8854527813ee2241eac895b59189a0fbc95c5
|
Provenance
The following attestation bundles were made for firmauy_mcp_inspect-0.1.6-py3-none-any.whl:
Publisher:
release.yml on carlosplanchon/firmauy-mcp-inspect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firmauy_mcp_inspect-0.1.6-py3-none-any.whl -
Subject digest:
7e0985314b82cc66d2644d0422d06e1785625823e97e98929b14ab53f93de4fe - Sigstore transparency entry: 2050645260
- Sigstore integration time:
-
Permalink:
carlosplanchon/firmauy-mcp-inspect@1b9cd8353ba3ed5740aeeb30859309bd6628305c -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/carlosplanchon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1b9cd8353ba3ed5740aeeb30859309bd6628305c -
Trigger Event:
push
-
Statement type: