Skip to main content
Glama
invoice-match-mcp

invoice-match-mcp

invoice-match-mcp

A small MCP server that lets an AI model three-way-match supplier invoices (invoice vs purchase order vs goods received) without ever being the one who decides.

The model calls the tools and explains the outcome. The decision comes from a deterministic rule engine, so it is reproducible, testable and auditable. That split is the point of this repo: LLM as interface, rules as policy, evals as proof.

What it exposes

MCP item

Purpose

check_invoice(invoice_json, register=false)

Returns APPROVE, HOLD_PRICE, HOLD_QTY, HOLD_NO_PO, HOLD_DUPLICATE, HOLD_CURRENCY or REVIEW, with human-readable reasons. Never posts or pays anything.

list_open_purchase_orders(supplier)

Lets the model look up PO lines, received quantities and agreed prices before it answers.

resource policy://matching

The policy in plain language, so the model can quote it.

Rules: unit price within 2 % of the PO; invoiced quantity not above received quantity; same currency; same supplier plus invoice number twice is a duplicate; no PO number gives REVIEW at best, never APPROVE.

Related MCP server: manager-mcp

Run it

Requires Python 3.10+ and the MCP Python SDK 2.x.

pip install -e ".[dev]"
python -m invoice_match.server        # stdio MCP server

Claude Desktop / any MCP client:

{ "mcpServers": { "invoice-match": { "command": "python", "args": ["-m", "invoice_match.server"] } } }

Use your own purchase orders by pointing INVOICE_MATCH_PO_FILE at a CSV with the columns of data/purchase_orders.csv.

Evals

python evals/run_evals.py

evals/golden_cases.json holds 11 invoices with the expected decision (clean match, price drift, over-billing, unknown PO, wrong supplier, currency, ambiguous PO, ...). The same file can grade an LLM agent: give the model each invoice with the server attached and compare its final answer to expect. One source of truth for engine, tools and agent.

pytest covers the engine (including the exact 2 % boundary and purity) and starts the real server over stdio, calling the tool the way a model would.

Why this exists

Built by Paweł Boruta-Ośko, who ran an omnichannel retailer for almost 20 years (ERP, WMS, 50+ integrations) and now builds agentic systems. See also OpenFill, an MCP browser agent. All data in this repo is fictional. Written with substantial help from Claude (Anthropic): the matching rules and scope were specified by the author, the code, tests and evals were drafted by Claude Code and reviewed by him.

MIT licence.

Available Tools

2 tools
check_invoiceA

Three-way match one invoice against the PO file.

invoice_json: {"invoice_number": str, "supplier": str, "po_number": str|null, "currency": "EUR", "lines": [{"sku": str, "qty": number, "unit_price": number}]} register: if true and the decision is APPROVE, remember the invoice so a resubmission is flagged as a duplicate. Returns status (APPROVE, HOLD_*, REVIEW), reasons and the PO used. It never pays or posts anything.

NameRequiredDescriptionDefault
registerNo
invoice_jsonYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden well: it declares the return shape (status with APPROVE/HOLD_*/REVIEW, reasons, PO used), states the safety boundary ('never pays or posts anything'), and discloses the hidden side effect of register — a persistent memory that causes future resubmissions to be flagged as duplicates. What's missing is behavior on errors and how the HOLD_* variants differ.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the operation, followed by parameter detail, then return and safety notes. Every block earns its place — the JSON shape is necessary given 0% schema coverage, and the duplicate-flagging side effect is non-obvious information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description supplies both the return contract and the safety profile, which is most of what an agent needs. Minor gaps remain around the meaning of the HOLD_* sub-statuses and how reasons are formatted, which would matter for deciding next steps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the schema types invoice_json as a bare string, so the description does the heavy lifting by spelling out the required JSON object shape, field types, the nullable po_number, the fixed EUR currency, and the line-item structure. It also states the exact conditional under which register fires (true AND decision APPROVE).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The verb+resource is specific and domain-precise: 'three-way match one invoice against the PO file' tells an agent exactly what operation runs and on what object. It is clearly distinct from the sibling list_open_purchase_orders, though 'the PO file' is left slightly vague as to where PO data is sourced.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: the tool is evidently a pre-payment invoice validation step, and the register parameter documents one conditional behavior. There is no explicit when-to-use/when-not-to-use guidance, no discussion of what an agent should do with HOLD_* or REVIEW decisions, and no alternatives named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_open_purchase_ordersC

