内容
**English** | [Español](README.es.md)
# 实际预算MCP服务器
一个连接LLM(如Claude Desktop和任何支持MCP的客户端)到自托管的[实际预算](https://actualbudget.org/)实例的MCP(模型上下文协议)服务器。该服务器专门为LLM使用而构建,具有正确的财务语义(转账不计入支出摘要,净资产包括预算外账户),金额在服务器端转换,以便模型永远不会进行分币计算,紧凑的输出,通过带有TTL的同步获取新鲜数据,以及由子交易支持的**真实**分割交易。
## 功能亮点
设计基于五个支柱:
- **正确的财务语义** — 转账(`transfer_id`) 不计入支出摘要,以免金额被重复计算,净资产包括预算外账户(养老金、基金、抵押贷款)。
- **服务器端单位转换** — 实际预算存储金额以分币为单位;每个工具返回的值已除以100,并附有 `amount_formatted` 字段。LLM永远不会进行数学运算。
- **紧凑、适合LLM的输出** — 响应被修剪为模型需要的信息,以保持低令牌使用率。
- **带有同步和TTL的新鲜数据** — 服务器在提供数据之前与实际预算同步,缓存同步以保持快速而不过时。
- **真实的分割交易** — `split_transaction` 在实际预算中创建真正的子交易,而不是伪造的笔记或变通方法。
## 在Claude Desktop中安装(推荐)
**要求:** 带有Apple Silicon(M1或更新版本)的Mac,Claude Desktop和一个自托管的实际预算服务器。 在Intel Macs,Windows或Linux上,请改用[手动安装](#手动安装)。
1. **下载** 最新版本的 `actual-budget-mcp-es.mcpb`,请访问 [Releases 页面](https://github.com/marcoaperez/actual-budget-mcp-es/releases/latest)。
2. **查找您的同步ID** — 在浏览器中打开实际预算,前往 **设置** → **高级设置** → **ID**,并复制 **同步ID**。
3. **安装** — 在Claude Desktop中,打开 **设置 → 扩展**,将下载的 `.mcpb` 文件拖到页面上,查看它声明的内容,然后选择 **安装**。Claude Desktop会注意到扩展程序未签名 — 这是自分布式捆绑包的预期行为。
4. **填写表单** Claude Desktop显示:
- **实际服务器URL** — 例如 `https://actual.example.org/`
- **服务器密码** — 您在浏览器中打开实际预算时使用的密码(由Claude Desktop的安全存储存储,不在配置文件中)
- **预算同步ID** — 来自步骤2
- **语言** 和 **货币** — 例如 `zh-CN` 和 `CNY`;默认为 `en-US` 和 `USD`
- **启用写入工具** — 暂时关闭(见下文)
- **消息语言** — `zh` 或 `en`;默认为 `zh`
5. **检查它是否有效** — 启动一个新的对话并询问 *"我有哪些账户?"*。您应该获得带有余额的账户列表。
### 启用写入访问
选中复选框关闭,您将获得8个只读工具。打开它(设置 → 扩展 → 此扩展 → 配置)以添加8个写入工具:编辑和分类交易,真实分割,计划交易,预算分配和类别注释。Claude Desktop在每次调用之前都会征求您的批准,除非您授予它永久许可,并且没有删除工具。
### 更新
下载更新的 `.mcpb` 并再次拖入 — 您的配置将被保留。
### 如果Claude Desktop更新后所有工具都失败
该捆绑包附带一个为特定Node ABI编译的原生SQLite模块(`better-sqlite3`)。Claude Desktop不会使用系统的Node运行扩展程序:它在自己的Electron运行时环境中启动它们,因此模块必须与该运行时环境匹配。当Claude Desktop更新更改运行时环境时,模块可能无法加载,扩展程序无法工作。
服务器在启动时检测到这一点,并明确说明:每个工具都使用涉及的两个版本号进行回复,并告诉您安装更新的捆绑包,而不是使用预算关闭的模糊消息。
您这边没有任何修复方法 — 捆绑包必须为新的运行时环境重建。获取更新版本,或使用 `npm run bundle` 在安装了更新的Claude Desktop的机器上自行构建。
这仅影响 `.mcpb` 捆绑包。 [手动安装](#手动安装) 在您自己的Node下运行,并不受其影响。
## 手动安装
适用于其他MCP客户端、Intel Macs、Windows和Linux。 需要Node.js >= 22 和一个自托管的实际预算服务器(>= 25.x;针对API 26.7构建),可以通过网络访问。
该项目是用TypeScript编写的,因此在使用之前需要构建:
```bash
git clone https://github.com/marcoaperez/actual-budget-mcp-es.git
cd actual-budget-mcp-es
npm install
npm run build # 将TypeScript编译到dist/
```
这会生成 `dist/index.js`,您的MCP客户端运行的入口点。
需要您的同步ID吗?请参见上面[在Claude Desktop中安装(推荐)](#在claude-desktop中安装推荐)中的步骤2。
### 客户端配置
将以下内容添加到您的 `claude_desktop_config.json`
(macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`),或适用于您的MCP客户端的等效配置文件。
将 `command` 指向 `node`,将 `args` 指向编译后的 `dist/index.js`,使用绝对路径:
```json
{
"mcpServers": {
"actual-budget": {
"command": "node",
"args": ["/absolute/path/to/actual-budget-mcp-es/dist/index.js"],
"env": {
"ACTUAL_SERVER_URL": "https://actual.example.org/",
"ACTUAL_SERVER_PASSWORD": "your-password",
"ACTUAL_SYNC_ID": "00000000-0000-0000-0000-000000000000",
"LOCALE": "zh-CN",
"CURRENCY": "CNY",
"ENABLE_WRITE": "false"
}
}
}
}
```
使用`"LOCALE": "en-US"`和`"CURRENCY": "USD"`进行美国格式化,或使用`"zh-CN"`和`"CNY"`进行中国格式化。仅当您想要写入工具时,请设置`"ENABLE_WRITE": "true"`(请参见下文)。保存后重新启动Claude Desktop,以便它检测到新的MCP服务器。
在开发过程中,您可以跳过构建并直接使用`tsx`运行TypeScript:
`"command": "npx"`,`"args": ["tsx", "/absolute/path/to/actual-budget-mcp-es/src/index.ts"]`。
### 环境变量
前三个变量是必需的;其余变量有默认值。
| 变量 | 必需 | 默认值 | 描述 |
|---|---|---|---|
| `ACTUAL_SERVER_URL` | 是 | — | 自托管的实际服务器URL |
| `ACTUAL_SERVER_PASSWORD` | 是 | — | 实际服务器密码 |
| `ACTUAL_SYNC_ID` | 是 | — | 预算同步ID(设置 → 高级 → 同步ID) |
| `ACTUAL_DATA_DIR` | 否 | `./data` | 下载的预算本地副本的文件夹 |
| `LOCALE` | 否 | `zh-CN` | 用于格式化金额和日期的语言环境(例如 `zh-CN`) |
| `CURRENCY` | 否 | `CNY` | 3位ISO货币代码(例如 `CNY`) |
| `SYNC_TTL_SECONDS` | 否 | `60` | 同步缓存的秒数,之后重新同步 |
| `ENABLE_WRITE` | 否 | `false` | 启用8个写入工具(分类、分割、计划、预算、注释) |
| `MESSAGES_LANG` | 否 | `zh` | 服务器错误消息的语言:`zh` 或 `en` |
## 工具
### 只读工具(8个)
始终可用。
| 工具 | 描述 |
|---|---|
| `get_accounts` | 具有当前余额的账户;默认包括预算外账户(养老金、基金、抵押贷款) |
| `get_net_worth` | 完整的净资产:预算内 + 预算外,逐一分解每个账户 |
| `get_categories` | 类别组和类别及其ID(写入工具需要) |
| `get_transactions` | 按日期范围的交易,可选过滤器(账户、类别、文本、金额、未分类)。不包括转账。分页(最多200) |
| `get_spending_summary` | 按类别、收款人或月份汇总的支出。始终不包括转账 |
| `get_budget_month` | 一个月的预算状态:预算、收入、总额和每个类别的预算/支出/余额 |
| `get_budget_averages` | 最近N个月每个类别的平均、中间和最大支出 — 预算准备的输入 |
| `get_schedules` | 实际预算中配置的计划/定期交易(名称、下一次日期、金额) |
### 写入工具(8个)
仅在 `ENABLE_WRITE=true` 时注册。
| 工具 | 描述 |
|---|---|
| `update_transaction` | 修复交易(金额、类别、收款人、备注、日期) |
| `categorize_transactions` | 批量分类(最多50个),每个项目的结果 |
| `split_transaction` | 将交易分成真正的部分 — 替换交易(新ID) |
| `create_transaction` | 记录支出/收入,创建时可选分割 |
| `create_schedule` | 创建计划/定期交易 |
| `set_budget` | 批量为某个月的多个类别分配预算 |
| `transfer_budget` | 在类别之间转移已分配的预算 |
| `update_category_note` | 类别备注和目标模板 |
> **关于写入工具的说明**
> - 仅当 `ENABLE_WRITE=true` 在 `env` 块中设置时,它们才会出现。
> - **输入金额为货币单位** (欧元/美元,不是分币);服务器处理转换。
> - `split_transaction` **替换** 原始交易为带有子交易的新的交易,因此生成的交易具有 **新的ID**。
## 安全
- **无删除工具** — 服务器无法删除交易、账户或预算。
- **默认禁用写入** — 除非 `ENABLE_WRITE=true`,否则写入工具永远不会注册。
- **分割保护** — 写入工具防止直接编辑分割部分或分割父级。
- 永远不要提交您的 `.env`。实际密码仅存储在您的本地 `.env` 或客户端配置文件中。
启用写入前,请了解三件事:
- `data/` 包含您整个预算的**纯文本,未加密副本** (仅限所有者,模式 `0700`)。
- 收款人和备注来自银行同步或CSV导入:它们是**不受信任的文本**,不是指令。请在MCP客户端中保持每次调用的批准开启。
- `split_transaction` **替换** 交易,因此它获得新的ID。
- 每个连接的MCP客户端都启动其自己的服务器进程。只有一个进程可以打开预算,因此额外的实例自动使用其自己的本地副本(`<data dir>-instance-<pid>`)。在手动安装中,您可以按客户端设置 `ACTUAL_DATA_DIR`(Claude Desktop捆绑包不公开此设置)。
完整的[安全模型 — 信任边界、凭证、破坏性操作、输入验证以及如何报告漏洞 — 请参见SECURITY.md](SECURITY.md)。
## 隐私政策
该软件不收集任何数据:没有遥测、分析或更新检查。它在您的机器上运行,仅与您自己的实际预算服务器和您连接的MCP客户端通信,后者根据其自身的隐私政策查看您询问的财务数据。
完整文本: [PRIVACY.md](PRIVACY.md) · [中文版本](PRIVACY.zh.md)
## 开发
```bash
npm test # 使用Vitest(127个测试)运行完整套件
npm run dev # 使用tsx运行src/index.ts(无构建)
npm run smoke # 使用.env进行手动检查(不适用于CI)
```
服务器通过stdio(stdin/stdout)进行通信,因此手动运行它不会显示交互式输出 — 日志输出到stderr。
### 添加新工具
1. 在 `src/tools/my_new_tool.ts` 中创建一个文件
2. 导出一个 `ToolDef` (`name`、`description`、`readOnly`、`inputSchema` 使用Zod,`handler`)
3. 在 `src/tools/index.ts` 的 `allTools` 数组中导入并注册它
`handler(input, ctx)` 接收一个初始化的 `{ api, config }` 上下文;使用 `moneyFields`/`toUnits` 从 `src/core/money.js` 始终以单位返回金额(从不以分币为单位)。
### 发布捆绑包
> **捆绑包与Claude Desktop的Electron ABI绑定。** 扩展程序在Claude Desktop自己的Electron中运行,而不是在系统的Node中,因此 `better-sqlite3` 必须编译为该运行时环境。 `npm run bundle` 从安装的 `Claude.app` 读取Electron版本,为其重新安装预构建的二进制文件,然后通过 `dlopen` 使用打包Node进行验证 — 该操作必须失败并报告Electron的ABI。
> 因此: **在针对的Claude Desktop上构建捆绑包** ,或传递 `ELECTRON_TARGET=x.y.z npm run bundle`。 每当Claude Desktop更新其Electron主版本时,请重新发布 — 以前的捆绑包对新版本的所有用户都停止工作。
1. 更改 `package.json` 和 `manifest.json` 中的 `version` **和** (强制测试确保它们匹配)。
2. `npm test && npm run build && npm run bundle` — 最后一行报告构建的Electron版本和ABI。请在发布描述中记录它。
3. 验证捆绑包:
```bash
npx mcpb validate manifest.json
unzip -l build/actual-budget-mcp-es.mcpb | grep -E 'tests/|docs/|\.env' | grep -v 'node_modules/' # 预期输出为空
unzip -l build/actual-budget-mcp-es.mcpb | grep -c 'better_sqlite3.node' # 预期至少为1
```
`npx mcpb info <file>` (CLI 2.1.2)仅打印文件名、大小和签名状态 — 它不检查清单,因此它不是 `validate` 的替代品。 `node_modules/` 排除项在此处是因为供应商依赖文件不是项目的代码,而是合法地包含 `tests/` 等路径 — 请勿删除它。
4. 在本地安装 `build/actual-budget-mcp-es.mcpb` 并检查它是否有效。
5. `gh release create vX.Y.Z build/actual-budget-mcp-es.mcpb` ,在发布描述中注明捆绑包针对的是Apple Silicon上的macOS。
## 许可证
MIT — 请参见 [LICENSE](LICENSE)。 版权所有 (c) 2026 Marco Antonio Pérez。