关于 Actual Budget
Actual Budget 是一个 MCP 服务,可通过 MCP 客户端查询和管理预算。它结合预算分析与交易管理,并提供删除预览、写入确认和只读模式等防护。
主要功能
- 01分析预测、趋势和预算摘要
- 02创建和编辑交易
- 03管理类别、收款方和规则
- 04删除前预览并要求确认
- 05启用写入工具只读模式
- 06修复不同步的预算
适用场景
- 01查看月度支出和类别趋势
- 02核对实际支出与预算
- 03添加或更新支出和转账
- 04同步状态不一致后恢复工具使用
MCP 相关
该服务将 MCP 客户端连接到 Actual Budget 服务器,并提供预算分析和管理工具。它可通过 stdio 本地运行,也可作为 Claude Desktop 扩展运行。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y actual-budget-mcpACTUAL_SERVER_URLURL of your Actual Budget server, for example http://localhost:5006ACTUAL_PASSWORD密钥Password for your Actual Budget server. Not needed if you set ACTUAL_SESSION_TOKENACTUAL_SESSION_TOKEN密钥Session token for servers behind OIDC, which have no password. Use instead of ACTUAL_PASSWORD; if both are set, the token winsACTUAL_BUDGET_IDYour budget's Sync ID, found under Settings > Show advanced settingsACTUAL_ENCRYPTION_PASSWORD密钥Only needed if your budget file is end-to-end encryptedACTUAL_READ_ONLYSet to 1 to hide every write tool from the model, exposing only reads and analysisACTUAL_TOOLSETSet to chat to offer the 17 everyday tools instead of all 43, which is 17.0 KiB of tool descriptions instead of 39.2 KiB on every connection. Defaults to fullACTUAL_DATA_DIRDirectory for the local budget cacheACTUAL_HTTP_TIMEOUT_MSHow long to wait for your server to start replying, in milliseconds. Defaults to 60000; raise it if a GoCardless bank sync is being cut off本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
41收录的工具列表 · 更新于 6 天前
此工具无需参数。
详细介绍
actual-budget-mcp
与你的预算对话。一个将 Actual Budget 连接到 Claude 的 MCP 服务。询问钱花到哪里去了,获得真正的分析结果,还能让它帮你记账,不必提心吊胆。
已列入 Actual Budget 官方社区项目。

