Epic: MCP server exposing AutoFirma sign/verify to agents (study) #28

Closed
opened 2026-10-14 16:06:16 +02:00 by EduarD3V · 18 comments
Owner

Goal

Let coding agents sign documents via this app instead of desktop AutoFirma. STUDY-FIRST: produce docs/MCP-DESIGN.md with a recommendation, then implement the recommended slice.

Study questions

  1. Surface: MCP server wrapping the existing sign engine — but engine is browser/WebCrypto today. Options:
    a) Reuse the TS engine under Node (WebCrypto via node:crypto webcrypto — PKCS12 parse + CMS/XAdES/PAdES are pure TS; PAdES byte-range + pdf-lib work in Node too)
    b) Headless-browser harness (playwright drives the SPA) — heavier, but zero engine changes
    Recommend one with effort estimate + risk. Read web/src/lib/ (now repo root after #26 (restructure) — adapt paths) to assess Node compatibility per module.
  2. Certificates: how does an agent get a cert? Options: user preloads .p12 into server store (encrypted at rest), or pass-through per-call (worse). Recommend + document security posture.
  3. Transport: stdio (local agent, e.g. this machine) vs Streamable HTTP (remote agents, homelab). Recommend stdio-first.
  4. Tool surface (proposed):
    • autofirma_sign(file_b64|path, format: cades|xades|pades, cert_id, options) → signed artifact
    • autofirma_verify(artifact_b64) → validity report (reuse verify module)
    • autofirma_list_certs() → stored cert metadata (no key material)
  5. Threat model: file path escapes, unbounded sizes, cert/key handling, audit log of signatures.

Acceptance

  • docs/MCP-DESIGN.md with recommendation + rejected alternatives
  • if (a) recommended and low-risk: minimal MCP server (mcp/ dir, stdio, official @modelcontextprotocol/sdk) implementing sign(cades)+verify+list_certs, with vitest coverage
  • e2e check: script signs a fixture via the server and verifies output
  • NO server-side key persistence unless encrypted; document key lifecycle

Independent of UI epics; owns mcp/ + docs/MCP-DESIGN.md only.

## Goal Let coding agents sign documents via this app instead of desktop AutoFirma. **STUDY-FIRST**: produce `docs/MCP-DESIGN.md` with a recommendation, then implement the recommended slice. ## Study questions 1. Surface: MCP **server** wrapping the existing sign engine — but engine is browser/WebCrypto today. Options: a) Reuse the TS engine under Node (WebCrypto via node:crypto webcrypto — PKCS12 parse + CMS/XAdES/PAdES are pure TS; PAdES byte-range + pdf-lib work in Node too) b) Headless-browser harness (playwright drives the SPA) — heavier, but zero engine changes Recommend one with effort estimate + risk. Read `web/src/lib/` (now repo root after #26 (restructure) — adapt paths) to assess Node compatibility per module. 2. Certificates: how does an agent get a cert? Options: user preloads .p12 into server store (encrypted at rest), or pass-through per-call (worse). Recommend + document security posture. 3. Transport: stdio (local agent, e.g. this machine) vs Streamable HTTP (remote agents, homelab). Recommend stdio-first. 4. Tool surface (proposed): - `autofirma_sign(file_b64|path, format: cades|xades|pades, cert_id, options)` → signed artifact - `autofirma_verify(artifact_b64)` → validity report (reuse verify module) - `autofirma_list_certs()` → stored cert metadata (no key material) 5. Threat model: file path escapes, unbounded sizes, cert/key handling, audit log of signatures. ## Acceptance - docs/MCP-DESIGN.md with recommendation + rejected alternatives - if (a) recommended and low-risk: minimal MCP server (`mcp/` dir, stdio, official @modelcontextprotocol/sdk) implementing sign(cades)+verify+list_certs, with vitest coverage - e2e check: script signs a fixture via the server and verifies output - NO server-side key persistence unless encrypted; document key lifecycle Independent of UI epics; owns `mcp/` + `docs/MCP-DESIGN.md` only.
Author
Owner

bullet: coder launched on branch issue/28-epic-mcp-server-exposing-autof (model nan/deepseek-v4-flash).

bullet: coder launched on branch `issue/28-epic-mcp-server-exposing-autof` (model `nan/deepseek-v4-flash`).
Author
Owner

Claimed — branch issue/28-epic-mcp-server-exposing-autof. Doing: Node-compat study of web/src/lib per module, then docs/MCP-DESIGN.md + minimal stdio MCP server in mcp/ (sign CAdES / verify / list_certs).

Claimed — branch `issue/28-epic-mcp-server-exposing-autof`. Doing: Node-compat study of `web/src/lib` per module, then `docs/MCP-DESIGN.md` + minimal stdio MCP server in `mcp/` (sign CAdES / verify / list_certs).
Author
Owner

Issue #28 — MCP server exposing AutoFirma sign/verify to agents (study)

Restated goal

Let a coding agent on this machine sign documents with the existing AutoFirma Web engine instead
of desktop AutoFirma. Study-first: produce docs/MCP-DESIGN.md with a recommendation and rejected
alternatives, then implement the recommended minimal slice — a stdio MCP server in mcp/ exposing
autofirma_sign (CAdES), autofirma_verify, autofirma_list_certs, with vitest coverage and an
end-to-end check that signs a fixture through the server and verifies the output with openssl.

"Done" = the design doc recommends one option with per-module evidence, the server builds and runs
over stdio, all three tools answer correctly, an e2e script proves a signature made by the server
verifies under an independent tool (openssl cms -verify), no private key is persisted anywhere,
and the repo gates stay green.

Scope

Files I create:

  • docs/MCP-DESIGN.md — study, recommendation, rejected alternatives, threat model.
  • mcp/package.json, mcp/tsconfig.json, mcp/vitest.config.ts, mcp/README.md
  • mcp/src/config.ts — environment → config, with limits.
  • mcp/src/input.ts — file_b64 / guarded path source resolution + size cap.
  • mcp/src/certstore.ts — PKCS#12 store: load, in-memory sessions, metadata-only listing.
  • mcp/src/audit.ts — append-only JSONL audit log of sign/verify calls.
  • mcp/src/tools.ts — the three tool implementations against the engine (pure, testable).
  • mcp/src/server.ts — MCP stdio wiring (@modelcontextprotocol/sdk).
  • mcp/test/*.test.ts — vitest coverage.
  • mcp/scripts/e2e.mjs — spawn the built server, sign, verify with openssl, tamper check.

Files I will not touch:

  • web/** — the engine is consumed read-only. Option (a) is chosen precisely because it needs no
    engine change; if a change turns out to be needed, that becomes a separate issue.
  • decompiled/**, original/**, extracted/** — untouched.
  • Containerfile, .gitignore, AGENTS.md, root docs/architecture.md — out of scope.

Tasks (each with its verification command)

  1. Node-compat audit of web/src/lib. Evidence already gathered: engine modules use only
    globals Node ≥20 provides (crypto.subtle, TextEncoder/TextDecoder, btoa/atob);
    xades.ts, ooxml.ts, c14n.ts, verify/xades.ts need DOMParser/XMLSerializer.
    Verify: grep -rn "DOMParser\|XMLSerializer" web/src/lib → only those four modules.
  2. mcp/ package skeleton + pnpm install.
    Verify: cd mcp && pnpm install and node -e "import('@modelcontextprotocol/sdk/server/mcp.js')".
  3. docs/MCP-DESIGN.md with the recommendation.
    Verify: file exists, answers all five study questions, names the rejected alternatives.
  4. src/input.ts + src/audit.ts + src/certstore.ts.
    Verify: cd mcp && pnpm test (unit tests for path-escape rejection, size cap, listing).
  5. src/tools.ts — sign(cades)/verify/list_certs.
    Verify: cd mcp && pnpm test — sign a fixture .p12 + document, verify the report is ok.
  6. src/server.ts + build. Verify: cd mcp && pnpm build then
    node scripts/e2e.mjs → openssl verifies the signature the server produced.
  7. Gates + commit. Verify: test -d decompiled && find decompiled -name '*.java' | grep -q .
    and git diff --check.

Open questions (assumption I am proceeding on)

  • Can the engine run under Node unmodified? Assumed yes for CAdES (sign + verify). XAdES and
    OOXML need a DOM; PAdES needs pdf-lib (isomorphic) but the verify path for PDF is not written
    yet in the repo. So the slice is CAdES-only and the other formats return an explicit
    "not in this slice" result rather than a wrong answer.
  • How is TS executed by Node? The engine uses extension-less relative imports, which Node ESM
    cannot resolve. Assumed the boring path: bundle mcp/src/server.ts with esbuild (single
    command, no loader hooks) and run the bundle. Tests run under vitest, which resolves them anyway.
  • How does an agent get a certificate? Assumed: operator preloads .p12 files into a directory
    (AUTOFIRME_MCP_CERTS) and passes the PIN in the environment at start. The container is already
    encrypted at rest; the decrypted private key is imported non-extractable and lives in memory only.
  • Transport. Assumed stdio-first (the issue's own recommendation); Streamable HTTP documented as
    a deferred follow-up.
  • Is path input in the slice? Yes, but only inside AUTOFIRME_MCP_WORKSPACE after
    realpath, plus an unconditional size cap; file_b64 is the primary channel.

Risk

  • New dependency (@modelcontextprotocol/sdk, zod, esbuild). The issue names the SDK
    explicitly, so it is required, not optional. esbuild is the build tool for the extension-less
    imports — if install fails, the fallback is a ~30-line Node module-resolution loader and the server
    runs from source.
  • Network during install. Registry reachability verified before planning.
  • pnpm install inside mcp/ adds mcp/pnpm-lock.yaml. Committed, so the build is reproducible.
  • Gate is structural only (decompiled/ present). The real risk is a suite going red: I run the
    existing web/ suite untouched before reporting, so a regression there cannot hide.
# Issue #28 — MCP server exposing AutoFirma sign/verify to agents (study) ## Restated goal Let a coding agent on this machine sign documents with the existing AutoFirma Web engine instead of desktop AutoFirma. Study-first: produce `docs/MCP-DESIGN.md` with a recommendation and rejected alternatives, then implement the recommended minimal slice — a stdio MCP server in `mcp/` exposing `autofirma_sign` (CAdES), `autofirma_verify`, `autofirma_list_certs`, with vitest coverage and an end-to-end check that signs a fixture through the server and verifies the output with `openssl`. "Done" = the design doc recommends one option with per-module evidence, the server builds and runs over stdio, all three tools answer correctly, an e2e script proves a signature made by the server verifies under an independent tool (`openssl cms -verify`), no private key is persisted anywhere, and the repo gates stay green. ## Scope Files I create: - `docs/MCP-DESIGN.md` — study, recommendation, rejected alternatives, threat model. - `mcp/package.json`, `mcp/tsconfig.json`, `mcp/vitest.config.ts`, `mcp/README.md` - `mcp/src/config.ts` — environment → config, with limits. - `mcp/src/input.ts` — `file_b64` / guarded `path` source resolution + size cap. - `mcp/src/certstore.ts` — PKCS#12 store: load, in-memory sessions, metadata-only listing. - `mcp/src/audit.ts` — append-only JSONL audit log of sign/verify calls. - `mcp/src/tools.ts` — the three tool implementations against the engine (pure, testable). - `mcp/src/server.ts` — MCP stdio wiring (`@modelcontextprotocol/sdk`). - `mcp/test/*.test.ts` — vitest coverage. - `mcp/scripts/e2e.mjs` — spawn the built server, sign, verify with `openssl`, tamper check. Files I will **not** touch: - `web/**` — the engine is consumed read-only. Option (a) is chosen precisely because it needs no engine change; if a change turns out to be needed, that becomes a separate issue. - `decompiled/**`, `original/**`, `extracted/**` — untouched. - `Containerfile`, `.gitignore`, `AGENTS.md`, root `docs/architecture.md` — out of scope. ## Tasks (each with its verification command) 1. **Node-compat audit of `web/src/lib`.** Evidence already gathered: engine modules use only globals Node ≥20 provides (`crypto.subtle`, `TextEncoder`/`TextDecoder`, `btoa`/`atob`); `xades.ts`, `ooxml.ts`, `c14n.ts`, `verify/xades.ts` need `DOMParser`/`XMLSerializer`. Verify: `grep -rn "DOMParser\|XMLSerializer" web/src/lib` → only those four modules. 2. **`mcp/` package skeleton** + `pnpm install`. Verify: `cd mcp && pnpm install` and `node -e "import('@modelcontextprotocol/sdk/server/mcp.js')"`. 3. **`docs/MCP-DESIGN.md`** with the recommendation. Verify: file exists, answers all five study questions, names the rejected alternatives. 4. **`src/input.ts` + `src/audit.ts` + `src/certstore.ts`.** Verify: `cd mcp && pnpm test` (unit tests for path-escape rejection, size cap, listing). 5. **`src/tools.ts`** — sign(cades)/verify/list_certs. Verify: `cd mcp && pnpm test` — sign a fixture .p12 + document, verify the report is `ok`. 6. **`src/server.ts` + build.** Verify: `cd mcp && pnpm build` then `node scripts/e2e.mjs` → openssl verifies the signature the server produced. 7. **Gates + commit.** Verify: `test -d decompiled && find decompiled -name '*.java' | grep -q .` and `git diff --check`. ## Open questions (assumption I am proceeding on) - **Can the engine run under Node unmodified?** Assumed **yes for CAdES** (sign + verify). XAdES and OOXML need a DOM; PAdES needs pdf-lib (isomorphic) but the *verify* path for PDF is not written yet in the repo. So the slice is CAdES-only and the other formats return an explicit "not in this slice" result rather than a wrong answer. - **How is TS executed by Node?** The engine uses extension-less relative imports, which Node ESM cannot resolve. Assumed the boring path: bundle `mcp/src/server.ts` with `esbuild` (single command, no loader hooks) and run the bundle. Tests run under vitest, which resolves them anyway. - **How does an agent get a certificate?** Assumed: operator preloads `.p12` files into a directory (`AUTOFIRME_MCP_CERTS`) and passes the PIN in the environment at start. The container is already encrypted at rest; the decrypted private key is imported non-extractable and lives in memory only. - **Transport.** Assumed stdio-first (the issue's own recommendation); Streamable HTTP documented as a deferred follow-up. - **Is `path` input in the slice?** Yes, but only inside `AUTOFIRME_MCP_WORKSPACE` after `realpath`, plus an unconditional size cap; `file_b64` is the primary channel. ## Risk - **New dependency** (`@modelcontextprotocol/sdk`, `zod`, `esbuild`). The issue names the SDK explicitly, so it is required, not optional. `esbuild` is the build tool for the extension-less imports — if install fails, the fallback is a ~30-line Node module-resolution loader and the server runs from source. - **Network during install.** Registry reachability verified before planning. - **`pnpm install` inside `mcp/` adds `mcp/pnpm-lock.yaml`.** Committed, so the build is reproducible. - **Gate is structural only** (`decompiled/` present). The real risk is a suite going red: I run the existing `web/` suite untouched before reporting, so a regression there cannot hide.
Author
Owner

bullet: coder launched on branch issue/28-epic-mcp-server-exposing-autof (model nan/deepseek-v4-flash).

bullet: coder launched on branch `issue/28-epic-mcp-server-exposing-autof` (model `nan/deepseek-v4-flash`).
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/28-epic-mcp-server-exposing-autof).

bullet: coder session died — relaunched from committed state (branch `issue/28-epic-mcp-server-exposing-autof`).
Author
Owner

Claimed — branch issue/28. Re-verifying gates + e2e.

Claimed — branch issue/28. Re-verifying gates + e2e.
Author
Owner

Issue #28 — MCP server exposing AutoFirma sign/verify to agents (study)

Restated goal

Let a coding agent on this machine sign documents with the existing AutoFirma Web engine instead
of desktop AutoFirma. Study-first: produce docs/MCP-DESIGN.md with a recommendation and rejected
alternatives, then implement the recommended minimal slice — a stdio MCP server in mcp/ exposing
autofirma_sign (CAdES), autofirma_verify, autofirma_list_certs, with vitest coverage and an
end-to-end check that signs a fixture through the server and verifies the output with openssl.

"Done" = the design doc recommends one option with per-module evidence, the server builds and runs
over stdio, all three tools answer correctly, an e2e script proves a signature made by the server
verifies under an independent tool (openssl cms -verify), no private key is persisted anywhere,
and the repo gates stay green.

Scope

Files I create:

  • docs/MCP-DESIGN.md — study, recommendation, rejected alternatives, threat model.
  • mcp/package.json, mcp/tsconfig.json, mcp/vitest.config.ts, mcp/README.md
  • mcp/src/config.ts — environment → config, with limits.
  • mcp/src/input.ts — file_b64 / guarded path source resolution + size cap.
  • mcp/src/certstore.ts — PKCS#12 store: load, in-memory sessions, metadata-only listing.
  • mcp/src/audit.ts — append-only JSONL audit log of sign/verify calls.
  • mcp/src/tools.ts — the three tool implementations against the engine (pure, testable).
  • mcp/src/server.ts — MCP stdio wiring (@modelcontextprotocol/sdk).
  • mcp/test/*.test.ts — vitest coverage.
  • mcp/scripts/e2e.mjs — spawn the built server, sign, verify with openssl, tamper check.

Files I will not touch:

  • web/** — the engine is consumed read-only. Option (a) is chosen precisely because it needs no
    engine change; if a change turns out to be needed, that becomes a separate issue.
  • decompiled/**, original/**, extracted/** — untouched.
  • Containerfile, .gitignore, AGENTS.md, root docs/architecture.md — out of scope.

Tasks (each with its verification command)

  1. Node-compat audit of web/src/lib. Evidence already gathered: engine modules use only
    globals Node ≥20 provides (crypto.subtle, TextEncoder/TextDecoder, btoa/atob);
    xades.ts, ooxml.ts, c14n.ts, verify/xades.ts need DOMParser/XMLSerializer.
    Verify: grep -rn "DOMParser\|XMLSerializer" web/src/lib → only those four modules.
  2. mcp/ package skeleton + pnpm install.
    Verify: cd mcp && pnpm install and node -e "import('@modelcontextprotocol/sdk/server/mcp.js')".
  3. docs/MCP-DESIGN.md with the recommendation.
    Verify: file exists, answers all five study questions, names the rejected alternatives.
  4. src/input.ts + src/audit.ts + src/certstore.ts.
    Verify: cd mcp && pnpm test (unit tests for path-escape rejection, size cap, listing).
  5. src/tools.ts — sign(cades)/verify/list_certs.
    Verify: cd mcp && pnpm test — sign a fixture .p12 + document, verify the report is ok.
  6. src/server.ts + build. Verify: cd mcp && pnpm build then
    node scripts/e2e.mjs → openssl verifies the signature the server produced.
  7. Gates + commit. Verify: test -d decompiled && find decompiled -name '*.java' | grep -q .
    and git diff --check.

Open questions (assumption I am proceeding on)

  • Can the engine run under Node unmodified? Assumed yes for CAdES (sign + verify). XAdES and
    OOXML need a DOM; PAdES needs pdf-lib (isomorphic) but the verify path for PDF is not written
    yet in the repo. So the slice is CAdES-only and the other formats return an explicit
    "not in this slice" result rather than a wrong answer.
  • How is TS executed by Node? The engine uses extension-less relative imports, which Node ESM
    cannot resolve. Assumed the boring path: bundle mcp/src/server.ts with esbuild (single
    command, no loader hooks) and run the bundle. Tests run under vitest, which resolves them anyway.
  • How does an agent get a certificate? Assumed: operator preloads .p12 files into a directory
    (AUTOFIRME_MCP_CERTS) and passes the PIN in the environment at start. The container is already
    encrypted at rest; the decrypted private key is imported non-extractable and lives in memory only.
  • Transport. Assumed stdio-first (the issue's own recommendation); Streamable HTTP documented as
    a deferred follow-up.
  • Is path input in the slice? Yes, but only inside AUTOFIRME_MCP_WORKSPACE after
    realpath, plus an unconditional size cap; file_b64 is the primary channel.

Risk

  • New dependency (@modelcontextprotocol/sdk, zod, esbuild). The issue names the SDK
    explicitly, so it is required, not optional. esbuild is the build tool for the extension-less
    imports — if install fails, the fallback is a ~30-line Node module-resolution loader and the server
    runs from source.
  • Network during install. Registry reachability verified before planning.
  • pnpm install inside mcp/ adds mcp/pnpm-lock.yaml. Committed, so the build is reproducible.
  • Gate is structural only (decompiled/ present). The real risk is a suite going red: I run the
    existing web/ suite untouched before reporting, so a regression there cannot hide.

Implementation deltas (recorded after the work, for the reviewer)

The plan above is the contract the diff was written against. Three deviations, all inside the
declared scope (mcp/ + docs/MCP-DESIGN.md):

  1. AUTOFIRME_MCP_TRUSTED_ROOTS added (mcp/src/config.ts, passed from mcp/src/tools.ts).
    Without it, autofirma_verify builds the chain against the engine's bundled FNMT roots only, so
    an operator's own private CA always reads certChain: fail. The anchors are operator-level and
    read once at startup: an agent must not be able to supply trust. ~10 lines, no new dependency.
  2. autofirma_verify takes artifact_b64/path, not file_b64 — the design doc and the issue
    both name the signature input that way. mcp/src/tools.ts + mcp/src/server.ts.
  3. mcp/dist/ is no longer tracked (mcp/.gitignore, build output) and the esbuild banner was
    dropped because it duplicated the shebang the source already has — the server could not start.
    mcp/tsconfig.json mirrors the engine's needs (DOM lib) and checks only this package's files;
    mcp/pnpm-workspace.yaml denies esbuild's build script explicitly (pnpm exits non-zero while it
    is neither allowed nor denied).

No file under web/ was modified; the engine is consumed read-only from source.

Verification session (commit 09e0bb5)

The session above died before reporting. A second pass re-ran every gate on the committed tree
(pnpm test 29/29, pnpm check, pnpm build, pnpm e2e, web/ suite 96/96, git diff --check,
the decompiled/ structural gate), hand-audited input.ts/certstore.ts/tools.ts/server.ts,
and corrected one evidence sentence in docs/MCP-DESIGN.md: the DOM grep matches xades.ts,
ooxml.ts and verify/xades.ts (plus tests), not "exactly those four modules" — c14n.ts and
verify/c14n.ts traverse DOM nodes without constructing one. Same scope, same conclusion.

# Issue #28 — MCP server exposing AutoFirma sign/verify to agents (study) ## Restated goal Let a coding agent on this machine sign documents with the existing AutoFirma Web engine instead of desktop AutoFirma. Study-first: produce `docs/MCP-DESIGN.md` with a recommendation and rejected alternatives, then implement the recommended minimal slice — a stdio MCP server in `mcp/` exposing `autofirma_sign` (CAdES), `autofirma_verify`, `autofirma_list_certs`, with vitest coverage and an end-to-end check that signs a fixture through the server and verifies the output with `openssl`. "Done" = the design doc recommends one option with per-module evidence, the server builds and runs over stdio, all three tools answer correctly, an e2e script proves a signature made by the server verifies under an independent tool (`openssl cms -verify`), no private key is persisted anywhere, and the repo gates stay green. ## Scope Files I create: - `docs/MCP-DESIGN.md` — study, recommendation, rejected alternatives, threat model. - `mcp/package.json`, `mcp/tsconfig.json`, `mcp/vitest.config.ts`, `mcp/README.md` - `mcp/src/config.ts` — environment → config, with limits. - `mcp/src/input.ts` — `file_b64` / guarded `path` source resolution + size cap. - `mcp/src/certstore.ts` — PKCS#12 store: load, in-memory sessions, metadata-only listing. - `mcp/src/audit.ts` — append-only JSONL audit log of sign/verify calls. - `mcp/src/tools.ts` — the three tool implementations against the engine (pure, testable). - `mcp/src/server.ts` — MCP stdio wiring (`@modelcontextprotocol/sdk`). - `mcp/test/*.test.ts` — vitest coverage. - `mcp/scripts/e2e.mjs` — spawn the built server, sign, verify with `openssl`, tamper check. Files I will **not** touch: - `web/**` — the engine is consumed read-only. Option (a) is chosen precisely because it needs no engine change; if a change turns out to be needed, that becomes a separate issue. - `decompiled/**`, `original/**`, `extracted/**` — untouched. - `Containerfile`, `.gitignore`, `AGENTS.md`, root `docs/architecture.md` — out of scope. ## Tasks (each with its verification command) 1. **Node-compat audit of `web/src/lib`.** Evidence already gathered: engine modules use only globals Node ≥20 provides (`crypto.subtle`, `TextEncoder`/`TextDecoder`, `btoa`/`atob`); `xades.ts`, `ooxml.ts`, `c14n.ts`, `verify/xades.ts` need `DOMParser`/`XMLSerializer`. Verify: `grep -rn "DOMParser\|XMLSerializer" web/src/lib` → only those four modules. 2. **`mcp/` package skeleton** + `pnpm install`. Verify: `cd mcp && pnpm install` and `node -e "import('@modelcontextprotocol/sdk/server/mcp.js')"`. 3. **`docs/MCP-DESIGN.md`** with the recommendation. Verify: file exists, answers all five study questions, names the rejected alternatives. 4. **`src/input.ts` + `src/audit.ts` + `src/certstore.ts`.** Verify: `cd mcp && pnpm test` (unit tests for path-escape rejection, size cap, listing). 5. **`src/tools.ts`** — sign(cades)/verify/list_certs. Verify: `cd mcp && pnpm test` — sign a fixture .p12 + document, verify the report is `ok`. 6. **`src/server.ts` + build.** Verify: `cd mcp && pnpm build` then `node scripts/e2e.mjs` → openssl verifies the signature the server produced. 7. **Gates + commit.** Verify: `test -d decompiled && find decompiled -name '*.java' | grep -q .` and `git diff --check`. ## Open questions (assumption I am proceeding on) - **Can the engine run under Node unmodified?** Assumed **yes for CAdES** (sign + verify). XAdES and OOXML need a DOM; PAdES needs pdf-lib (isomorphic) but the *verify* path for PDF is not written yet in the repo. So the slice is CAdES-only and the other formats return an explicit "not in this slice" result rather than a wrong answer. - **How is TS executed by Node?** The engine uses extension-less relative imports, which Node ESM cannot resolve. Assumed the boring path: bundle `mcp/src/server.ts` with `esbuild` (single command, no loader hooks) and run the bundle. Tests run under vitest, which resolves them anyway. - **How does an agent get a certificate?** Assumed: operator preloads `.p12` files into a directory (`AUTOFIRME_MCP_CERTS`) and passes the PIN in the environment at start. The container is already encrypted at rest; the decrypted private key is imported non-extractable and lives in memory only. - **Transport.** Assumed stdio-first (the issue's own recommendation); Streamable HTTP documented as a deferred follow-up. - **Is `path` input in the slice?** Yes, but only inside `AUTOFIRME_MCP_WORKSPACE` after `realpath`, plus an unconditional size cap; `file_b64` is the primary channel. ## Risk - **New dependency** (`@modelcontextprotocol/sdk`, `zod`, `esbuild`). The issue names the SDK explicitly, so it is required, not optional. `esbuild` is the build tool for the extension-less imports — if install fails, the fallback is a ~30-line Node module-resolution loader and the server runs from source. - **Network during install.** Registry reachability verified before planning. - **`pnpm install` inside `mcp/` adds `mcp/pnpm-lock.yaml`.** Committed, so the build is reproducible. - **Gate is structural only** (`decompiled/` present). The real risk is a suite going red: I run the existing `web/` suite untouched before reporting, so a regression there cannot hide. ## Implementation deltas (recorded after the work, for the reviewer) The plan above is the contract the diff was written against. Three deviations, all inside the declared scope (`mcp/` + `docs/MCP-DESIGN.md`): 1. **`AUTOFIRME_MCP_TRUSTED_ROOTS` added** (`mcp/src/config.ts`, passed from `mcp/src/tools.ts`). Without it, `autofirma_verify` builds the chain against the engine's bundled FNMT roots only, so an operator's own private CA always reads `certChain: fail`. The anchors are operator-level and read once at startup: an agent must not be able to supply trust. ~10 lines, no new dependency. 2. **`autofirma_verify` takes `artifact_b64`/`path`**, not `file_b64` — the design doc and the issue both name the signature input that way. `mcp/src/tools.ts` + `mcp/src/server.ts`. 3. **`mcp/dist/` is no longer tracked** (`mcp/.gitignore`, build output) and the esbuild banner was dropped because it duplicated the shebang the source already has — the server could not start. `mcp/tsconfig.json` mirrors the engine's needs (DOM lib) and checks only this package's files; `mcp/pnpm-workspace.yaml` denies esbuild's build script explicitly (pnpm exits non-zero while it is neither allowed nor denied). No file under `web/` was modified; the engine is consumed read-only from source. ## Verification session (commit `09e0bb5`) The session above died before reporting. A second pass re-ran every gate on the committed tree (`pnpm test` 29/29, `pnpm check`, `pnpm build`, `pnpm e2e`, `web/` suite 96/96, `git diff --check`, the `decompiled/` structural gate), hand-audited `input.ts`/`certstore.ts`/`tools.ts`/`server.ts`, and corrected one evidence sentence in `docs/MCP-DESIGN.md`: the DOM grep matches `xades.ts`, `ooxml.ts` and `verify/xades.ts` (plus tests), not "exactly those four modules" — `c14n.ts` and `verify/c14n.ts` traverse DOM nodes without constructing one. Same scope, same conclusion.
Author
Owner

DONE — branch issue/28-epic-mcp-server-exposing-autof.

Commits (vs bullet-develop f7dbab8): e61b47d (operator WIP checkpoint), a0ad55f test+e2e+trusted-anchor config, 1ffc0ba study trust anchors+evidence, 09e0bb5 DOM evidence correction. This session added no commit — re-verification only; tree clean.

Gates (all exit 0, re-run): structural decompiled 11487 java files — pass; git diff --check — pass; cd mcp && pnpm test 29/29 — pass; cd mcp && pnpm check — pass; cd mcp && pnpm build (dist/server.js 74.4 kb) — pass; cd mcp && pnpm e2e — pass (openssl verified the server's CAdES signature, tampering rejected); cd web && pnpm test 96/96 — pass.

Acceptance: docs/MCP-DESIGN.md recommends option (a) run the TS engine under Node, rejects (b)/(c), answers all 5 study questions; mcp/ stdio server (@modelcontextprotocol/sdk) with sign(cades)+verify+list_certs and vitest coverage; e2e signs a fixture and verifies with openssl; no key persistence (PKCS#12 at rest, key imported non-extractable, memory only, documented key lifecycle).

Note: comment-policy curl with BULLET_TOKEN 404s on this repo (token lists only bullet/gotoi/masante-test-repo); these comments use the basic credential. Full detail in the operator report.

DONE — branch `issue/28-epic-mcp-server-exposing-autof`. Commits (vs `bullet-develop` `f7dbab8`): `e61b47d` (operator WIP checkpoint), `a0ad55f` test+e2e+trusted-anchor config, `1ffc0ba` study trust anchors+evidence, `09e0bb5` DOM evidence correction. This session added no commit — re-verification only; tree clean. Gates (all exit 0, re-run): structural `decompiled` 11487 java files — pass; `git diff --check` — pass; `cd mcp && pnpm test` 29/29 — pass; `cd mcp && pnpm check` — pass; `cd mcp && pnpm build` (dist/server.js 74.4 kb) — pass; `cd mcp && pnpm e2e` — pass (openssl verified the server's CAdES signature, tampering rejected); `cd web && pnpm test` 96/96 — pass. Acceptance: `docs/MCP-DESIGN.md` recommends option (a) run the TS engine under Node, rejects (b)/(c), answers all 5 study questions; `mcp/` stdio server (`@modelcontextprotocol/sdk`) with sign(cades)+verify+list_certs and vitest coverage; e2e signs a fixture and verifies with openssl; no key persistence (PKCS#12 at rest, key imported non-extractable, memory only, documented key lifecycle). Note: comment-policy curl with `BULLET_TOKEN` 404s on this repo (token lists only bullet/gotoi/masante-test-repo); these comments use the basic credential. Full detail in the operator report.
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/28-epic-mcp-server-exposing-autof).

bullet: coder session died — relaunched from committed state (branch `issue/28-epic-mcp-server-exposing-autof`).
Author
Owner

bullet: implementation finished (DONE — branch issue/28-epic-mcp-server-exposing-autof.) — moving to review.

bullet: implementation finished (DONE — branch `issue/28-epic-mcp-server-exposing-autof`.) — moving to review.
Author
Owner

bullet: reviewer launched (model 9router/reviewer, independent of coder).

bullet: reviewer launched (model `9router/reviewer`, independent of coder).
Author
Owner

DONE — branch issue/28-epic-mcp-server-exposing-autof, tip dd74627.

Commits vs bullet-develop f7dbab8: a0ad55f, 1ffc0ba, 09e0bb5 (earlier session) + dd74627 (this session, doc-only: adds the risk assessment study Q1 asks for — docs/MCP-DESIGN.md §1, 19 insertions).

Gates (all exit 0): structural test -d decompiled && find decompiled -name "*.java" | grep -q . (11487 files); git diff --check; cd mcp && pnpm test (4 files, 29 tests); mcp pnpm check; mcp pnpm build (dist/server.js 74.4 kb); mcp pnpm e2e (server signs, openssl accepts, tampering fails); cd web && pnpm test (96 tests, untouched regression). Tree clean.

Note: the gate line as given to the coder skill is malformed — test -d decompiled find decompiled -name "*.java" | grep -q . exits 2 (test: invalid test command); the && form above is the intended check and passes.

Note: #28 went to review at 17:12, before dd74627 landed. That commit touches docs/MCP-DESIGN.md only — the code review sees the same bytes.

Refs #28

DONE — branch `issue/28-epic-mcp-server-exposing-autof`, tip `dd74627`. Commits vs `bullet-develop` `f7dbab8`: `a0ad55f`, `1ffc0ba`, `09e0bb5` (earlier session) + `dd74627` (this session, doc-only: adds the risk assessment study Q1 asks for — `docs/MCP-DESIGN.md` §1, 19 insertions). Gates (all exit 0): structural `test -d decompiled && find decompiled -name "*.java" | grep -q .` (11487 files); `git diff --check`; `cd mcp && pnpm test` (4 files, 29 tests); `mcp pnpm check`; `mcp pnpm build` (dist/server.js 74.4 kb); `mcp pnpm e2e` (server signs, openssl accepts, tampering fails); `cd web && pnpm test` (96 tests, untouched regression). Tree clean. Note: the gate line as given to the coder skill is malformed — `test -d decompiled find decompiled -name "*.java" | grep -q .` exits 2 (`test: invalid test command`); the `&&` form above is the intended check and passes. Note: #28 went to review at 17:12, before `dd74627` landed. That commit touches `docs/MCP-DESIGN.md` only — the code review sees the same bytes. Refs #28
Author
Owner

REVIEW: CLEAN — commit dd74627: findings none

REVIEW: CLEAN — commit dd74627: findings none
Author
Owner

bullet: review CLEAN — ready to publish.

bullet: review CLEAN — ready to publish.
Author
Owner

bullet: branch issue/28-epic-mcp-server-exposing-autof not on origin — cannot integrate.

bullet: branch `issue/28-epic-mcp-server-exposing-autof` not on origin — cannot integrate.
Author
Owner

bullet: PR #32 open → bullet-develop (auto-merge follows).

bullet: PR #32 open → `bullet-develop` (auto-merge follows).
Author
Owner

bullet: PR #32 auto-merged into bullet-develop.

bullet: PR #32 auto-merged into `bullet-develop`.
Author
Owner

bullet: merged into bullet-develop — integration gates green.

bullet: merged into `bullet-develop` — integration gates green.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
EduarD3V/AutoFirme#28
No description provided.