freeagent-mcp-remote
An MCP server that connects Claude to FreeAgent accounting data, allowing natural language queries for financial information like bank transactions, invoices, and reports. It also includes a command-line tool for direct API access.
README
freeagent-connector
This "connector" was built to give Claude access to my FreeAgent data. At this point it is an internal tool, however I tried to write clearly and for the general public in case it would be useful for others. If you need help to set up, adapt, or if you'd like a similar tool for your business, ask a question.
Inspired by samaxbytez/freeagent-mcp. Although initially I thought I'd be developing on top of it, I decided to start from scratch using FastMCP and Python.
- WIP — "work in progress". A term used by developers, used throughout this readme to mark functionality that is not yet available. Equivalent to "coming soon".
What's available
| Status | |
|---|---|
| A FreeAgent command-line tool — read your accounting data from a terminal | Works now |
| A Claude connector — ask Claude questions about your books | WIP |
They share the same setup, so following the steps below gets you the working half today.
How it works
This project is a small server that sits between FreeAgent and Claude (or another AI provider) and acts as a translator. Once it's connected, you can ask Claude things like "which bank transactions from March are still unexplained?" and Claude can go and look.
The technical name for this kind of translator is an MCP server — MCP being a shared standard for connecting AI assistants to outside tools. In Claude, these show up as connectors. You don't need to know anything more than that to use it.
Additional reading:
What is MCP? explains it in plain terms (think "a USB-C port for AI").
Get started with custom connectors.
Setup
Needed for both the command-line tool and (later) the connector. Written assuming you can follow a terminal, but haven't necessarily built a Python service before.
1. Get FreeAgent credentials
Before connecting to FreeAgent you need to register an "app". That gives you two strings — a client ID and a client secret — which together identify this server to FreeAgent. This way one "app" can be installed on different FreeAgent organisations, and for example if an "app" is found to be malicious FreeAgent can uninstall it from all the organisations at once. Unfortunately this registration is required even if you only want to connect to your own account.
-
Go to the FreeAgent Developer Dashboard and sign in.
-
Create an app.
-
Set the OAuth redirect URI to
http://localhost:8723/callback. This is where FreeAgent sends your browser back to after you approve access, so it has to match exactly — a trailing slash will break it.That address is the one the command-line tool uses, because the browser comes back to your own machine. The connector, once it's deployed, is reached at a public web address instead, and so needs its own redirect URI registered —
<the container's URL>/auth/callback. Nothing to do about that now; the deployment runbook covers it at the point where it matters. It's only worth knowing so that seeing two different addresses later doesn't look like one of them is a mistake. -
Duplicate
.env.exampleinto.envif you haven't already. That file is not checked into git, thanks to.gitignore. Copy the OAuth identifier and secret into it asFREEAGENT_CLIENT_IDandFREEAGENT_CLIENT_SECRET.
2. Install
The only thing you need installed first is uv, a tool that manages Python projects. It fetches the right version of Python for you, so you don't need Python installed already and don't need to know anything about virtual environments.
On a Mac, with Homebrew:
brew install uv
uv --version # check it worked
Other platforms, and other ways to install it, are covered in uv's installation guide.
Then:
git clone https://github.com/kivistudio/freeagent-connector.git
cd freeagent-connector
uv sync # creates .venv, installs everything, fetches Python 3.14
cp .env.example .env # then add the credentials from the step above
uv sync takes a minute the first time and is near-instant afterwards.
uv run <command> runs things inside that environment, which is why every command below
starts with it.
3. Authorise
uv run scripts/fa_auth.py
A browser opens, you approve access, and it writes a token back to .env. FreeAgent's
access tokens last an hour, but a refresh token is saved alongside and used automatically,
so this is genuinely a one-time step.
Sandbox. FreeAgent offers a free sandbox at signup.sandbox.freeagent.com — a throwaway company you can safely write to. It needs its own signup and its own app registration; sandbox credentials don't work against production. Point at it by setting
FREEAGENT_API_BASE_URL=https://api.sandbox.freeagent.com/v2, and the login endpoints follow automatically so the two can't get crossed. Worth doing before anything that writes; not worth it for reading, since a sandbox has none of your actual data.
Using the command-line tool
This works today. It reads any part of your FreeAgent account from the terminal, handling the login for you.
FreeAgent's data is organised into "endpoints" — /company, /invoices,
/bank_accounts and so on. The FreeAgent API docs list
them all. You ask for one like this:
uv run fastmcp call scripts/freeagent_api_caller.py request path=/company
Some things to try
All of these are read-only and safe.
# Your company profile: year end dates, VAT registration, company type
uv run fastmcp call scripts/freeagent_api_caller.py request path=/company
# Bank accounts, including how many transactions are still unexplained
uv run fastmcp call scripts/freeagent_api_caller.py request path=/bank_accounts
# Trial balance — every nominal account and its total
uv run fastmcp call scripts/freeagent_api_caller.py request \
path=/accounting/trial_balance/summary
# One contact, to see what fields a contact has
uv run fastmcp call scripts/freeagent_api_caller.py request \
--input-json '{"path": "/contacts", "params": {"per_page": "1"}}'
# Click around in a browser instead
uv run fastmcp dev inspector scripts/freeagent_api_caller.py
Simple arguments go as key=value. Nested ones — params and body — need
--input-json, which can carry the whole call.
Keeping the output manageable
A list endpoint can return thousands of records. Two ways to trim it, and they combine:
per_page=1limits how many records come back. Usually what you want — one real record shows you the actual formats values come in.shape_only=truefield names and types, no values. Useful for learning API shape during development.
uv run fastmcp call scripts/freeagent_api_caller.py request path=/invoices shape_only=true
Other options
| Argument | What it does |
|---|---|
path |
Which endpoint to call. The only required one. |
method |
GET by default. |
params |
Query options, e.g. {"view": "unexplained"}. Needs --input-json. |
show_headers |
Adds the true record count and paging links to the result. |
confirm_write |
Required before anything that changes data. |
Changing data (POST, PUT, DELETE) needs confirm_write=true. That's deliberate
friction — these are your real accounting records. Use the sandbox for those.
[WIP] The Claude connector
Not ready yet. When it is, you'll be able to add this to Claude as a connector and ask questions in plain language rather than calling endpoints yourself:
- Work through unexplained bank transactions and suggest how to categorise them
- Pull up the profit & loss, balance sheet or trial balance for a period
- Look at journal entries, or post corrections
- Prepare figures for VAT returns and corporation tax
- Review payroll and PAYE figures
- Think through the salary-versus-dividends split using your actual profit
- Track time, tasks and projects
The difference from the command-line tool is that the connector exposes each of these as a separate, narrow capability rather than one general "call anything" command — for reasons under Safety below.
For developers
Everyday commands
uv run pytest # run the tests
uv run pytest --lf # just the ones that failed last time
uv run ruff format . # auto-format the code
uv run ruff check . # find likely mistakes and style problems
uv run mypy # check the types line up
mypy is the one worth not skipping: it's set to strict, so it catches a whole class of
"this could be nothing here" bugs before they ever run.
Checks on commit
A git hook runs all four automatically every time you commit. Enable it once:
git config core.hooksPath .githooks
The whole suite takes about two seconds. If something fails, the commit stops and you get the output.
To commit anyway, use git's built-in bypass:
git commit --no-verify -m "..."
The hook also refuses outright to commit .env, which holds a live FreeAgent secret and
access token.
Working on the connector
# What tools does the server expose, and what do their inputs look like?
uv run fastmcp inspect src/server.py:create_server
# Click through it in a browser
uv run fastmcp dev inspector src/server.py:create_server
Note the :create_server at the end — these commands need the file and the name of the
function inside it that builds the server, not just the filename.
FreeAgent's documentation has gaps, contradictions and at least two copy-paste errors,
so the connector's tools are designed against real API responses rather than against the
docs. That's what the command-line tool above is for. scripts/freeagent_api_caller.py is local
only and must never be deployed; there's a test that fails if it ever reaches the deployed
server.
Learning
Caches
Three directories appear once you've run the tools. All are generated, gitignored, and never inputs to the program — deleting any of them costs nothing but a slower next run.
.mypy_cache/— what mypy learned about each file's types, so re-checking an unchanged file is a cache read rather than a fresh analysis. The one that matters most: without it, every run re-analyses all your dependencies' type information..pytest_cache/— which tests failed last time. This is what powerspytest --lf(last-failed) and--ff(failed-first), so you can iterate on just the broken tests..ruff_cache/— per-file lint results. Ruff is fast enough that you'd barely notice this one missing.
If anything ever behaves strangely, rm -rf .mypy_cache .pytest_cache .ruff_cache is a
safe reset.
If you get stuck
I built this for my own company's books, and wrote it up properly in case it's useful to someone else.
If you're trying to set up something like this and it isn't going well, I do this kind of work professionally and I'm happy to talk. <!-- TODO: name + how to get in touch -->
If you've found a bug or something here is wrong, an issue is welcome.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。
mcp-server-qdrant
这个仓库展示了如何为向量搜索引擎 Qdrant 创建一个 MCP (Managed Control Plane) 服务器的示例。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。