功能特性
- 真正的分析,不只是查询 - 预测、类别趋势、预算与实际支出对比,以及月度摘要
- 值得信赖的写入操作 - 每次删除都会预览将要移除的内容,并等待你确认;
ACTUAL_READ_ONLY=1会对模型完全隐藏写入工具(参见安全性) - 经得起实际使用的多币种支持 - 支持拆分和余额调节,而不只是显示货币符号
- 从不同步的预算中恢复 - 当
@actual-app/api与你的服务端状态不一致时,repair_sync会重建本地同步状态;否则,这类故障会导致所有工具都报错 - 用日常语言询问预算情况 - “我这个月在食品上花了多少钱?”或“有哪些项目超出预算了吗?”
- 创建和管理交易 - 无需打开应用即可添加支出、转账和编辑交易
- 管理类别、收款方和规则 - 无需打开应用即可执行完整的增删改查
- 使用名称,而不是 ID - 说“Wallet”,而不是
a1b2c3d4-...;如果名称有歧义,还会提供有用的建议 - 支持英语和西班牙语自然日期 - “上个月”、“este mes”、“hace 3 meses”、“昨天”
- 整洁、格式清晰的输出 - 对齐的表格和清晰的摘要,而不是原始 JSON
- 清楚的错误消息 - 出现问题时,你能准确知道该修复什么
支持本地模型吗?
支持。这是一个 MCP 服务,因此兼容任何支持 MCP 的客户端;客户端使用什么模型由客户端决定,与此服务无关。下文介绍 Claude Desktop、Claude Code、Cursor 和 VS Code,是因为大家经常询问它们;但凡是能运行 MCP 客户端的环境,包括连接到 Ollama 或 LM Studio 的本地配置,都可以用同样的方式与它交互。
你的预算数据会发送到客户端使用的模型。如果你在意这一点(对很多使用 Actual 的人来说确实如此),本地模型可以让数据留在你的设备上。
支持 ChatGPT 吗?
不支持,原因不在于这个服务。ChatGPT 的连接器只接受远程 MCP 服务:使用 SSE 或 Streamable HTTP 的公共 HTTPS 端点。ChatGPT 无法连接到运行在你自己设备上的进程,而这正是此服务的运行方式。OpenAI 确实提供了用于本地服务的隧道,但仅限企业套餐使用。
要让它支持 ChatGPT,就必须将你的 Actual 服务暴露到互联网,这与大多数 Actual 用户的需求恰恰相反。任何可以启动本地 MCP 进程的客户端都可以使用它,例如 Claude Desktop、Claude Code、Cursor、VS Code、Gemini CLI,或连接到本地模型的自定义配置。
如果你真正想用的是 OpenAI 的模型,可以使用 Codex,它支持通过 stdio 在本地运行 MCP 服务。选项 6只需一条命令。
前提条件
- 运行中的 Actual Budget 服务(本地或远程)
- 以下所有选项都需要安装 Node.js 22.14 或更高版本,桌面扩展除外(参见 Node.js 要求)
- 桌面扩展只需要 Claude Desktop。它运行在 Claude Desktop 自带的 Node 上,安装包也包含了适用于其支持的每个平台的 SQLite 二进制文件,因此无需编译。已在 Windows 11 上验证:使用 Claude Desktop 2.110.0,且从机器上移除了 Node。
快速开始
在 Claude Desktop 中,最简单的方式是使用扩展:无需编辑配置文件,也无需运行命令。否则,将以下内容复制到 Claude Code 或 Claude Desktop:
Install the actual-budget-mcp MCP server from npm (https://github.com/henfrydls/actual-budget-mcp).
Configure it with these credentials:
- My Actual Budget server: http://localhost:5006
- Password: YOUR_PASSWORD
- Budget ID: YOUR_BUDGET_ID
Claude 会为你配置好一切。
安装
选项 1:Claude Desktop 扩展(无需配置文件)
现在提供打包好的桌面扩展:安装后,Claude Desktop 会在自己的设置界面中询问你的服务 URL、密码和 Sync ID,并将密码和会话令牌存储在操作系统的钥匙串中,而不是需要你编辑的配置文件里。
下载 actual-budget-mcp.mcpb,然后打开 Claude Desktop,前往 设置 > 扩展,将文件拖到该界面中。
在 Windows 上,只能通过拖放方式安装:双击文件会打开 Windows 的“选择用于打开此文件的应用”对话框,因为 Claude Desktop 没有注册 .mcpb 文件类型。已在全新安装的 Windows 11 和 Claude Desktop 0.14.10 上验证。
扩展包含了所需的一切,因此你提出的第一个问题就能立即得到回答,无需等待一个无法看到安装进度的安装过程。它只需下载一次,文件较大,但会显示进度条。
使用这种方式无需安装 Node.js。Claude Desktop 会使用它自带的 Node 运行扩展。我们在一台 Windows 11 机器上将 Node 改名使其不可用,然后照常提问进行了验证:服务成功启动并给出了回答。
早期版本会从 npm 启动该软件包。这虽然减小了下载量,却把下载推迟到了首次运行时;当时没有任何进度提示:Claude Desktop 会一直等待,随后判定服务已停止运行并提示无法连接,而扩展则会在几分钟后自行启动。现在,安装包包含了 Actual 为其支持的每个平台提供的 SQLite 二进制文件。这些是 N-API 二进制文件,因此每个平台只需一个即可适用于所有 Node 版本,启动时也无需选择。
更新扩展
在旧版本上安装新版本会保留你填写的设置,但有一种实际遇到过的例外:不同版本之间某个字段的标题发生变化时,已保存的服务密码会被清除。如果 Claude 在更新后无法连接,请先打开扩展设置并检查密码字段,再检查其他地方。
选项 2:Claude Code(一条命令)
claude mcp add actual-budget-mcp -e ACTUAL_SERVER_URL=http://localhost:5006 -e ACTUAL_PASSWORD=your-password -e ACTUAL_BUDGET_ID=your-budget-id -- npx -y actual-budget-mcp
选项 3:Claude Desktop(编辑配置文件)
将以下内容添加到你的 claude_desktop_config.json:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"actual-budget-mcp": {
"command": "npx",
"args": ["-y", "actual-budget-mcp"],
"env": {
"ACTUAL_SERVER_URL": "http://localhost:5006",
"ACTUAL_PASSWORD": "your-password",
"ACTUAL_BUDGET_ID": "your-budget-sync-id"
}
}
}
}
选项 4:Cursor
该按钮会使用占位值进行安装。之后打开 Cursor 设置 > MCP,并替换这三项:你的服务 URL、密码和预算的 Sync ID。若要手动完成所有操作,请前往 Cursor 设置 > MCP > 添加新的 MCP 服务,并添加:
{
"mcpServers": {
"actual-budget-mcp": {
"command": "npx",
"args": ["-y", "actual-budget-mcp"],
"env": {
"ACTUAL_SERVER_URL": "http://localhost:5006",
"ACTUAL_PASSWORD": "your-password",
"ACTUAL_BUDGET_ID": "your-budget-sync-id"
}
}
}
}
选项 5:VS Code(GitHub Copilot)
与上面相同:按钮会填入占位值,之后请替换这三个值。手动操作时,将以下内容添加到 VS Code 的 settings.json 中:
{
"mcp": {
"servers": {
"actual-budget-mcp": {
"command": "npx",
"args": ["-y", "actual-budget-mcp"],
"env": {
"ACTUAL_SERVER_URL": "http://localhost:5006",
"ACTUAL_PASSWORD": "your-password",
"ACTUAL_BUDGET_ID": "your-budget-sync-id"
}
}
}
}
}
选项 6:Codex(OpenAI)
只需运行一条命令,它就会为你将条目写入 ~/.codex/config.toml:
codex mcp add actual-budget-mcp \
--env ACTUAL_SERVER_URL=http://localhost:5006 \
--env ACTUAL_PASSWORD=your-password \
--env ACTUAL_BUDGET_ID=your-budget-sync-id \
-- npx -y actual-budget-mcp
Codex 没有扩展或捆绑包格式,因此这条命令是最简便的方式。之后可通过 codex mcp list 查看,并通过 codex mcp remove actual-budget-mcp 撤销。
这里说的是本地 Agent Codex、CLI 和 IDE 扩展。浏览器中的 Codex 在 OpenAI 的机器上运行,无法访问你网络中的 Actual 服务。
选项 7:Docker
与其他选项一样,该镜像使用 stdio 通信,因此由客户端启动容器并管理其生命周期:
{
"mcpServers": {
"actual-budget-mcp": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"--add-host=host.docker.internal:host-gateway",
"-v", "actual-budget-mcp-data:/data",
"-e", "ACTUAL_SERVER_URL",
"-e", "ACTUAL_PASSWORD",
"-e", "ACTUAL_BUDGET_ID",
"ghcr.io/henfrydls/actual-budget-mcp:latest"
],
"env": {
"ACTUAL_SERVER_URL": "http://host.docker.internal:5006",
"ACTUAL_PASSWORD": "your-password",
"ACTUAL_BUDGET_ID": "your-budget-sync-id"
}
}
}
}
有两点很容易踩坑:
- 在容器内,
localhost指的是容器本身。 你的 Actual 服务不在那里。host.docker.internal(使用上面的--add-host标志可使其在 Linux 上解析)会连接到宿主机。 - 挂载
/data。 这里存放预算缓存。如果没有卷,每次启动都会从服务重新下载整个预算。
选项 8:从源代码运行(适用于贡献者)
git clone https://github.com/henfrydls/actual-budget-mcp.git
cd actual-budget-mcp
npm install
cp .env.example .env # Edit with your credentials
npm run build
npm run test:connection # Verify it works
验证设置
--verify 会读取运行该命令的 shell 环境,而上述安装选项会将凭据写入 MCP 客户端的配置。因此,请为该命令设置这些变量:
ACTUAL_SERVER_URL=http://localhost:5006 \
ACTUAL_PASSWORD=your-password \
ACTUAL_BUDGET_ID=your-sync-id \
npx -y actual-budget-mcp --verify
该命令会连接服务、下载预算,并打印找到的账户和类别组数量。如果在未设置这些变量的情况下运行,它会报告变量缺失;这是针对该命令的提示,并不表示你的安装有问题。
更改客户端配置后,请重启客户端。 Claude Desktop、Claude Code 等会在启动时读取 MCP 配置,只有重启后才会加载修改。
连续运行数天
大多数客户端会在打开时启动此服务,并在关闭时停止它,这种情况下,下面的内容都不重要。如果你将它作为持续运行的进程、始终在线的聊天机器人或服务来运行,这些内容就很重要。
服务会在启动时下载你的预算,并保留一份本地副本。每次读取之前,无论你是通过工具发起请求,还是读取 actual://accounts,它都会拉取 Actual 服务中本地副本尚未包含的内容,因此你在 Actual 应用中所做的编辑会显示在你接下来提出的问题中。你不必重启它,也不必运行银行同步来触发更新。
以下三个限制可以避免每次调用都产生一次往返:
- 最近 60 秒内拉取的副本会被视为最新,因此一连串问题只会同步一次。任何同步都计入此限,包括写入操作在决定是否写入前进行的同步。
- 读取最多等待 20 秒来完成拉取。超过此时间后,它会使用本地副本作答,而不是一直等待,并会说明这一点。
- 如果拉取失败或超过此时限,接下来 60 秒内的读取会直接使用本地副本作答,而不再尝试拉取。否则,服务宕机时,每次读取都会依次等待完整时限。
如果 Actual 服务宕机、无法连接或只是响应缓慢,导致未能拉取,回复末尾会显示一行文字,说明无法刷新、数据有多旧,以及出了什么问题:
Could not refresh from the Actual server; these figures are from the last
sync, 14 minutes ago. Anything changed in the Actual app since then may be
missing. Reason: The budget's sync state is out of sync with the Actual
server, so no operation can run until it is repaired. Run the `repair_sync`
tool to rebuild the sync state (non-destructive), or repair it in the Actual
app under Settings > Show advanced settings.
这行文字正是关键所在。否则,服务宕机后仍会使用数小时前的数据作答,却不会说明数据是哪一份。之所以提供原因,是因为不同情况需要采取不同处理方式:网络短暂故障会自行恢复,预算不同步则需要修复,而登录被拒则需要检查你的凭据。
写入不受影响:写入操作本来就会在决定是否写入的检查之前进行拉取,因此不必为第二次拉取付出代价。
每个客户端都需要使用各自的 ACTUAL_DATA_DIR。两个进程共用一份本地副本会导致数据损坏;而长时间运行的服务更容易意外发生这种情况,因为它全天都在运行,第二个客户端可以轻易指向同一份副本。
配置
| 变量 | 必填 | 描述 |
|---|---|---|
ACTUAL_SERVER_URL | 是 | 你的 Actual Budget 服务 URL。参见使用哪个 URL 和端口 |
ACTUAL_PASSWORD | 是* | 服务密码(在 Actual Budget 的 Settings 中设置)。*如果使用 ACTUAL_SESSION_TOKEN,则无需设置 |
ACTUAL_SESSION_TOKEN | 否 | 适用于没有密码的 OIDC 后端服务。使用此项代替 ACTUAL_PASSWORD;如果两者都设置,则优先使用令牌 |
ACTUAL_BUDGET_ID | 是 | 预算同步 ID(在 Settings > Show advanced settings 中找到) |
ACTUAL_ENCRYPTION_PASSWORD | 否 | 仅在预算文件已加密时需要 |
ACTUAL_DATA_DIR | 否 | 预算缓存所在位置。默认为操作系统的数据目录(见下文) |
ACTUAL_READ_ONLY | 否 | 设置为 1/true/yes 以只读模式运行。参见安全性 |
ACTUAL_TOOLSET | 否 | chat 提供 18 个日常工具,而不是全部 44 个。默认为 full。参见选择工具集 |
使用会话令牌(OIDC 服务端)
如果你的 Actual 服务端通过 OIDC 为你登录,就没有可填入
ACTUAL_PASSWORD 的密码,因为服务端会改为签发会话令牌。将
ACTUAL_SESSION_TOKEN 设为该令牌,并将密码留空。
要找到令牌,请在已登录 Actual 的浏览器中:
- 打开浏览器的开发者工具
- 前往 Application(Chrome/Edge)或 Storage(Firefox)
- 展开 IndexedDB →
actual数据库 →asyncStorage存储区 - 复制
user-token键的值
它存储在 IndexedDB 中,而不是 Local Storage,因此很多人去那里查找时找不到。
请像对待密码一样保护该令牌:它授予相同的访问权限。令牌也会过期;如果过期,服务端会明确告知,并提示你签发新令牌,而不会把问题归咎于你并没有的密码。
使用哪个 URL 和端口
这取决于你运行 Actual 的方式;选错后会出现无法解释原因的连接错误:
| 运行 Actual 的方式 | URL |
|---|---|
| 自托管同步服务(Docker、VPS 等) | http://localhost:5006,或你托管服务的地址 |
| 桌面应用 | http://localhost:5007 |
桌面应用会在端口 5007 上运行自己的同步服务,并且仅在应用打开时运行。关闭应用后,就没有服务在监听,因此无法连接。
这个内嵌服务也只绑定到 127.0.0.1。它只能从同一台机器访问,其他地方都无法访问。因此,如果 Claude 运行的位置与安装应用的机器不同,例如在另一台电脑或虚拟机上运行,你需要使用 SSH 隧道或端口转发。使用主机的局域网地址无法连接。
缓存存放位置
除非设置了 ACTUAL_DATA_DIR,否则预算缓存会存放在系统的标准数据目录中:
| OS | 默认位置 |
|---|---|
| Linux | $XDG_DATA_HOME/actual-budget-mcp,或 ~/.local/share/actual-budget-mcp |
| macOS | ~/Library/Application Support/actual-budget-mcp |
| Windows | %APPDATA%\actual-budget-mcp |
这是缓存,不是你的数据:删除它只会在下次运行时触发重新下载。缓存会有意存放在临时目录之外,因此重启不会清除它,也就不会导致下次启动时重新下载整个预算。
查找你的预算 ID
- 打开 Actual Budget
- 打开 Settings:点击预算名称旁边的箭头,或使用侧边栏中的 More,再选择 Settings
- 点击 Show advanced settings
- 复制 Sync ID
请使用 Sync ID,而不是 Budget ID。 Actual 会同时显示这两个 ID,一个在另一个下方,且两者都是 UUID。尽管变量名称如此,ACTUAL_BUDGET_ID 要求填写的是标记为 Sync ID 的那个。使用另一个 ID 会显示 Budget "..." not found on the server,看起来像是输入错误,但实际上只是选错了字段。
如果 Sync ID 显示为 (none),说明该预算从未同步到服务端。此服务通过 Actual 的同步服务与 Actual 通信,因此本地专用预算在同步之前无法使用。
隐私政策
数据收集。 此服务不会收集任何数据。它没有遥测、分析或使用情况报告,也没有这方面的计划:它会读取个人财务数据,而这样的工具不应该向外发送数据。无需创建账户,也无需选择退出。
使用和存储。 此服务只会连接到一个地方:你配置 URL 的 Actual Budget 服务。你的预算会缓存在本机上,存储于缓存所在位置中说明的数据目录,这样就不必在每次启动时都重新下载。数据不会写入其他任何位置。
你的凭据由 MCP 客户端处理,而不是由此服务处理。Claude Desktop 会将密码和会话令牌存储在操作系统的钥匙串中;服务会在启动时将它们作为环境变量接收,用于连接,并且绝不会将它们写入磁盘。
与第三方共享。 不会。数据不会发送给作者、任何分析服务或任何第三方。此服务建立的唯一网络连接是连接到你自己的 Actual 服务。
有两点值得说明,因为它们同样属实:你正在与之交谈的模型(Claude 或你使用的其他客户端)必然会看到你询问的预算数据,具体受该提供商自身条款约束;通过 npx 安装会从 npm 下载软件包,这只是普通的软件包下载,不涉及任何预算数据。
数据保留。 缓存会一直保留在本机,直到你将其删除。删除缓存不会造成任何损失,因为它只是 Actual 服务中数据的副本;下次运行时会重新下载。卸载此服务后,只会留下该目录,你可以将其删除。
联系方式。 请在 https://github.com/henfrydls/actual-budget-mcp/issues 提交问题。完整政策也发布于 https://actual-mcp.henfrydls.com/privacy/。
此服务写入的交易都带有它生成的 id
通过此服务创建的每笔交易、拆分和转账,在发送前都会分配一个 UUID。如果写入时报告错误,服务会使用该 id 再次查找相应行。它是交易自身的 id,而不是
imported_id,因此 Actual 对导入文件的去重功能仍会像对其他行一样正常应用于这些行。
这在 Actual 中不可见,对你也没有任何影响。之所以记录此行为,是因为它与手动写入同一笔交易确实有所不同。
安全性
三项保护措施可防止 Agent 因指令含糊而影响你的预算。
删除前先预览
所有删除工具在首次调用时都会拒绝删除任何内容。它会报告将要删除的内容,然后停止。删除操作需要再进行一次明确调用:
delete_category(category: "Groceries")
→ preview: transactions affected, budget and rollover warning. Nothing deleted.
delete_category(category: "Groceries", confirm: true, confirm_name: "Groceries")
→ deleted
预览会涵盖所有可能被删除的行,这正是它的意义所在:日期晚于今天、比预算中其他内容更早、属于拆分中的一部分,或位于已关闭账户中。过去这四种情况的预览都会显示为空白,却仍然执行删除,因此保护措施要求你确认的其实什么也没有。现在,如果某个 id 与任何交易都不匹配,工具会拒绝操作,而不是报告已删除。
通过名称查找目标的工具(delete_account、delete_category、
delete_category_group、delete_payee)还要求提供与名称完全一致的 confirm_name。误删实际就发生在这种情况下:请求删除 “Adicionales”,可能会匹配到 “Ingresos Adicionales”。接受精确 id 的工具(delete_transaction、delete_rule)只需要 confirm: true。
已存在的交易不会被创建两次
create_transaction 会先检查,再写入。如果账户、日期和金额都与预算中已有的某笔交易相符,它就不会创建任何内容,而是向你显示已有的交易:
create_transaction(account: "Checking", amount: -50, date: "2026-06-05")
→ A transaction like this one already exists, so nothing was created:
2026-06-05 -50.00 Checking Claro
id: 0b6d516e-...
Same account, same date, same amount. If this is a second, genuine payment
rather than the same one recorded twice, call again with allow_duplicate: true.
create_transaction(account: "Checking", amount: -50, date: "2026-06-05", allow_duplicate: true)
→ Transaction created
同一张卡在同一天买两杯相同的咖啡确实可能发生,因此提供了这个标记,代价只是多调用一次。这与 0.9.x 不同:当时第二次调用会创建第二行,且不会提示。
检查前会先同步,因此它能看到其他客户端写入的内容,而不只是当前客户端所做的更改。这正是该功能的用途:两个 Agent 操作同一个预算,却都看不到对方。出于同样的原因,reconcile_currency_residual 也接受相同的标记,并会在读取用于计算余额的数据前先同步。
它不会作为错误返回。 删除工具会在预览结果中设置 isError,以防重复调用意外删除内容。这个工具刻意采用相反的做法:重复调用不会创建任何内容;而读取 isError 的 Agent 会把被询问当成遭到拒绝并重试,从而造成重复。删除操作会设置标记;这个工具不会。
代价。 每次调用 create_transaction 都会多一次往返,无论是否发现重复记录。连接同一台机器上的服务时,这点开销无法测量;连接远程服务时,每次写入耗时大致会翻倍:往返延迟为 80 ms 时,单次创建从 87 ms 增至 171 ms,连续创建 22 笔交易则从 1.9 s 增至 3.8 s。传入 allow_duplicate: true 会跳过同步和检查,因此已在其他地方去重的批量导入不会产生额外开销。
离线和无响应的服务。 如果同步失败,检查仍会针对本地副本运行,不会阻止写入,因此离线会话仍可继续工作,只是检查力度较弱,而不是完全无法写入。它会在 stderr 中说明这一点及其原因,因此检查力度降低时不会悄无声息。
过去,接受连接后却始终不响应的服务是最慢的情况:Actual 库本身没有设置超时,因此调用会一直等到 Node 自带的五分钟标头超时才返回,而且写入前后都可能发生这种情况。现在,ACTUAL_HTTP_TIMEOUT_MS(见下文)会默认在 60 秒后结束等待(#99)。
它无法检测以下情况:
- 规则在存储行时重写了金额或日期,因此存储的行与请求内容不再匹配。重命名规则是最常见的一类,对此没有影响。
- 交易在检查与写入之间到达。同步会缩短这段时间,但无法将其消除。它会先检查再写入,但这不等于两者同时进行。
create_transfer和create_split_transaction尚未运行此检查,create_account创建的期初余额也不会运行。相关问题已记录在 #98。
reconcile_currency_residual 与未标记为已结清的行
它会将账户中截至今天的所有交易都纳入比较,这一直以来都是如此。银行对账单通常只显示已入账的交易,因此,如果账户中有尚未被任何人勾选的行,这两个数字衡量的可能并不是同一件事。
它不会替你判断,而是会报告这一情况:
Currency residual reconciled:
Account: Card (USD)
Was: -120.00
includes 1 row not marked cleared, -20.00
cleared rows alone come to -100.00, and against that
figure the adjustment would have been 100.00.
Check which of the two the balance you gave is measuring.
Target: 0.00
Adjustment: 120.00
所有行都标记为已结清时,不会显示任何内容。
我们曾考虑改为与已结清的行比较,并先用真实预算进行了验证。大多数未标记为已结清的行已经在那里放了好几周,而且没有一笔来自银行:它们只是没人勾选的行,并非正在处理中的项目;后者通常一两天内就会结清。若按已结清金额进行对账,所有这些行都会被记作残差类别中的调整额,这正是此处所要避免的情况,只不过从另一面发生。因此,数字保持不变,同时会向你显示两个数值。
reconcile_account 不会写入任何内容,它会读取同样的两个数值,并使用 balance_counts 在两者之间进行选择。
reconcile_currency_residual 和日期
它拒绝为所写入的调整指定未来日期。它的全部作用是让账户余额与银行当前报告的余额一致,而一条稍后才生效的记录做不到这一点。它也无法正常工作:余额只统计截至今天的交易,因此未来日期的调整不会计入余额,每次运行都会再记一笔。
这里的“今天”是服务器的今天。时区比服务器靠前的客户端可能会被告知自己的日期是未来日期;省略 date 或传入 "today",使用的时钟与检查时相同,因此始终有效。create_transaction 没有这项限制,因此仍可用它记录日期在未来的消费记录;例如,信用卡周末消费在下一个工作日入账时,这正是你需要的功能。
当账户中有日期晚于今天的交易时,它会询问这些交易应如何处理。 Actual 的余额只统计到今天;银行的数字可能不是如此。周末消费通常会记为下一个工作日的日期,因此银行已经将其计入,而余额尚未计入,否则这项差额就会被记为汇率漂移。
因此,reconcile 会报告这些记录,并在你说明采用哪种解读之前不记入任何内容:
reconcile_currency_residual(account: "Card", target_balance: -140, category: "Cashback")
→ No adjustment was booked for Card.
This account holds 2 transactions dated after today, so the balance Actual
reports and the balance your bank reports are not measuring the same thing.
2026-09-28 -40.00 WEEKEND-PURCHASE (came from the bank, so the bank counts it)
2026-10-26 -80.00 SCHEDULED-LATER
Balance to today: -100.00
Those rows come to: -120.00
Balance counting them: -220.00
You said the bank says: -140.00
Which is it?
future_rows: "exclude" the bank has not posted them yet.
Adjustment would be -40.00.
future_rows: "include" the bank has posted them already, ...
Adjustment would be 80.00.
由你来选择,因为通常没有任何依据能确定一条记录属于哪种情况。有依据时会明确说明:从银行导入的记录显然已被银行计入;而在此处录入但尚未对账的记录,银行可能还未看到。两者都不能定论,但都能缩小范围。记录按从早到晚排列,因此最接近今天、最可能已入账的那条会排在最前面。
没有未来日期记录的账户不受影响,也不会看到此问题。日期恰好为今天的记录算作当前记录,而非未来记录,因为余额已经将其计入。
无论你选择哪种方式,都会记录在调整本身上,例如 FX residual adjustment (counting 2 transactions dated after today),这样以后就能找到依据错误解读记入的记录,而不必费力猜测。
在此功能出现前测得的情况:账户截至今天的余额为 -100.00,有一笔日期在未来、银行已入账的 -40.00 消费记录,一笔日期在未来、银行尚未入账的 -80.00 转账,以及银行报告的 -140.00。它记入了 -40.00,导致账户合计变为 -260.00,而银行余额是 -220.00。调整金额恰好等于那笔消费,并被重复记录了一次,还归入了一个将其称为汇率漂移的类别。
它还会拒绝不存在的日期,例如 2026-09-31 或 2026-02-30,而不是将其视为未来日期。其他工具仍会接受不可能的日期并原样存储;这是早已存在的行为,本次并未更改。
它在正常流程中还会同步三次:读取余额前一次、委托调用的 create 内一次,以及推送时一次。其中两次拉取连续发生,因此远程服务会多进行一次不必要的往返。
只读模式
设置 ACTUAL_READ_ONLY=1 后,服务只会暴露 17 个读取、分析和修复工具。写入工具根本不会注册,因此不会出现在工具发现结果中,Agent 也无法被说服去调用它看不到的内容。
repair_sync 会按设计保持可用:它修复的是同步状态,而非预算数据;隐藏它会让同步状态失常的预算无从恢复。
默认启用写入功能。只读模式需主动启用。
选择工具集
每次连接都会在收到任何请求之前,先发送完整的工具列表。使用
scripts/measure-toolset.mjs 进行测量:该脚本会注册实际工具,并让
真实客户端请求 tools/list:
ACTUAL_TOOLSET | 工具数 | tools/list |
|---|---|---|
full(默认) | 44 | 39.7 KiB |
chat | 18 | 17.4 KiB |
chat 配合 ACTUAL_READ_ONLY=1 | 8 | 5.7 KiB |
full 配合 ACTUAL_READ_ONLY=1 | 17 | 12.4 KiB |
chat 是日常使用的工具集:读取预算、记录交易并为交易分类,以及在类别间转移已编入预算的资金。它不包含预算的设置和修复操作,也就是创建和删除账户、类别、分组、收款人和规则,以及 reconcile_currency_residual 和 repair_sync。
设置工具集有两个值得考虑的原因,第二个更为重要:无论对话内容是什么,都会消耗上下文;而模型从 44 条工具描述中做选择,比从 18 条中做选择更容易选错工具。
提示词与其调用的工具相对应。每条提示词都是一个简短脚本,其中会指定三到四个工具,因此,如果提示词所需的工具未提供,该提示词也不会提供。使用 chat 时,所有提示词都不会提供;而不包含提示词的服务根本不会公布提示词:prompts/list 会回答该方法不受支持,而不是返回空列表。
这两个设置会共同缩小范围,而不是彼此冲突:只读模式决定哪些工具存在,ACTUAL_TOOLSET 决定其中哪些工具会被提供。
工具(44)
读取(11)
| 工具 | 描述 | 示例提示词 |
|---|---|---|
list_accounts | 所有账户及其余额 | “显示我的所有账户” |
get_budget_month | 指定月份的预算 | “我的 3 月预算是什么样的?” |
get_transactions | 可按筛选条件查询交易 | “显示上周金额超过 5000 的交易” |
get_category_balance | 跨一段月份区间的类别历史记录 | “6 月前的三个月里,餐饮支出情况如何?” |
get_budget_summary | 预算概览 | “给我一份 2 月的预算摘要” |
get_categories | 所有类别组和类别;使用 include_hidden 可包含隐藏类别 | “我有哪些类别?” |
get_payees | 预算中的所有收款方 | “列出我的所有收款方” |
get_schedules | 定期付款:接下来有哪些款项到期、金额是多少,以及上次付款是否已入账 | “接下来有哪些账单要付?” |
reconcile_account | 将账户与银行金额进行比较,并说明差额 | “我的支票账户对账单显示 12,500.00,是哪里出了差错?” |
get_rules | 所有交易规则 | “显示我的规则” |
balance_history | 账户余额随时间的变化 | “显示我的支票账户余额历史记录” |
参数
get_budget_month - month(可选):YYYY-MM 或自然语言(“本月”“上个月”“enero 2025”)
reconcile_account - account(必填)| expected_balance(必填):银行显示的金额 | as_of(可选):该金额对应的日期,默认为今天;不计入该日期之后的交易 | balance_counts(可选):all(默认)也计入未结清的行,cleared_only 则不计入 | lookback_days(可选,默认 90)。仅读取,不会记账。会列出可能解释差额的原因,并按信号强弱排序:重复录入的费用、记在其他账户中的金额、日期晚于截止日期的行,最后是单纯的金额匹配。不会有意搜索组合情况,因为对于普通账户来说,任意两笔金额之和都可能接近某个整数金额。如果没有任何原因能解释差额,就会如实说明。
get_transactions - account(可选):账户名称 | start_date / end_date(可选):YYYY-MM-DD 或自然语言 | category(可选):类别名称(匹配其中任意部分)或完整类别 ID | payee(可选):收款方名称 | min_amount / max_amount(可选):按金额筛选 | notes_contains(可选):在备注中查找的文本,不区分大小写;也会匹配交易所属拆分项的备注;除非指定日期范围,否则会搜索所有日期 | uncategorized(可选):仅查找没有类别的交易,不包括拆分父项、预算内账户之间的转账以及预算外账户;除非指定日期范围,否则会搜索所有日期 | limit(可选,默认 50)
get_category_balance - category(必填):类别名称或 ID | months(可选,默认 3):时间范围涵盖的月份数;默认值只是默认值,不是上限 | month(可选):时间范围的结束月份,默认为本月,因此可以直接查询过去的时间段
get_budget_summary - month(可选):YYYY-MM 或自然语言。未设置预算的组不会显示百分比:非正预算的占比没有合理的解读方式,因此该行会直接说明情况。
balance_history - account(必填):账户名称或 ID | start_date(可选,默认为 3 个月前)| end_date(可选,默认为今天)
| 工具 | 描述 | 示例提示词 |
|---|---|---|
budget_vs_actual | 按类别查看预算金额与支出金额 | "这个月有哪项支出超出预算了吗?" |
spending_projection | 月末支出预测 | "这个月的支出会在预算范围内吗?" |
category_trends | 一段月份区间内的支出趋势 | "截至 6 月的六个月里,我的支出趋势如何?" |
spending_by_category | 按类别查看支出明细 | "显示 2 月按类别划分的支出" |
monthly_summary | 收入、支出与储蓄对比 | "过去 3 个月我的财务状况如何?" |
参数
budget_vs_actual - 某类别当月净额为正,表示有资金流入,而非预算结余,因此不计入预算结余总额。month(可选):YYYY-MM 或自然语言 | group(可选):按类别组筛选
spending_projection - 流入的资金不会计作支出;标题统计已超出预算的类别,包括完全没有预算的类别。month(可选):YYYY-MM 或自然语言
category_trends - category(可选):指定类别;若省略,则显示支出最高的类别 | months(可选,默认值为 6):时间区间涵盖的月数;默认值只是默认值,并非上限 | month(可选):时间区间的结束月份,默认为本月。早于预算文件所涵盖范围的月份会在回复中列出,不会导致调用中止
spending_by_category - start_date / end_date(可选):日期范围 | include_income(可选,默认值为 false) | limit(可选,默认值为 20)。拆分交易的每一部分都会计入各自的类别;预算外账户不纳入统计,并采用与月度交叉核对相同的合计金额。占比列表示占支出的比例,因此某类别在该期间的净额为正(例如退款或报销)时,仍会列出,但不计入占比;页脚会分别列出支出、流入资金和净额。否则,单笔流入会缩小分母,导致各项占比之和超过 100%。
monthly_summary - months(可选,默认值为 3):要显示的月份数
写入:交易(11)
| 工具 | 描述 | 示例提示词 |
|---|---|---|
create_transaction | 添加一笔新交易 | "今天用 Wallet 支付 500 买杂货" |
create_transactions | 一次添加多笔交易,要么全部成功,要么全部失败 | "记录从 18 日开始的这 22 笔收支" |
create_split_transaction | 将一笔费用拆分到多个类别 | "拆分这笔 3,000 的费用:2,000 买杂货,1,000 用于家庭用品" |
update_transaction | 编辑现有交易 | "将那笔交易的金额改为 600" |
delete_transaction | 删除一笔交易(会先预览,参见 安全) | "删除那笔测试交易" |
update_budget_amount | 设置预算金额或增加预算 | "这个月给 Health 增加 10,000" |
transfer_between_categories | 在类别之间转移预算资金,不创建交易 | "将 125.50 从 Pending Refunds 转到 Family" |
hold_for_next_month | 留存本月部分未分配资金,供下月使用 | "把剩余资金中的 5,000 留到下个月" |
set_category_carryover | 将类别的超支结转,而非从预算总额中扣除 | "从现在起,让 Groceries 自行结转超支" |
recategorize_transaction | 将交易移至其他类别 | "将那笔交易移到 Entertainment" |
create_transfer | 在账户之间转账 | "从 Checking 转 10,000 到 Savings" |
reconcile_currency_residual | 清除累计的汇率残差 | "将我的 USD 卡对账为 250.00 USD" |
run_bank_sync | 与已关联的银行同步 | "同步我的银行交易" |
参数
**create_transactions** — `transactions`(必填):`{account, amount, payee?, category?, date?, notes?, cleared?, imported_id?}` 数组 | `allow_duplicate`(可选)。**要记录多笔交易,请使用此工具。** 每一行都会先解析并检查,之后才会写入;如果有任何一行不可用,就不会创建任何交易:回复会指出失败的行及原因,并说明其他行都没问题,但也不会写入。此工具取代了并行多次调用 `create_transaction` 的做法:曾经一次并行调用九次,导致服务崩溃,我们由此得知该限制。若某行的收款方名称与您的某个账户相同,则会按照 `create_transaction` 使用的规则将其视为转账;回复会在末尾列出这些行,并说明每笔对预算的影响。若两行分别代表同一笔资金流动的两端(例如从两份对账单读取信用卡还款时会出现这种情况),则会拒绝将其作为同一笔资金流动重复写入;如果转账的另一端已从银行导入目标账户,也会拒绝。即使信用卡对账单上显示的是类似 “PAYMENT RECEIVED” 的字样,而不是账户名称,也适用此规则;两家银行相隔**一天**入账时也适用,这在不同银行之间很常见。相隔两天则视为两笔资金流动。同一账户在不同日期收到的两笔金额相同的付款不受影响:此处仍按确切日期进行比对。若某行的 `imported_id` 已存在于预算中,则会拒绝该行,因此重新发送批次不会造成重复。create_transaction — account(必填):账户名称 | amount(必填):支出为负数,收入为正数 | payee(可选):若名称与某个账户相同,则视为转账,并在该账户中创建对应行 | category(可选):指定类别表示这是普通消费而非转账,但仅当两个账户都在预算内时才适用;例如商店名称恰好与账户名称相同的情况。如果任一账户不在预算内,则仍视为转账,因为资金正在跨出预算范围 | date(可选) | notes(可选) | cleared(可选) | allow_duplicate(可选):即使已存在账户、日期和金额相同的交易,也照样创建
update_transaction — transaction_id(必填) | amount、payee、category、date、notes、cleared(均为可选)
delete_transaction — transaction_id(必填) | confirm(可选):必须为 true 才会删除;不提供时,此工具只会预览
update_budget_amount — category(必填) | amount(必填) | month(可选) | mode(可选):absolute(默认值)设置预算金额;delta 将金额加到现有金额上,也可以为负数。普通调整应使用 delta:有结转和支出的情况下,设置绝对金额需要先算出类似 24,545.50 这样的数值,而单看这个数,无法判断计算是否有误。
transfer_between_categories — from(必填):转出金额的类别 | to(必填):转入金额的类别 | amount(必填):正数 | month(可选,默认为当前月份)。若任一端是收入类别、月份格式不是 YYYY-MM 或月份不在 01 到 12 之间,或试图将金额转到类别自身,则会拒绝操作。(Actual 按类别标记收入,因此收入类别可能位于支出组中。)Actual 自带的处理程序接受这三种情况,并会悄无声息地丢失、销毁或凭空创造资金。允许为超支类别补足金额,回复会说明这一点。
hold_for_next_month — amount(必填):加到现有留存金额上;0 表示释放全部留存金额 | month(可选,默认为当前月份)。Actual 自带的调用会累加而非设置金额,并且会留存当月所有金额却不作说明,因此回复会给出留存总额;如果实际留存金额少于请求金额或完全没有留存,也会说明。舍入后为零的金额会被拒绝,不会视为释放留存金额。仅适用于信封预算:跟踪型预算没有可分配的资金,也没有可留存的金额。
set_category_carryover - category(必需)| enabled(必需)| month(可选,默认为当前月份)。不只设置一个月:Actual 会将此标记写入指定月份至已创建的最后一个月,因此回复会说明哪些月份发生了更改、设置延续到哪个月份,以及之后新建的月份不会带有此标记。最后一个月份比 Actual 的 API 可列出或读取的范围多一个月,因此虽然已写入,但回复会说明无法检查。拒绝用于收入类别(没有可结转的超支),以及追踪型预算(没有可结转至其中的信封)。
recategorize_transaction - transaction_id(必需)| category(必需)
create_transfer - from_account(必需)| to_account(必需)| amount(必需)| date(可选)| notes(可选)
create_split_transaction - account(必需)| amount(必需):总金额,必须等于各拆分金额之和 | splits(必需):两个或更多 {category, amount, notes} | payee、date、notes、cleared(均可选)
reconcile_currency_residual - 与截至今天的每一行进行比较,并说明该数额中有多少尚未标记为已结清,以及不计入这些金额时调整额会是多少。account(必需)| category(必需):记录调整的类别 | target_balance(可选,默认为 0)| payee、notes(可选)| date(可选,今天或更早;拒绝未来日期)| allow_duplicate(可选):即使当天已有同金额的交易,也进行记录 | future_rows(可选):exclude 或 include,表示你提供的余额是否已包含日期晚于今天的交易
run_bank_sync - account(可选):同步指定账户;省略则同步所有账户
写入:类别(6)
| 工具 | 描述 | 示例提示词 |
|---|---|---|
create_category | 创建新类别 | "在 Variable Spending 中创建一个名为 Gym 的类别" |
update_category | 重命名或隐藏类别 | "将 Gym 重命名为 Fitness" |
delete_category | 删除类别(先预览,参见安全性) | "删除 Fitness 类别" |
create_category_group | 创建新分组 | "创建一个名为 Health 的类别分组" |
update_category_group | 重命名或隐藏分组 | "将 Health 分组重命名为 Wellness" |
delete_category_group | 删除分组(先预览,参见安全性) | "删除 Wellness 分组" |
参数
create_category - name(必需)| group(必需):分组名称或 ID
update_category - category(必需):名称或 ID | name(可选):新名称 | hidden(可选):true/false
delete_category - category(必需)| transfer_to(可选):将交易转移至的类别 | confirm + confirm_name(删除时必需)
create_category_group - name(必需)
update_category_group - group(必需):名称或 ID | name(可选):新名称 | hidden(可选):true/false
delete_category_group - group(必需)| transfer_to:孤立交易的目标类别;除非分组为空,否则必需 | confirm + confirm_name(删除时必需)
| 工具 | 描述 | 示例提示词 |
|---|---|---|
create_payee | 创建新的收款方 | “创建一个名为 Netflix 的收款方” |
update_payee | 重命名收款方 | “将 Netflix 重命名为 Netflix Premium” |
delete_payee | 删除收款方(会先预览,参见安全性) | “删除 Netflix Premium 收款方” |
create_rule | 创建交易规则 | “创建一条规则:当收款方为 Amazon 时,将类别设为 Shopping” |
delete_rule | 删除规则(会先预览,参见安全性) | “删除那条规则” |
参数
create_payee - name(必填)
update_payee - payee(必填):名称或 ID | name(必填):新名称
delete_payee - payee(必填):名称或 ID | confirm + confirm_name(删除时必填)
create_rule - condition_field(必填):payee、payee_name、category、category_group、account、amount、notes、imported_payee | condition_op(必填):is、isNot、oneOf、notOneOf、contains、gt、lt 等;其中 oneOf 和 notOneOf 接受列表,但仅适用于 payee、category、category_group、account、payee_name 和 imported_payee | condition_value(必填):对于 payee 或 category,填写名称或 ID | action_field(必填):category、payee、notes、account | action_value(必填):对于 payee、category 或 account,填写已存在的名称或 ID。设置 account 会移动交易;之后导入相同的转账时,系统无法识别,因此银行同步可能会产生重复项;而移出预算的行则会失去其类别 | stage(可选)。payee、category、category_group 和 account 不接受 contains、doesNotContain 和 matches:Actual 将这些字段存储为 ID,因此比较会针对 ID 进行,导致规则应用于错误的交易。对于文本,imported_payee 保存该行导入时的文本,可能来自银行,也可能来自在此处创建该行的人;对于尚未见过的商店,这是能匹配首次消费的字段。payee_name 会根据收款方当前名称进行比较,但只有在该收款方已存在后才有效,因此无法匹配首次消费。已通过两种方式实测。get_rules 会将所有 ID 字段(包括列表中的 ID)读回为名称,并标记已以另一种方式保存的规则,以及任何在应填写 ID 的字段中存有名称的规则;后者永远不会触发。
delete_rule - rule_id(必填)| confirm(删除时必填)
| 工具 | 说明 | 示例提示词 |
|---|---|---|
create_account | 创建预算内或预算外账户 | “创建一个名为 Family Investment 的预算外账户,余额为 10,000” |
delete_account | 删除账户及其历史记录 | “删除 ZZ Test 账户” |
update_account | 重命名账户 | “将 Main Checking 重命名为 Everyday Account” |
delete_account需要两个键。 删除操作会清除账户的全部交易历史,因此单次调用不会执行删除。首次调用只会预览将丢失的内容(名称、余额、交易笔数),并建议改为关闭账户,因为关闭账户会将其停用,同时保留历史记录。若要实际删除,请再次调用,并将confirm: true和confirm_name设置为账户的准确名称。在拒绝删除时,该工具会报告isError: true,因此确认提示绝不会被误认为删除已完成。
参数
create_account - name(必填)| offBudget(可选,默认值为 false)| initialBalance(可选):人类可读的金额,会创建“Starting Balance”交易。(Actual 只将账户模型分为预算内和预算外,因此没有账户 type。)
update_account - account(必填):名称或 ID | name(必填):新名称。仅重命名。不会开放预算状态(offbudget)和关闭账户的操作:将账户移入或移出预算会一次性更改每个月的总额,而关闭账户在应用中有专门的流程,会询问剩余余额的去向。如果账户名称已被其他账户使用,则会拒绝重命名,因为 Actual 允许名称重复,之后便无法通过名称区分账户。
delete_account - account(必填):名称或 ID | confirm(删除时必填):必须为 true | confirm_name(删除时必填):账户的准确名称
维护(1)
| 工具 | 说明 | 示例提示词 |
|---|---|---|
repair_sync | 修复不同步的预算 | “修复同步,所有操作都失败了” |
如果工具开始因同步错误而失败,说明预算的同步状态与服务端不一致。
repair_sync会重建该状态,不会触及预算数据。请注意,删除本地ACTUAL_DATA_DIR不能解决问题,因为不一致出在同步状态,而不是缓存。它会先检查服务端是否正常运行。 两种不同的问题会导致相同的失败:同步状态损坏,以及服务端未运行——对于桌面应用而言,这意味着应用已关闭,因为它的服务端仅在应用开启时才会在端口 5007 上运行。
repair_sync只能修复第一种问题,因此如果没有服务在监听,它会说明情况并且不做任何更改,而不会对“应用未运行”这个问题进行无谓的修复。
参数
repair_sync - 无参数
环境
ACTUAL_HTTP_TIMEOUT_MS(可选,默认值为 60000)——等待 Actual 服务开始响应的时长。开始响应后,等待完整响应的时间不受限制,因此缓慢的预算下载不会被中断。如果不设置此项,一个接受连接却始终不响应的服务会让每次写入都等待五分钟,这是 Node 自身的限制。
如果值低于 1000、超过计时器可支持的上限,或为 Infinity,则会调整为最接近的限制值,并在 stderr 中输出一行说明调整原因。如果值不是数字,或为零或负数,则保留默认值且不输出任何信息:对于从未设置此项的人,没有什么有用的信息可告知。
它会影响哪些请求,可从 SDK 中查证,而非凭空假设:
- SimpleFIN、Pluggy、Akahu 和 Enable Banking 会为获取交易的调用设置自己的限制,因此此设置不会缩短这些调用的时长。限制为 60 秒,但 SimpleFIN 同步多个账户时为 300 秒,默认银行同步使用的正是这种方式。这些限制涵盖完整响应,而不只是开始响应,因此比此设置的限制更长。
- GoCardless 不设置限制,因此其交易下载时长由此设置控制。如果银行响应较慢,导致同步被中断,请调高此值。
- 服务执行的其他所有操作,也就是对你自己的 Actual 服务进行的所有常规读取和写入。
如果你安装了 .mcpb 扩展,同一设置也位于其配置中的 服务器响应超时(ms)。
有一种情况是此设置刻意不涵盖的:服务发送响应头后停止发送响应正文。此时期限已满足,因此该请求会由 Node 自身的五分钟正文超时结束。若要涵盖这种情况,就必须对传输本身设置期限,而这正是修复此问题之前导致正常下载被中断的原因。
提示词
内置提示词模板,可引导 Claude 完成多步骤财务分析:
| 提示词 | 描述 |
|---|---|
monthly-review | 任意月份的完整预算审查:支出与预算对比、超支情况及建议 |
spending-check | 快速检查:本月进度是否符合预期? |
spending-patterns | 深入分析多个月份的支出趋势和模式 |
在 Claude Desktop 中,点击提示词图标即可使用;在 Claude Code 中,请 Claude 使用这些提示词。
资源
预加载的数据,Claude 无需调用工具即可引用:
| 资源 | URI | 描述 |
|---|---|---|
| 账户 | actual://accounts | 所有账户及其余额 |
| 类别 | actual://categories | 类别组和类别及其 ID |
| 收款方 | actual://payees | 按字母顺序排列的所有收款方 |
使用示例
以下是可供使用的真实提示词:
"How much did I spend in February?"
"Show me my top 5 spending categories this month"
"Am I over budget on anything?"
"I spent 1,200 on electricity from my checking account yesterday"
"What's my savings rate this month?"
"Show me all transactions from Wallet in the last 30 days"
"Transfer 5,000 from Checking to Savings"
"What are my spending trends for food over the last 6 months?"
"Create a category called Gym in Variable Spending"
"Rename the Gym category to Fitness"
"Create a rule: when payee is Netflix, set category to Suscripciones"
"How have my finances been the last 3 months?"
这有什么不同?
与其他 Actual Budget MCP 服务相比:
| 功能 | actual-budget-mcp | 其他 |
|---|---|---|
| 自然语言日期 | “上个月”“este mes”“hace 3 meses” | 仅支持 YYYY-MM-DD |
| 名称解析 | 输入“Wallet”,而非 UUID | 必须使用准确 ID |
| 输出格式 | 对齐的表格、易读的文本 | 原始 JSON |
| 错误消息 | 清楚说明如何修复 | 通用错误 |
| 分析工具 | 预算与实际支出对比、预测、趋势 | 不可用 |
| MCP 提示词 | 3 个引导式分析工作流 | 有限或没有 |
| MCP 资源 | 预加载账户、类别、收款方 | 不可用 |
| 双语日期 | 英语 + 西班牙语 | 仅英语 |
| 转账 | 两个关联端、匹配的 transfer_id、无类别,与应用中的方式相同 | 通常只有一端或类别错误 |
| 删除 | 先预览,再明确确认 | 立即执行 |
| 同步状态不一致时的恢复 | repair_sync 重建本地同步状态 | 重新安装并寄希望于此 |
| API 版本 | @actual-app/api 26.x(当前版本) | 通常已过时 |
安全性
- 此服务使用你提供的凭据连接到你的 Actual Budget 实例
- 凭据通过环境变量传递,MCP 服务绝不会存储这些凭据
- 与 Actual Budget 服务的所有通信都在本地进行(或连接到你的自托管服务)
- 此服务仅通过官方
@actual-app/api库访问预算数据 - 不会向第三方发送任何数据
故障排除
遇到这里没有列出的问题?告诉我是什么让你卡住了。只需一句话,因为在我看来,失败的配置和未进行配置看起来完全一样。
“无法连接到 Actual Budget 服务”
- 确认 Actual Budget 正在运行(打开应用或启动服务)
- 检查
ACTUAL_SERVER_URL是否正确 - 运行
npx -y actual-budget-mcp --verify测试连接
“身份验证失败”
- 你的服务需要密码。请在配置中设置
ACTUAL_PASSWORD - 如果忘记了密码,请在 Actual Budget 的 Settings > Server 中重置
“未找到预算”
- 检查
ACTUAL_BUDGET_ID。请在 Settings > Show advanced settings > Sync ID 中查找
“预算文件已加密”
- 设置
ACTUAL_ENCRYPTION_PASSWORD,并填入你的加密密码
“名称不明确:匹配 X、Y”
- 请使用更具体的名称。例如,不要用“Main”,而应使用“Main Checking”或“Main Savings”
Node.js 要求
服务端立即退出,或毫无提示地崩溃
- 从 0.10.1 开始,最低要求为 Node 22.14。Actual 的 SQLite 库是针对 N-API 10 构建的,该版本从此版本开始提供;在较旧的 Node 上,二进制文件会加载,然后在打开预算时发生段错误。在 22.13.1 上测得:退出代码为 139,没有提示、没有堆栈,也没有任何日志。会重启服务端的主机将会不断重复此操作。
- 现在服务端会在启动时检查这一点并给出提示,而不是崩溃,但仅限 0.10.1 及之后的版本。
- **解决方案:**运行 Node 22.14 或更高版本,或使用自带 Node 的 Docker 镜像。
“ReferenceError: navigator is not defined”
@actual-app/api在 26.6 及之前的版本中引用了navigator全局变量。该全局变量仅在 Node.js 21 及更高版本中存在,因此在 Node.js 20 上导入此库会在服务端启动前抛出错误。26.8 移除了此引用。- **解决方案:**运行 Node.js 22.14 或更高版本。
使用此服务端后,你的 Actual 应用无法再打开预算
- 打开预算时,库会运行文件中不存在的任何迁移,下一次同步时会上传迁移结果。从 0.10.1 开始,此服务端搭载 Actual 26.10,因此将它连接到 26.9 服务端时,会把预算迁移到较新的格式,而仍使用 26.9 的 Actual 应用将无法打开该预算。实测结果:迁移数从 59 增至 60。
- Actual 自带的应用更新时也会执行相同操作;区别在于,在你更新其他任何内容之前,此服务端就可能访问你的预算。
- 当服务端启动时,如果发现你的 Actual 服务端版本低于其库的版本,它会在下载任何内容之前发出警告。
- **解决方案:**使用此版本之前,请先将 Actual 服务端和应用更新到 26.10 或更高版本。如果你已经遇到此问题,更新它们后即可重新打开预算,且不会丢失任何内容。
“version `GLIBC_2.34' not found”,或在较旧的 Linux 上始终无法打开预算
- Actual 的 Linux SQLite 二进制文件是针对 glibc 2.34 构建的,因此无法在 Debian 11、Ubuntu 20.04 或 RHEL 8 上加载,因为这些系统的 glibc 版本较旧。针对 Debian 11 的 libc 实测结果:
version 'GLIBC_2.33' not found。 - 没有可用的覆盖方案:
package.json覆盖项不会应用于npx安装,而且该二进制文件由库本身提供。 - **解决方案:**运行自带 glibc 的 Docker 镜像,或使用 glibc 2.34 或更高版本的发行版(Debian 12、Ubuntu 22.04、RHEL 9)。
从源代码安装时出现“gyp ERR! find Python”
- 从克隆仓库安装时会运行
npm ci,而 npm 会构建任何包含binding.gyp的软件包。better-sqlite3虽然不需要构建,但也会触发此操作:它使用的二进制文件已包含在软件包中。 - **解决方案:**使用
npm ci --ignore-scripts。不会丢失任何内容;扩展和 Docker 镜像正是这样做的。
Node 版本管理器(fnm、nvm、volta)
Claude Desktop 中的 MCP 服务显示“Server disconnected”
- Claude Desktop 不会加载你的 shell 配置文件(
.bashrc、.zshrc),因此 fnm、nvm 和 volta 等版本管理器无法与默认的npx命令配合使用。这适用于配置文件中的手动npx条目,不适用于自带依赖项的桌面扩展。 - **解决方案:**在配置中使用 node 的绝对路径。使用以下命令查找:
readlink -f $(which node)
然后更新你的 claude_desktop_config.json:
{
"mcpServers": {
"actual-budget-mcp": {
"command": "/home/user/.local/share/fnm/node-versions/v22.22.1/installation/bin/node",
"args": ["/path/to/actual-budget-mcp/dist/index.js"],
"env": {
"ACTUAL_SERVER_URL": "http://localhost:5006",
"ACTUAL_PASSWORD": "your-password",
"ACTUAL_BUDGET_ID": "your-budget-sync-id"
}
}
}
}
或者,创建一个包装脚本 mcp-wrapper.sh:
#!/bin/bash
export PATH="$HOME/.local/share/fnm/node-versions/v22.22.1/installation/bin:$PATH"
exec npx -y actual-budget-mcp "$@"
然后在配置中使用它:
{
"mcpServers": {
"actual-budget-mcp": {
"command": "/path/to/mcp-wrapper.sh"
}
}
}
参与贡献
欢迎贡献!请提交 issue 或拉取请求。
git clone https://github.com/henfrydls/actual-budget-mcp.git
cd actual-budget-mcp
npm install
npm run build
npm test # Run unit tests
npm run test:connection # Needs .env configured
许可证
MIT - DLSLabs
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。