{
  "markdown": "# FreeAgent MCP Server\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server for [FreeAgent](https://www.freeagent.com) accounting. Gives Claude (or any MCP client) the ability to reconcile bank transactions with receipts, claim expenses and mileage, raise and chase invoices, record supplier bills, log time against projects, and read the company's financial position.\n\n## What access this server requires\n\n### FreeAgent\n\nThe FreeAgent OAuth credentials grant **full access** to the connected FreeAgent account. This server uses that access to:\n\n| Action | Tools that use it |\n|--------|------------------|\n| Read bank accounts | `freeagent_list_bank_accounts` |\n| Read bank transactions and explanations | `freeagent_list_transactions` |\n| Update transaction explanations (category, description, project, rebilling, VAT, approval, attachments) | `freeagent_explain_transaction` |\n| Read, create, amend and delete expense claims | `freeagent_list_expenses`, `freeagent_create_expense`, `freeagent_create_mileage_expense`, `freeagent_update_expense`, `freeagent_delete_expense` |\n| Read the chart of accounts | `freeagent_list_categories` |\n| Read, create, amend and delete projects | `freeagent_list_projects`, `freeagent_create_project`, `freeagent_update_project`, `freeagent_delete_project` |\n| Read, create, amend and delete contacts | `freeagent_list_contacts`, `freeagent_create_contact`, `freeagent_update_contact`, `freeagent_delete_contact` |\n| Read, raise, edit and change the status of invoices | `freeagent_list_invoices`, `freeagent_get_invoice`, `freeagent_create_invoice`, `freeagent_update_invoice`, `freeagent_update_invoice_status` |\n| Read, record and amend supplier bills | `freeagent_list_bills`, `freeagent_get_bill`, `freeagent_create_bill`, `freeagent_update_bill` |\n| Read, log and amend time against project tasks | `freeagent_list_tasks`, `freeagent_create_task`, `freeagent_update_task`, `freeagent_list_timeslips`, `freeagent_create_timeslip`, `freeagent_update_timeslip` |\n| Read accounting reports | `freeagent_profit_and_loss`, `freeagent_trial_balance`, `freeagent_aged_debtors`, `freeagent_aged_creditors`, `freeagent_tax_timeline`, `freeagent_company_summary` |\n| Delete expenses, invoices, bills, contacts, projects, tasks and timeslips | `freeagent_delete_expense`, `freeagent_delete_invoice`, `freeagent_delete_bill`, `freeagent_delete_contact`, `freeagent_delete_project`, `freeagent_delete_task`, `freeagent_delete_timeslip` |\n\nFreeAgent does not offer granular OAuth scopes — authorising an app grants access to all of the above.\n\n**Destructive operations.** The delete tools are flagged `destructiveHint: true` so your MCP client can prompt before running them, as is `freeagent_update_invoice_status` (its `mark_as_cancelled` transition voids an issued invoice). The three whose loss is unrecoverable — `freeagent_delete_expense`, `freeagent_delete_contact` and `freeagent_delete_project` — additionally require `confirm: true`, so a client that auto-approves tool calls still cannot trigger them by accident. The server never deletes bank transactions, and never emails anything to your clients — status transitions change status only.\n\n**Outbound fetches.** `freeagent_explain_transaction`, `freeagent_create_expense`, `freeagent_update_expense`, `freeagent_create_bill` and `freeagent_update_bill` each accept a `fileUrl` to download a receipt or invoice. That URL is treated as untrusted input: only `http`/`https` are allowed, hosts resolving to loopback, link-local, or private addresses are refused (on the initial request *and* on every redirect), and downloads are capped at 10 MB.\n\n**Local file reads.** Those same tools accept a `filePath`, and the server reads that file from the host it runs on. The path must be absolute, and symlinks are resolved before the check. Over stdio the server runs as you, so this is no more access than the client already has. In **HTTP mode the caller is remote**, so local paths are refused outright unless you set `FREEAGENT_ATTACHMENT_ROOTS` to the directories that may be read (colon-separated); anything outside them is refused. Set it in stdio mode too if you want to bound what a prompt-injected model can attach.\n\n**Truncation.** List tools page through results and report `mayHaveMore`; when true, any total they return covers only the records fetched and is named `totalOutstandingForReturned`. The `freeagent_aged_debtors` and `freeagent_aged_creditors` reports page to exhaustion and return `complete: true` — treat `complete: false` as an incomplete figure. Records whose due date is missing or unparseable are counted in a separate `unknown_due_date` bucket rather than being assumed not yet due.\n\n### Email and file sources (external — not provided by this server)\n\nThis server has no email tools. For receipt/invoice search to work automatically, connect one or more of the following MCP servers alongside this one:\n\n- **Gmail** (e.g. `mcp__claude_ai_Gmail`) — personal email\n- **Microsoft 365 / Outlook** (e.g. `mcp__claude_ai_microsoft-365`) — business email\n\nWhen both are connected, Claude will search all of them automatically for matching receipts before asking you to provide a file manually.\n\n---\n\n## Tools\n\n### Banking and expenses\n\n| Tool | Description |\n|------|-------------|\n| `freeagent_list_bank_accounts` | List all bank accounts and their IDs |\n| `freeagent_list_transactions` | List transactions (unexplained / explained / all / marked_for_review) with date filters |\n| `freeagent_explain_transaction` | Update, approve, rebill or attach a receipt to a transaction explanation |\n| `freeagent_list_categories` | List the full chart of accounts — all four category groups |\n| `freeagent_list_expenses` | List expense claims by date or project, with `unbilledOnly` for costs queued to bill on and `untaggedRebillOnly` for project-tagged costs that will never be billed |\n| `freeagent_create_expense` | Create an expense claim with optional receipt (local path, URL or base64), project tag with rebill type/factor, EC VAT status and bank-transaction auto-matching |\n| `freeagent_update_expense` | Update an existing expense — attach or replace the receipt, set the rebill treatment, retag project/category, correct date, amount or VAT |\n| `freeagent_create_mileage_expense` | Create a mileage claim with engine type/size for fuel VAT, optionally rebilled to a project; the rate comes from the account's own mileage settings |\n| `freeagent_delete_expense` | Delete an expense filed in error (needs `confirm: true`) |\n\n### Contacts, invoicing and bills\n\n| Tool | Description |\n|------|-------------|\n| `freeagent_list_contacts` | List clients and suppliers, with an optional name/email filter |\n| `freeagent_create_contact` | Create a client or supplier, with VAT registration number and default payment terms |\n| `freeagent_update_contact` | Update a contact, add its VAT number, or hide it (`status: \"Hidden\"`) |\n| `freeagent_delete_contact` | Delete a contact (needs `confirm: true`) |\n| `freeagent_list_invoices` | List invoices by view (`overdue`, `open_or_overdue`, `draft`, `paid`…) with the total outstanding |\n| `freeagent_get_invoice` | Fetch one invoice in full, including line items |\n| `freeagent_create_invoice` | Raise an invoice with line items and EC VAT status — always created as a **draft** |\n| `freeagent_update_invoice` | Edit a draft invoice — dates, project, VAT status, discount, and add/edit/remove line items |\n| `freeagent_update_invoice_status` | Mark an invoice as sent, draft, scheduled or cancelled (no email is sent) |\n| `freeagent_delete_invoice` | Delete an invoice |\n| `freeagent_list_bills` | List supplier bills with the total outstanding |\n| `freeagent_get_bill` | Fetch one bill in full, including line items and their URLs (needed to edit lines) |\n| `freeagent_create_bill` | Record a supplier bill, with optional invoice attachment (local path, URL or base64), EC VAT status, project allocation and rebill treatment |\n| `freeagent_update_bill` | Update a bill — reference, dates, VAT status, project, rebill treatment, attachment and line items |\n| `freeagent_delete_bill` | Delete a bill |\n\n### Time tracking\n\n| Tool | Description |\n|------|-------------|\n| `freeagent_list_tasks` | List project tasks (time is always logged against a task) |\n| `freeagent_create_task` | Create a project task with its billing rate |\n| `freeagent_update_task` | Rename a task, change its billing rate, or close it (`status: \"Completed\"`) |\n| `freeagent_delete_task` | Delete a task with no time logged against it |\n| `freeagent_list_timeslips` | List logged time for a date range, with totals per project; `view: \"unbilled\"` finds uninvoiced work |\n| `freeagent_create_timeslip` | Log time against a project task |\n| `freeagent_update_timeslip` | Correct a timeslip's hours, date, task or comment |\n| `freeagent_delete_timeslip` | Delete a timeslip |\n\n### Projects\n\n| Tool | Description |\n|------|-------------|\n| `freeagent_list_projects` | List projects, for tagging expenses, invoices, bills and time |\n| `freeagent_create_project` | Create a project against a client contact — only contact and name are required |\n| `freeagent_update_project` | Rename, rebudget, change billing rate or close a project |\n| `freeagent_delete_project` | Delete a project with nothing booked against it (needs `confirm: true`) |\n\n### Reporting\n\n| Tool | Description |\n|------|-------------|\n| `freeagent_profit_and_loss` | Income, expenses, operating profit, corporation tax estimate and retained profit |\n| `freeagent_trial_balance` | Balance on every nominal account |\n| `freeagent_aged_debtors` | Unpaid customer invoices bucketed by age (not yet due, 1–30, 31–60, 61–90, 90+ days) |\n| `freeagent_aged_creditors` | Unpaid supplier bills bucketed by age |\n| `freeagent_tax_timeline` | Upcoming VAT, corporation tax and Companies House deadlines with amounts due |\n| `freeagent_company_summary` | Company details, VAT registration and accounting year end |\n\n---\n\n## Prerequisites\n\n### FreeAgent OAuth credentials\n\n1. Log in to FreeAgent → **Settings → Developer API**.\n2. Create an OAuth application. Set the redirect URI to `http://localhost:8080/callback`.\n3. Note your **Client ID** and **Client Secret**.\n4. Run the bundled auth command to complete the OAuth flow and save a refresh token to `.mcp.json` automatically:\n\n```bash\nnpx @oxygenbubbles/freeagent-mcp-server auth\n```\n\nThe command prompts for your Client ID and Client Secret, opens the FreeAgent authorization page in your browser, listens for the callback, exchanges the code for a long-lived refresh token, and writes everything to `.mcp.json` in the current directory. If `.mcp.json` already exists, it updates just the `freeagent` entry.\n\n---\n\n## Installation\n\n```bash\ngit clone https://github.com/OxygenBubbles/freeagent-mcp-server.git\ncd freeagent-mcp-server\nnpm install\nnpm run build\n```\n\n---\n\n## Configuration\n\nAll settings are read from environment variables.\n\n### Required\n\n| Variable | Description |\n|----------|-------------|\n| `FREEAGENT_CLIENT_ID` | OAuth client ID |\n| `FREEAGENT_CLIENT_SECRET` | OAuth client secret |\n| `FREEAGENT_REFRESH_TOKEN` | Long-lived refresh token |\n\n### Optional\n\n| Variable | Description |\n|----------|-------------|\n| `VENDOR_CATEGORIES` | JSON object extending the built-in vendor → category mapping (see below) |\n| `MILEAGE_CATEGORY_URL` | FreeAgent category URL for mileage expenses (default `/v2/categories/249`, the standard Mileage category) |\n| `MILEAGE_RATE_PENCE` | Fallback pence-per-mile rate for the estimate, used only when FreeAgent's own mileage settings cannot be read |\n| `HMRC_RATE_HIGH_PENCE` | HMRC high-band rate in pence, used as a last-resort fallback (default `45`) |\n| `HMRC_RATE_LOW_PENCE` | HMRC low-band rate in pence, used as a last-resort fallback (default `25`) |\n| `HMRC_THRESHOLD_MILES` | Miles per tax year before the low band kicks in (default `10000`) |\n| `ORS_API_KEY` | [OpenRouteService](https://openrouteservice.org) API key for drive-distance lookups |\n| `GOOGLE_MAPS_API_KEY` | Google Maps API key for drive-distance lookups (alternative to ORS) |\n| `PORT` | If set, serves over HTTP on this port instead of stdio |\n| `FREEAGENT_ATTACHMENT_ROOTS` | Colon-separated directories that `filePath` attachments may be read from. Required for local paths in HTTP mode; optional (and recommended) over stdio |\n| `AUTH_TOKEN` | Bearer token required on every HTTP request. Strongly recommended whenever `PORT` is set |\n| `FREEAGENT_DEBUG` | Set to `1` to log every request and error response to stderr. Tokens, credentials and file payloads are redacted |\n\n### Built-in vendor → category mappings\n\nThe server ships with mappings for common vendors, using FreeAgent's standard UK nominal codes:\n\n- **Web Hosting (268)** — IONOS, AWS, Netlify, Vercel, Heroku, DigitalOcean, Cloudflare\n- **Computer Software (269)** — OpenAI, Anthropic, GitHub, Stripe, Google, Microsoft, Zoom, Notion, Dropbox, Slack, Adobe, Fastmail, Mailchimp\n- **Accommodation and Meals (285)** — Booking.com, Hotels.com, Premier Inn, Travelodge, Airbnb\n- **Travel (365)** — Trainline, LNER, Uber\n\nCheck these against your own chart of accounts with `freeagent_list_categories` — nominal codes can be customised per account. Override or extend via `VENDOR_CATEGORIES`:\n\n```bash\nVENDOR_CATEGORIES='{\"ACME CORP\":\"/v2/categories/285\",\"NETFLIX\":\"/v2/categories/269\"}'\n```\n\n---\n\n## Claude Desktop setup\n\nAdd to `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"freeagent\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/freeagent-mcp-server/dist/index.js\"],\n      \"env\": {\n        \"FREEAGENT_CLIENT_ID\": \"...\",\n        \"FREEAGENT_CLIENT_SECRET\": \"...\",\n        \"FREEAGENT_REFRESH_TOKEN\": \"...\"\n      }\n    }\n  }\n}\n```\n\n---\n\n## Usage examples\n\n```\nList my unexplained Starling transactions for April 2026\n\nApprove explanation 12345678 and attach the receipt at ~/Downloads/invoice.pdf\n\nCreate an expense for the £22.80 IONOS charge on 3 April, rebilled to the Example Client project at cost — the receipt is at ~/Desktop/ionos.png\n\nThat IONOS expense should be Reverse Charge, not UK/Non-EC — fix it\n\nLog 24 miles for a client visit from the office to a customer site on 10 April\n\nAdd a line to draft invoice 4471 for 2 days' consultancy at £650\n\nWhich April expenses are tagged to a project but not set to rebill?\n\nShow me everything queued to bill on to Example Client that isn't on an invoice yet\n```\n\n---\n\n## Mileage rates\n\nMileage is a special FreeAgent category: you submit the **miles and vehicle type**, and FreeAgent calculates the claim value from the mileage rate configured on the account. That figure is what appears in your accounts and HMRC reporting, so the server does not attempt to override it.\n\nThe estimate returned alongside the filed amount uses that same rate: the server reads `GET /v2/expenses/mileage_settings` and applies the band published for the journey's date and vehicle. The response records where the rate came from in `estimateSource`:\n\n| `estimateSource` | Meaning |\n|------------------|---------|\n| `freeagent_mileage_settings` | The account's own published rate — the normal case |\n| `argument` | A `ratePence` you passed explicitly, which always wins |\n| `MILEAGE_RATE_PENCE` | Settings could not be read; the environment variable was used |\n| `hmrc_defaults` | Settings could not be read and no override was set; the built-in HMRC bands were used |\n\nThe last two are fallbacks, and the response says so in `notes`. When the estimate and the filed amount still differ, that is worth a look:\n\n> FreeAgent filed £46.20 using the mileage rate configured on the account; the estimate from 84 miles @ 45p/mile (FreeAgent mileage settings) was £37.80.\n\nHMRC's approved rates are 45p/mile for the first 10,000 business miles in the tax year and 25p above it, and anything paid above the approved rate is a taxable benefit.\n\nTo reclaim the VAT on the fuel element, pass `engineType` and `engineSize` (and `haveVatReceipt`) — FreeAgent cannot calculate it without them.\n\n---\n\n## Development\n\n```bash\nnpm run dev        # watch mode (tsx)\nnpm run build      # compile TypeScript → dist/\nnpm start          # run compiled server\nnpm test           # run unit tests once\nnpm run test:watch # watch mode for tests\n```\n\n---\n\n## Claude Code setup\n\nCreate `.mcp.json` in your project directory (or `~/.mcp.json` for global access):\n\n```json\n{\n  \"mcpServers\": {\n    \"freeagent\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/freeagent-mcp-server/dist/index.js\"],\n      \"env\": {\n        \"FREEAGENT_CLIENT_ID\": \"...\",\n        \"FREEAGENT_CLIENT_SECRET\": \"...\",\n        \"FREEAGENT_REFRESH_TOKEN\": \"...\"\n      }\n    }\n  }\n}\n```\n\n---\n\n## HTTP mode\n\nSet `PORT` to run as an HTTP server (for webhooks, iPhone Shortcuts, Power Automate):\n\n```bash\nPORT=3000 AUTH_TOKEN=a-long-random-string node dist/index.js\n```\n\nAlways set `AUTH_TOKEN` when exposing HTTP mode — every request must include `Authorization: Bearer <AUTH_TOKEN>` or it is rejected with 401. Without `AUTH_TOKEN` the server starts anyway but prints a warning to stderr and accepts all requests; only do that on a trusted loopback interface.\n\n---\n\n## Security\n\n- Credentials are environment variables, never in code\n- FreeAgent tokens are cached in memory and refreshed automatically\n- Transactions are never approved without a confirmed receipt or explicit instruction\n- The server never creates new categories — only selects from existing ones\n- Receipt URLs are fetched through an SSRF guard: the address validated is the address dialled, every redirect hop is re-checked, proxies are bypassed and downloads are size-capped\n- Local receipt paths must be absolute and are resolved through symlinks before use; in HTTP mode they are refused unless `FREEAGENT_ATTACHMENT_ROOTS` names the directories that may be read\n- The three deletes whose loss is unrecoverable — expenses, contacts and projects — require `confirm: true` in addition to the client's own prompt\n- `.mcp.json` is excluded from git via `.gitignore`\n\n---\n\n## Licence\n\nMIT\n",
  "bytes": 18082,
  "sha": "85c31b207398be860dc1fc0841d4abf946fda0e2a7cd4a64178e5578fe77f4e4",
  "repo_slug": "oxygenbubbles/freeagent-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_oxygenbubbles_freeagent_mcp_se_f4e73798/readme"
}