Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Claude Code Invoice Guard

FreeNot checked

Deterministic guardrail (refuse, don't guess) for an LLM agent on a VAT-critical invoice workflow: skill + subagent + MCP server + PreToolUse hook. Portfolio/de

GitHubEmbed

About

Deterministic guardrail (refuse, don't guess) for an LLM agent on a VAT-critical invoice workflow: skill + subagent + MCP server + PreToolUse hook. Portfolio/demo plugin.

README

Osoittaa Claude Coden runtime-primitiivit yhden kriittisen datapolun ympärillä, pakattuna asennettavaksi pluginiksi. Sisarprojekti standalone-guardrailille (refuse-dont-guess) — sama deterministinen gate, nyt Claude Code -natiivina orkestrointina.

Liputa, älä piilota: kuvitteellinen keissi, ei oikeaa asiakasdataa. ALV-sääntö on havainnollistava placeholder, ei lakitulkinta. Artefakti todistaa primitiivien käytön ja deterministisen gaten — ei tuotanto-orkestrointia skaalassa.


Arkkitehtuuri

Primitiivi Tehtävä Sijainti (plugin-konventio)
Plugin-manifesti metadata, versio, kuvaus .claude-plugin/plugin.json
Itseään isännöivä marketplace tekee pluginista asennettavan .claude-plugin/marketplace.json
Skill process-invoice — toistettava ALV-työnkulku skills/process-invoice/SKILL.md
Subagentti invoice-inspector — laskun tarkastus eristetyssä kontekstissa agents/invoice-inspector.md
MCP-server mock-ERP: list_pending_invoices, get_invoice, write_vat_treatment mcp_server/server.py, .mcp.json
PreToolUse-hook deterministinen gate kriittisen kirjoituksen edessä hooks/vat_gate.py, hooks/hooks.json

Datavirta:

Claude Code (invoice-guard:process-invoice -skill)
  → get_invoice                    (MCP)
  → invoice-inspector               (subagentti, eristetty konteksti)
  → write_vat_treatment             (MCP, KRIITTINEN)
        └─ PreToolUse-hook ajetaan ENNEN kutsua → salli / kiellä

Plugin-rakenteessa polut viittaavat pluginin omaan asennushakemistoon ${CLAUDE_PLUGIN_ROOT}-muuttujalla (dokumentoitu käytäntö), koska plugin kopioidaan asennettaessa cache-hakemistoon eikä suhteellinen polku ../ toimisi enää.

Gate → hook: exit-koodit

Tilanne kind Exit Vaikutus
Faktat selkeät, ehdotus vastaa sääntöä PASS 0 kirjoitus sallitaan
Faktat monitulkintaiset ESCALATE 2 kirjoitus estetään, ihmiselle
Injektio tai ehdotus ristiriidassa säännön kanssa BLOCK 2 kirjoitus estetään

Exit 1 Claude Codessa tarkoittaa "salli varoituksella" — se sallisi kirjoituksen, joten sitä ei käytetä eskalointiin. Gate käyttää siis vain 0 (salli) ja 2 (kiellä); ero BLOCK vs ESCALATE näkyy stderr-viestissä.

Mikä on verifioitu ja missä

Verifioitu tässä repossa (deterministinen, ei vaadi Claude Codea):

  • Gate-logiikka: neljä tapausta oikein (tests/test_gate.py)
  • Hookin exit-koodit simuloiduilla PreToolUse-payloadeilla, myös plugin-muotoisen args-kutsun kautta
  • Kaikki neljä JSON-manifestia (plugin.json, marketplace.json, .mcp.json, hooks/hooks.json) validi JSON
  • MCP-server importtaa ja rekisteröi kolme työkalua (mcp 1.28.1)

Verifioitu Claude Codella livenä (claude --plugin-dir ., versio 2.1.197) — per tapaus, ei "kaikki todistettu":

Case Todistustaso Huomio
PASS (INV-001) Täysin live Malli kirjoitti ensin vapaamuotoisen selityksen, hook torjui sen (BLOCK, täsmäysvirhe), malli luki hookin oman koodin ja korjasi arvon tarkaksi KÄÄNNETTY_ALV_0%:ksi, hook päästi läpi. Vahvin mahdollinen todiste.
BLOCK / ground-truth-ristiriita Täysin live Todistui sivutuotteena PASS-tapauksen ensimmäisestä (virheellisestä) kirjoitusyrityksestä.
BLOCK / injektio (INV-003) Täysin live Malli tunnisti laskuun upotetun ohjeen eikä totellut sitä, mutta yritti kirjoitusta testinä — hook pysäytti riippumatta mallin omasta arviosta.
ESCALATE (INV-002) Vain simuloitu suoralla PreToolUse-payloadilla Malli kieltäytyi jo promptitasolla (SKILL.md:n sääntö — huom: CLAUDE.md ei lataudu plugin-kontekstiksi, ks. sen oma huomautus) eikä koskaan yrittänyt oikeaa kirjoitusta, joten hook-haaraa ei laukaistu livenä. Hyvä uutinen skillin toiminnasta, ei livetodiste juuri tästä hook-haarasta.
Subagentti (invoice-inspector) Täysin live, toisella yrityksellä Ks. alla.

Kaksi bugia löytyi livenä ja korjattiin — sama juurisyy molemmissa: plugin-kontekstissa MCP-työkalujen todellinen nimi on mcp__plugin_invoice-guard_invoice-erp__*, ei aiemmin oletettu mcp__invoice-erp__*. Tämä oli tietoisesti liputettu avoimeksi oletukseksi aiemmassa versiossa ("kannattaa vahvistaa /mcp-komennolla") — vahvistus paljasti oletuksen vääräksi:

  1. hooks/hooks.json:n matcher ei osunut oikeaan nimeen → PreToolUse-hook ei koskaan lauennut, kirjoitukset menivät läpi täysin ilman gatea. Korjattu joustavammaksi: mcp__.*write_vat_treatment.
  2. agents/invoice-inspector.md:n tools:-kenttä osoitti samaan väärään nimeen → subagentilla ei ollut pääsyä get_invoiceiin, ja se hallusinoi laskun myyjän/ostajan tiedot sen sijaan että olisi raportoinut virheen. Korjattu vastaamaan oikeaa nimeä; toisella ajolla subagentti tuotti täsmälleen mock_data.py:n mukaisen datan.

Arkkitehtuurin ydinväite todistui juuri tämän bugin kautta: vaikka subagentti hallusinoi väärän myyjänimen raporttiinsa, itse ALV-kirjauspäätöstä ei koskaan tehty subagentin väitteiden perusteella — write_vat_treatmentin edessä oleva hook laskee oikean vastauksen aina uudelleen mock_data.py:n ground truthista, ei agentin tekstistä. Väärä data raportissa ei olisi voinut johtaa vääreään ALV-kirjaukseen, koska poiminta ja päätös on arkkitehtonisesti erotettu toisistaan — sama periaate kuin sisarprojektissa, nyt todistettuna live-bugin kautta eikä vain väitettynä.

Asennus ja ajo

Kokeile ilman asennusta (nopein tapa, repon juuresta):

pip install "mcp>=1.27,<2"
claude --plugin-dir .

Huom /mcp-näkymästä: kun ajat tätä repon juuresta, /mcp näyttää invoice-erp:n kahteen kertaan — kerran "Project MCPs"-rivinä ✗ failed (Missing environment variables: CLAUDE_PLUGIN_ROOT), koska cwd:ssä oleva .mcp.json tulkitaan myös erilliseksi projektikonfiguraatioksi, ja kerran "Built-in MCPs"-rivinä plugin:invoice-guard:invoice-erp ✔ connected · 3 tools, joka on se oikeasti käytetty palvelin. Epäonnistunut rivi on harmiton — jätä se huomiotta.

Asenna pluginina omasta marketplacesta:

/plugin marketplace add mikko-lab/claude-code-invoice-guard
/plugin install invoice-guard@mikko-lab-portfolio

Kokeile:

Käsittele lasku INV-001     → selkeä → hook sallii (exit 0), KÄÄNNETTY_ALV kirjataan
Käsittele lasku INV-002     → ostajan rooli epäselvä → hook estää (exit 2), ihmiselle
Käsittele lasku INV-003     → injektio tekstissä → hook estää (exit 2)

Aja deterministiset tarkistukset ilman Claude Codea:

python3 -m pytest tests/ -q

Tietoisesti rajattu ulos

Oikea ALV-laki (→ havainnollistava sääntö) · pysyvä tietokanta (mock in-memory) · autentikointi · usean laskun rinnakkaisajo · tuotantomittakaava · virallinen Anthropic-plugin-hakemisto (tämä on henkilökohtainen portfolio-marketplace, ei siihen pyritty). Ydin — neljä primitiiviä + deterministinen gate, pakattuna asennettavaksi pluginiksi — todistetaan ensin.

from github.com/mikko-lab/claude-code-invoice-guard

Installing Claude Code Invoice Guard

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/mikko-lab/claude-code-invoice-guard

FAQ

Is Claude Code Invoice Guard MCP free?

Yes, Claude Code Invoice Guard MCP is free — one-click install via Unyly at no cost.

Does Claude Code Invoice Guard need an API key?

No, Claude Code Invoice Guard runs without API keys or environment variables.

Is Claude Code Invoice Guard hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Claude Code Invoice Guard in Claude Desktop, Claude Code or Cursor?

Open Claude Code Invoice Guard on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Claude Code Invoice Guard with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs