Payables MCP
# Payables MCP
A small MCP server for accounts payable. I built it to work out what an AP agent should look
like if you give a model tools instead of a UI: what it should be allowed to do on its own, what
needs a person, and where the line between the two sits.
Everything runs against a mock ERP in `data.json`, so you can clone it and click through it in
a couple of minutes. There are no API keys.
## Running it
```bash
uv venv && uv sync
uv run mcp dev server.py # opens the MCP inspector
uv run pytest # 5 tests
```
`AP_PROFILE=analyst` or `AP_PROFILE=processor` in front of the run command gives you a
smaller server. More on that below.
## What you get
### Tools

Eleven tools. The first five read and write bills and vendors, the same shape as any ERP
connector. The interesting ones are in the middle: `check_duplicates`, `match_invoice` and
`recommend_approval` are where the AP judgment lives. They call plain functions in
`ap_logic.py` and hand back a dict with the reasons, so a person can see why a bill got held.
`request_payment_release` is the one to notice. It queues approved bills for a payment run and
that is all it does. There is no tool that releases the run or sends money. That part stays in
the ERP, done by a person, on purpose.
### Prompts

One prompt for now, the weekly payment run. It tells the assistant which resource to read and
which tools to run in what order. It carries no data itself.
### Resources

Things the assistant reads often and should not need a tool call for: all bills, one bill by
id, bills due in the next N days, and the tolerance policy.
## Profiles
Not every deployment should get every tool. `AP_PROFILE` decides what gets registered when the
server starts:
| profile | can |
|---|---|
| `analyst` | read bills and vendors, run the checks, see the audit log |
| `processor` | all of the above, plus create bills and record bank changes |
| `full` | all of the above, plus queue payment runs |
An analyst server does not refuse `create_bill`. It never lists it, so the model cannot try.
## The files
| file | |
|---|---|
| `server.py` | the tools, resources, prompt, and the profile setup at the bottom |
| `ap_logic.py` | matching, duplicate check, screening. No MCP in here, so it tests on its own |
| `erp.py` | the mock ERP. Swap this for a real backend and the server stays the same |
| `data.json` | 3 vendors, 3 purchase orders, 5 bills |
| `test_server.py` | the logic on its own, one bill through the whole flow, and a check on tool names |
The five bills are set up so each one shows something: B-1001 is clean, B-1002 is a duplicate of
it that came in through a different channel, B-1003 bills more than was received, B-1004 is from
a vendor that changed bank details by email last week and has a memo asking for a payment
change, and B-1005 has no purchase order.
TDQS
Scored across 11 tools
Most tools have clearly distinct roles (create/get/search bills, match against PO, check duplicates, recommend approval, release payments). The only potential confusion is between check_duplicates and match_invoice, both bill-validation steps, but their descriptions ('earlier bills' vs 'PO and goods receipt') clearly separate them.
All tools follow a consistent snake_case verb_noun pattern (get_bill, search_bills, create_bill, match_invoice, request_payment_release). No mixing of conventions or vague verbs; the pattern is predictable throughout.
11 tools is well-scoped for a payables/AP workflow. Each tool maps to a distinct step (bill CRUD, validation, approval recommendation, payment release, audit) and earns its place without redundancy.
Core bill lifecycle (create, get, search, validate, recommend, release) and audit are covered, but there is no update_bill/void, no vendor search or create (only get_vendor by id), and no explicit approve action. These are workable gaps for a controlled approval workflow but leave some dead ends.