An MCP server for managing FreeAgent accounting data
3.0K
An MCP server that lets AI assistants like Claude manage your FreeAgent accounting data — track time, create invoices, and query projects.
Forked from markpitt/freeagent-mcp.
Warning: This server can create, modify, and delete real financial data in your FreeAgent account, including invoices and timeslips. It has not been extensively tested and is provided as-is with no warranty. Use entirely at your own risk. The authors accept no responsibility for any data loss or unintended changes to your accounting records.
create_invoice and update_invoice accept a project_ids array (numeric IDs or URLs) listing which projects the invoice covers. They refuse by default in two situations, surfacing a clear menu of next steps so the agent can ask you what to do:
include_timeslips (with a grouping mode) to attach them, or omit_unbilled_timeslips: true to leave them — this prevents accidentally invoicing a project while leaving billable time stranded.project_ids.length > 1) and you haven't picked which project's invoice sequence the new invoice should be numbered from. Pass numbering_source set to one of the project IDs to use that project's per-project sequence (if it has one configured), or "org-wide" to use the organisation-wide sequence.Manage employee expenses — money a team member spent that the company should account for. Type /mcp__freeagent__log-expenses in Claude Code for a guided receipts → categorise → review → create flow with an explicit approval gate. Or just ask:
Amounts are entered as a positive number; out-of-pocket spending — money owed back to the claimant — is the default. Set refund_due when the claimant owes money back to the company instead. Categories and claimants can be given by name ("Travel", "Jane Smith") and are resolved automatically; the claimant defaults to you.
Mileage claims use create_mileage_expense: give the miles, vehicle type, and — for cars and motorcycles — the engine. Engine type and size are checked against the official mileage settings for the claim date, so a wrong value comes back with the valid options listed; engine_type defaults to Petrol.
create_expense and update_expense also handle the advanced modes: rebillable expenses (associate with a project, optionally rebill_type cost/markup/price), recurring expenses (a recurring frequency), and foreign-currency expenses (currency plus an optional native-currency amount). create_expenses posts a whole batch in one call.
Receipts attach via an opt-in staging volume (see Optional: enable receipt attachments). The staging directory is a shared folder, so the agent calls get_staging_directory, copies the receipt file in directly, and passes that path to create_expense — fast and lossless, no base64 round-trip. FreeAgent caps attachments at 5 MB.
You'll need Docker, a FreeAgent account with API access, and OAuth credentials from the FreeAgent Developer Dashboard.
export FREEAGENT_CLIENT_ID="your_client_id"
export FREEAGENT_CLIENT_SECRET="your_client_secret"
node scripts/get-oauth-tokens.js
The easiest way to run the server is to pull the pre-built image from Docker Hub. Add the following to your Claude Code MCP settings (~/.claude/settings.json):
{
"mcpServers": {
"freeagent": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "FREEAGENT_CLIENT_ID",
"-e", "FREEAGENT_CLIENT_SECRET",
"-e", "FREEAGENT_ACCESS_TOKEN",
"-e", "FREEAGENT_REFRESH_TOKEN",
"eddgrant/freeagent-mcp"
],
"env": {
"FREEAGENT_CLIENT_ID": "your_client_id",
"FREEAGENT_CLIENT_SECRET": "your_client_secret",
"FREEAGENT_ACCESS_TOKEN": "your_access_token",
"FREEAGENT_REFRESH_TOKEN": "your_refresh_token"
}
}
}
}
The Docker Hub image uses the following tagging convention:
| Tag | Description |
|---|---|
latest | Most recent build from main — always up to date |
sha-<short> | Immutable tag for a specific commit, useful for pinning a known-good version |
pr-<number> | Latest build from a pull request, for testing changes before they land on main |
To pin to a specific version, replace eddgrant/freeagent-mcp with e.g. eddgrant/freeagent-mcp:sha-d775a51 in the config above.
If you prefer to build the image yourself:
git clone https://github.com/eddgrant/freeagent-mcp.git
cd freeagent-mcp
docker build -t freeagent-mcp .
Then replace eddgrant/freeagent-mcp with freeagent-mcp in the MCP settings above.
Attaching evidence files (PDF/JPEG/PNG receipts) to expenses needs a shared filesystem volume between your host and the Docker container. The volume is the channel through which the agent hands bytes to the FreeAgent MCP without round-tripping them through the model's context window.
Skip this if you don't need to attach receipts — expenses without attachments work without the volume.
One-time setup:
# Create the staging directory on the host. IMPORTANT: do this BEFORE
# the first container run. If the directory doesn't exist when Docker
# mounts it, Docker creates it as root and the container (running as
# your user) won't be able to write to it.
mkdir -p /tmp/freeagent-mcp
If you've already hit that ownership trap, fix it once with:
sudo chown -R "$USER" /tmp/freeagent-mcp
MCP config additions — three extra args to your docker run invocation:
{
"mcpServers": {
"freeagent": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--user", "1000:1000", // run as your host UID:GID
"-v", "/tmp/freeagent-mcp:/tmp/freeagent-mcp", // shared staging dir
"-e", "FREEAGENT_EVIDENCE_BASE", // tells the server where it lives
"-e", "FREEAGENT_CLIENT_ID",
"-e", "FREEAGENT_CLIENT_SECRET",
"-e", "FREEAGENT_ACCESS_TOKEN",
"-e", "FREEAGENT_REFRESH_TOKEN",
"eddgrant/freeagent-mcp"
],
"env": {
"FREEAGENT_EVIDENCE_BASE": "/tmp/freeagent-mcp",
"FREEAGENT_CLIENT_ID": "...",
"FREEAGENT_CLIENT_SECRET": "...",
"FREEAGENT_ACCESS_TOKEN": "...",
"FREEAGENT_REFRESH_TOKEN": "..."
}
}
}
}
Replace 1000:1000 with the output of id -u:id -g on your host. The path can be anything writable by your user — /tmp/freeagent-mcp, ~/.cache/freeagent-mcp, etc. — but it must be the same on both sides of the -v mount.
A per-session subdirectory is created inside FREEAGENT_EVIDENCE_BASE at startup and removed on shutdown; stale subdirectories from previous runs are auto-reaped after 24 hours, so you never need to clean up manually.
How to tell it's working: call get_staging_directory. A { "ready": true, "path": "..." } response means the volume is mounted; { "ready": false, "path": null } means it isn't (expenses without attachments still work).
When a PR builds an image (e.g. eddgrant/freeagent-mcp:pr-42) and you want to actually use it from a Claude Code session — without polluting your normal ~/.claude/settings.json and without losing your stable MCP setup — use:
./scripts/test-image.sh pr-42
This creates a self-contained temp directory with:
.mcp.json pointing at the image, server-named freeagent_test so it doesn't collide with your real freeagent serverevidence/ staging directory mounted into the container at the same path on both sidesCLAUDE.md that Claude Code auto-loads, containing a smoke-test checklistThe script prints cd <temp-dir> && claude for you to run. Credentials pass through from your shell via bare -e VAR flags — nothing is written to disk. When you're done, rm -rf the temp dir.
./scripts/test-image.sh # latest from Docker Hub
./scripts/test-image.sh sha-abc1234 # specific commit
./scripts/test-image.sh --image fa-dev # local image (skips Docker Hub prefix)
./scripts/test-image.sh pr-42 --no-staging # test the unmounted-volume code path
The log-expenses prompt is invoked via /mcp__freeagent_test__log-expenses in that session.
Required env vars in your shell: FREEAGENT_CLIENT_ID, FREEAGENT_CLIENT_SECRET, FREEAGENT_ACCESS_TOKEN, FREEAGENT_REFRESH_TOKEN.
| Tool | Description |
|---|---|
list_timeslips | List timeslips with optional date, project, task, user, and status filters |
get_timeslip | Get a single timeslip by ID |
create_timeslip | Create a new timeslip |
create_timeslips | Batch create multiple timeslips (with deduplication) |
update_timeslip | Update an existing timeslip |
delete_timeslip | Delete a timeslip |
start_timer | Start a timer on a timeslip |
stop_timer | Stop a running timer |
create_invoice | Create an invoice, optionally attaching unbilled timeslips |
update_invoice | Update invoice fields or line item descriptions |
list_invoices | List invoices with optional filters |
get_invoice | Get a single invoice by ID |
download_invoice_pdf | Download an invoice as base64-encoded PDF |
delete_invoice | Delete an invoice (requires confirmation for non-draft invoices) |
mark_invoice_as_draft | Transition a sent invoice back to draft status |
mark_invoice_as_sent | Transition a draft invoice to sent status |
list_categories | List FreeAgent categories (nominal codes) grouped by type |
list_bank_accounts | List all bank accounts |
list_bank_transactions | List bank transactions for a bank account with optional date filtering |
list_bank_transaction_explanations | List categorised bank transaction explanations with optional date filtering |
list_bills | List bills (supplier invoices) with optional date, contact, project, and status filter |
get_bill | Get a single bill by ID, including line items and categories |
create_project | Create a new project |
list_projects | List projects with optional status filter |
create_task | Create a new task for a project |
list_tasks | List tasks, optionally filtered by project |
list_users | List users in the organisation |
get_current_user | Get the currently authenticated user |
get_staging_directory | Report the session staging directory for copying receipt files in directly |
list_expenses | List expenses with optional date, project, view, and claimant filters |
get_expense | Get a single expense by ID |
create_expense | Create an employee expense (category/claimant by name, receipts via staging) |
update_expense | Update an existing expense |
delete_expense | Delete an expense (requires confirmation if rebilled onto an invoice) |
get_mileage_settings | Get valid engine types, sizes, and mileage rates by date period |
create_mileage_expense | Log a mileage claim (engine validated against the dated mileage settings) |
create_expenses | Batch-create multiple expenses in a single call |
| Prompt | Description |
|---|---|
/mcp__freeagent__log-expenses | Guided expense entry: gather receipts → categorise → review → create with a gate |
# Build the project
npm run build
# Watch for changes
npm run watch
# Build Docker image
docker build -t freeagent-mcp .
# Run the MCP inspector
npm run inspector
This project is licensed under the MIT License - see the LICENSE file for details.
Content type
Image
Digest
sha256:ac1334869…
Size
79.5 MB
Last updated
4 months ago
docker pull eddgrant/freeagent-mcp