List PO lines for a supplier, with ordered/received quantities and agreed unit price.

NameRequiredDescriptionDefault
supplierYes

Output Schema

NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses what fields come back (quantities, unit price), which is useful, but says nothing about whether results are filtered to open lines only, pagination, supplier resolution behavior, or what happens when the supplier is unknown.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler; the resource and returned fields come first. Nothing redundant or padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value detail is rightly omitted, but the description omits the 'open' filtering semantics that the name implies and that materially change results. For a listing tool whose whole point is scope, that gap is significant.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for the single 'supplier' parameter, so the description must compensate. It clarifies that the argument scopes results to one supplier, but never states the expected format — supplier ID, code, or name — leaving real ambiguity at call time.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'List PO lines for a supplier,' and names the returned fields (ordered/received quantities, agreed unit price). It is not differentiated from the sibling check_invoice, though that tool is unrelated enough that confusion is unlikely. Minor mismatch: the name says 'open purchase orders' while the description says 'PO lines,' without clarifying the relationship.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this versus alternatives, no prerequisites, no exclusions. Most notably, the 'open' qualifier in the tool name is never explained — an agent cannot tell whether closed/fully-received orders are included or filtered out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedcheck_invoice
    • First observedlist_open_purchase_orders

TDQS

B3.4/5.0

Scored across 2 tools

Disambiguation5/5

The two tools target clearly different actions on different resources: one reads PO lines for a supplier, the other performs a three-way match on a single invoice. There is no realistic way to confuse them, and the descriptions reinforce the boundary (read-only listing vs. decision-returning validation).

Naming Consistency4/5

Both names follow a snake_case verb_noun convention (list_..., check_...) that reads naturally and consistently. The only minor deviation is that list_open_purchase_orders stacks multiple qualifiers while check_invoice is terse, but the pattern is still predictable.

Tool Count3/5

Two tools is thin even for a narrowly scoped three-way match server; the whole workflow lives in a single check_invoice call with one supporting lookup. It is functional and focused, but borderline under-provisioned for a stated accounts-payable domain.

Completeness3/5

The core path (fetch PO lines with ordered/received quantities, then match an invoice) is covered, and the register flag gives basic duplicate handling. However there is no way to look up a previously registered invoice, list invoices for a supplier, inspect prior decisions, or re-check a HOLD after correction, leaving notable gaps in the lifecycle.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    B
    maintenance
    Enables AI assistants to perform financial reconciliation with a deterministic proof engine: intake files, match transactions, verify proofs, resolve exceptions, and sign off on balanced journals under the user's authority.
    21
    -
  • A
    license
    B
    quality
    B
    maintenance
    Enables AI assistants to query and manage self-hosted accounting data—invoices, balances, and books—through natural language, with read-only tools by default and optional scoped write operations.
    30
    25 PyPI
    1
    AGPL 3.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables LLM agents to query and reconcile an accounts-payable ledger through safe read-only SQL, deterministic invoice-to-payment matching, and exception explanations for underpayments, duplicates, currency mismatches, and overdue invoices. Ships with a synthetic ledger, golden-set evals, and a month-end review workflow so quality can be measured in CI.
    4
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Gives AI agents a safe way to read and check invoices, returning a deterministic APPROVE, REVIEW or STOP decision with every reason, while no tool can pay, approve or change anything.
    MIT