Turn an accountant's "missing invoices" list into a ready-to-send reply with the files attached.
Fetch is a local toolkit your own AI agent drives. It runs on your machine, under your own logins, and keeps no credentials of its own.
Small companies (and their agents) that get a periodic "please send the invoices we are missing" message from their accountant, and want to answer it fast.
- Reads the accountant's list, in any format (pasted text, a table, an email).
- Finds each invoice. First in your Gmail inbox. For anything not there, it hands your agent a clear task to fetch it from the vendor's portal or e-invoice platform.
- Drafts a reply to the accountant with the found PDFs attached, plus a note on anything still missing. You review the draft and send it.
It can also classify and file invoices, copy them to a folder of your choice (Dropbox, a shared drive, or any local folder), and build a SEPA payment file. See the tool list below.
- No stored credentials. It uses your local
gog(Gmail) andclaudeCLIs. - It drafts, it does not send. Outgoing replies are Gmail drafts you review first.
- Local audit log. Every fetch, download, and draft is recorded in
output/audit.log.
Full posture, hardening, and the honest capability boundary: SECURITY.md.
Fetch is an MCP server, and the easy way to set it up and use it is to paste the
block below to your MCP-capable AI agent (such as Claude Code). There is no clone
or install step: uvx runs Fetch straight from this repo.
You need uv, plus your own
gog (Gmail, brew install openclaw/tap/gogcli) and
claude CLIs. The agent can check
those for you and help you install them.
Set up the "fetch" invoice toolkit for me, then help me use it.
1. Add an MCP server named "fetch-invoices" to your config: command uvx, args --from git+https://github.com/silly-geese/fetch fetch-mcp. Set env FETCH_CONFIG to a new file ~/fetch/config.yml and FETCH_OUTPUT_DIR to ~/fetch/output (use absolute paths).
2. Create ~/fetch/config.yml from the template at https://github.com/silly-geese/fetch/blob/main/config.example.yml, and ask me for my company names, the folder where each company's invoices should be filed (any folder, e.g. a Dropbox or shared-drive path), and bank (debtor) details to fill it in.
3. Call health_check and confirm gog and claude are installed and signed in. If not, walk me through it.
From then on, when I forward you a list of missing invoices: call parse_missing_list, then find each one in my Gmail with search_inbox, get_message, and download_attachment. For anything not in the inbox, call plan_retrieval and fetch it with your own browser. Finally draft_reply to the accountant with the files attached and a short note on anything still missing. Always leave a draft for me to review; never send.
The MCP entry it adds looks like this:
Prefer to run it yourself or use the CLI? Clone it and point the server at the clone:
git clone https://github.com/silly-geese/fetch.git
cd fetch
cp config.example.yml config.yml # fill in your details
uv sync
./fetch onboarding # checks your setup{
"mcpServers": {
"fetch-invoices": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/your/clone", "fetch-mcp"]
}
}
}./fetch mcp runs the same server.
| Tool | What it does |
|---|---|
health_check |
Check gog, claude, Gmail auth, and config.yml |
parse_missing_list |
Turn a free-form missing list into a checklist |
search_inbox, get_message, download_attachment |
Find and pull invoice PDFs from Gmail |
plan_retrieval |
Build tasks to fetch invoices that are not in the inbox |
draft_reply |
Draft a reply to the accountant with files attached (never sends) |
read_audit |
Read the local audit log |
reconcile, build_report |
Optional: match the list to what you found and write a summary |
fetch_invoices, classify_invoice, list_invoices |
Fetch and classify invoices from Gmail |
copy_to_dropbox, generate_payments, archive_thread |
File to a folder (Dropbox or any path), build SEPA payments, archive threads |
Full reference and the step-by-step workflow: SKILL.md.
Fetch reads Gmail through your local gog CLI, so the server itself never holds
your credentials. There are two ways to give gog access.
Standard, best for hands-off use. Create a Google OAuth client once, then sign in. This stores a refresh token and keeps working without you:
-
In Google Cloud Console: create a project, enable the Gmail API, create an OAuth client of type "Desktop app", and download the client secret JSON.
-
Register it and sign in (a browser opens for consent):
gog auth credentials <client_secret.json> gog auth add you@example.com --services gmail gog auth doctor --check
Quick, no Cloud project. If you already have a Gmail access token, gog can
use it directly with no OAuth client setup at all:
export GOG_ACCESS_TOKEN=<a gmail.modify-scoped token>The token needs the https://www.googleapis.com/auth/gmail.modify scope (read
alone is enough to search and download, but drafting and archiving need modify).
Access tokens last about an hour and are not refreshed on this path, so it suits
a single session rather than a long unattended run. The easiest way to mint a
token without your own Cloud project is Google's OAuth Playground.
Either way, health_check tells you whether Gmail is connected.
If your agent already has its own Gmail connector (for example Claude Cowork's
Google Workspace connector), it can do much of the inbox work without gog:
searching the inbox, reading threads, and archiving. You connect Gmail once in
the agent's own UI, with no Cloud Console setup.
Two steps still go through gog, because they need the real file:
- Downloading the invoice PDF to disk. A Gmail connector returns an attachment's name and id but not its bytes, and Fetch classifies, files, and attaches actual files on disk.
- Attaching those PDFs to the draft reply.
draft_replyreliably attaches local files; building a draft with attachments straight from the connector is not confirmed yet.
So a clean split is: let the connector search, read, and archive; use Fetch
(which calls gog) for download_attachment, classify_invoice(pdf_path=…),
and draft_reply(attachments=[…]). Both classify_invoice and draft_reply
take any local path, so however a PDF gets onto disk, Fetch can use it.
Inside Cowork, gog runs in the sandbox VM, so enable network egress and
allowlist googleapis.com and accounts.google.com so it can reach Google.
This last part is worth a quick test in your own Cowork session; we cannot
confirm the egress allowlist behaviour from outside it.
Some invoices arrive through a vendor's billing portal or an e-invoice platform, not email. plan_retrieval turns each missing item into a task your agent can act on.
Teach it where to look per vendor by adding recipes to config.yml:
vendor_sources:
acme: # matched against the vendor name
portal_url: "https://billing.acme.com/invoices"
login_hint: "log in with the company Google account, invoices under Billing"
notes: "monthly subscription, issued on the 1st"With a recipe, the task tells your agent the exact URL and how to log in. Without one, it suggests the usual places (the vendor portal, your e-invoice platform, or asking the vendor to resend). Your agent does the fetching with its own browser. The toolkit never holds portal logins. Add recipes over time and they become a reusable library for your company.
All settings live in config.yml (gitignored). Copy config.example.yml and edit:
default_slug: my-company
companies:
my-company: "My Company OÜ"
dropbox_dirs: # any folder, not only Dropbox
my-company: "/path/to/My Company/Invoices"
debtor_accounts:
my-company:
name: "My Company OÜ"
iban: "EE000000000000000000"
bic: "LHVBEE22"
vendor_sources: {} # optional, see "Fetch invoices that are not in your inbox"Point at a config elsewhere with FETCH_CONFIG. Change the output folder with FETCH_OUTPUT_DIR.
fetch onboarding Check prerequisites
fetch mcp Run the MCP server (stdio) for agents
fetch invoices fetch Fetch and classify invoices from Gmail
fetch invoices to-dropbox Copy classified invoices to each company's folder
fetch invoices generate-payments Build SEPA payment XML for to-pay invoices
fetch invoices reconcile Reconcile a missing-invoice list
fetch invoices archive Review and archive Gmail threads
Use -h on any command for options.
uvx ruff check src/ # lint
uvx ruff format src/ # formatStyle: single quotes, spaces, Python 3.11+. Config is in pyproject.toml.
fetch Shell entry point (uv run python -m src)
config.example.yml Config template (copy to config.yml)
src/
__init__.py Click root group (+ the mcp command)
mcp_server.py MCP server: all the agent tools
onboarding.py Setup check
invoices/
reconcile.py Parse a missing list, match it, build a report
retrieval.py Plan fetching invoices not in the inbox
audit.py Local audit log
gmail.py Gmail search, download, draft reply (via gog)
classify.py AI classification (claude)
payment_xml.py SEPA payment XML
config.py, models.py, helpers.py, process.py, dropbox.py, summary.py
Apache-2.0. See LICENSE.
{ "mcpServers": { "fetch-invoices": { "command": "uvx", "args": ["--from", "git+https://github.com/silly-geese/fetch", "fetch-mcp"], "env": { "FETCH_CONFIG": "/absolute/path/to/your/config.yml", "FETCH_OUTPUT_DIR": "/absolute/path/to/your/output" } } } }