YNAB预算API服务器

一个为YNAB预算API提供的模型上下文协议服务器,支持读取预算计划、账户、类别、交易和月度摘要,可选择启用写入功能。

  • by bold-minds
  • Go
  • 本地部署
  • 更新于
在 GitHub 查看 bold-minds/mcp-ynab

使用说明

mcp-ynab

A. 默认情况下为只读 模型上下文协议服务器 你需要一个预算 API预算编制。让LLM(Claude Desktop、Cursor、Claude Code或任何兼容MCP的客户端)检查您的计划、账户、类别、交易和月度摘要。编写工具(创建/更新/批准交易、更新预算类别)可用 只有当 YNAB_ALLOW_WRITES=1 在环境中设置——未设置时,启动时不会注册写入,也无法调用。

内置Go against the 官方MCP Go SDK一个二进制stdio传输,无入站网络表面,操作系统密钥环令牌存储。

为什么只读

YNAB个人访问令牌是长期和全范围的——它们授予读取权限 和 在账户上的每个计划上都写上,YNAB对突变没有批量撤销。LLM可以调用 deleteTransaction 这是一份及时注入的交易备忘录,可以避免在没有追索权的情况下扰乱预算。此服务器故意暴露 仅 该设计没有改变YNAB数据的代码路径。

工具

读取工具(始终可用)

工具 说明
list_plans 列出经过身份验证的用户拥有的所有YNAB计划(在YNAB UI中称为“预算”)。
list_accounts 列出计划中具有当前余额的账户。默认情况下不包括已关闭的帐户。Delta已同步。
list_categories 列出计划中的所有类别,包括本月分配/活动/余额金额和目标详细信息。
list_transactions 列出计划的交易记录,最近的交易记录排在第一位。筛选依据 since_date,批准状态(type),或范围-- 之一 account_id, category_id,或 payee_id。未过滤的变体是增量同步的。类别/收款人范围简化了拆分交易。默认限制100,最大500。
list_months 最近几个月的月度汇总摘要(收入、预算、活动、预算、货币年龄),最近的先到。用于逐月趋势。默认限制6,最大60。
get_month 完整的计划月,按类别细分。接受 "current" 或 YYYY-MM-01。用于周日仪式/真实费用检查
list_scheduled_transactions 按date_next顺序(最快优先)排列的定期和未来日期的计划交易。可选的 upcoming_days 过滤器
list_payees 在计划中列出收款人。可选的 name_contains 执行不区分大小写的子字符串匹配——用于在调用之前将收款人名称解析为ID list_transactions 和 payee_id 或 ynab_spending_check 和 excluded_payee_ids.

任务型工具(基元组合)

工具 说明
ynab_status 一个呼叫周日仪式仪表板:准备分配、超支类别(不包括信用卡支付类别)、具有可选APR增额的债务账户、流动账户(支票/储蓄/现金)、每个账户自上次对账以来的天数、未批准的计数以及未来7天具有重复扩展的计划现金流。
ynab_spending_check “我本周在杂货上的花费是否低于500美元?”与预算相比,在一个日期范围内,一个或多个类别的净流出之和,返回 on_plan 超出预算时的判决和违规交易。支持 excluded_payee_ids 比如“约会之夜除了Chipotle”。
ynab_weekly_checkin 收入、流出和未批准计数的每周比较,加上每月新超支类别和货币年龄增量。
ynab_debt_snapshot 当前债务余额+雪崩式回报预测。整数基点模拟,复合循环中没有浮点数。可选的 extra_per_month_milliunits 运行一个比较场景。当最低金额无法覆盖利息时,返回结构化负摊销误差和短缺金额。
ynab_waterfall_assignment 咨询,不写。 按类别行走优先瀑布 need_milliunits 技能已经计算,返回建议的分配和剩余部分。法学硕士介绍了该计划;如果获得批准,则发布 update_category_budgeted 单独通话。

编写工具(选择加入,需要 YNAB_ALLOW_WRITES=1)

书写工具有 启动时未注册 除非 YNAB_ALLOW_WRITES=1 在MCP服务器的环境中设置。禁用时,它们不会出现在 tools/list 根本无法调用。

工具 说明
create_transaction 创建新交易。通过启发要求MCP客户确认。金额>1万美元需要 amount_override_milliunits 回复确认。提供一个 import_id 在重试时进行幂等重复数据删除。
update_category_budgeted 更改单个计划月的单个类别的分配数量。规则3货币的原始货币在周日仪式中移动。返回预算和余额快照之前/之后的结果。
update_transaction 交易的部分更新:类别、收款人、备注、已批准状态、已清算状态、标志颜色。 金额变化在结构上不受支持 --输入结构体没有数量字段,由反射回归测试强制执行。
approve_transaction 便利包装设置 approved=true 在一笔交易中。 每次通话引发跳过 以支持批处理日常待处理清理工作流。这 YNAB_ALLOW_WRITES=1 env-var-gate仍然是主要的防御手段。

所有阅读和任务型工具都有广告 readOnlyHint: true 在他们的MCP注释中。写工具广告 readOnlyHint: false.

工具数量:

  • 没有 YNAB_ALLOW_WRITES: 13工具 (8次阅读+5次任务型)
  • 随着 YNAB_ALLOW_WRITES=1: 17工具 (添加4条文字)

每种工具都能实现什么

用例 工具
“我这周在杂货店花了多少钱?” list_categories → 查找杂货id→ list_transactions 和 category_id
债务快照(所有债务账户+余额) list_accounts (筛选到信用卡/贷款/债务类型)
真实费用检查/超支检测 list_categories 或 get_month
环比趋势(“我的支出是否比上个月多?”) list_months
“这个月有什么计划?”(租金、订阅费) list_scheduled_transactions 和 upcoming_days: 30
已准备好分配瀑布对话 get_month → to_be_budgeted 现场
收款人模式检测 list_transactions 和 payee_id
饮食计划审核 list_categories → 杂货店/餐厅ID→ list_transactions 和 category_id 和 since_date

金钱代表

工具响应中的每个货币值都是 Money 具有两个字段的对象:

"balance": { "milliunits": 123456, "decimal": "123.456" }
  • milliunits 是权威的int64值——YNAB的原生格式,适用于每种货币。
  • decimal 是一个具有3个小数位数(毫单位精度)的预格式化字符串。

此服务器中没有使用代码路径 float64 对于货币。 格式化仅通过整数运算执行。看 money.go.

安装

自制/预构建二进制文件

从下载版本 GitHub 发布 --Linux、macOS和Windows的静态二进制文件(amd64和arm64)。

码头工人

docker pull ghcr.io/bold-minds/mcp-ynab:latest

基于Distroles的映像(没有shell,没有包管理器),以非根、静态二进制、仅stdio的形式运行——没有暴露的端口。

来源

go install github.com/bold-minds/mcp-ynab@latest

需要Go 1.25或更高版本。

配置

获取YNAB个人访问令牌

登录YNAB,转到 账户设置→ 开发人员设置,并创建一个 个人访问令牌。复制一次,以后无法检索。

将令牌提供给服务器

三个选项,按优先顺序排列。服务器按以下顺序检查它们,并使用提供非空值的第一个源。

1.OS钥匙圈(推荐)

将令牌一次性存储在操作系统的本机凭据存储中(macOS Keychain、Linux Secret Service、Windows凭据管理器):

printf '%s' "your-ynab-personal-access-token" | mcp-ynab store-token

令牌是从stdin读取的——它从不出现在命令行上,因此它永远不会出现在shell历史记录中或 /proc/PID/cmdline后续的运行 mcp-ynab 自动拾取它,不需要环境变量。

要旋转,请重新运行 store-token 新的价值。

2.基于文件的秘密

适用于Docker secrets、systemd LoadCredential,或Kubernetes秘密卷:

export YNAB_API_TOKEN_FILE=/run/secrets/ynab_token

将文件保存在 chmod 600.

3.环境变量(明文)

最简单的选择,但令牌在进程环境中以明文形式存在:

export YNAB_API_TOKEN=your_personal_access_token

将其连接到您的MCP客户端

克劳德桌面

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)或您操作系统上的等效软件。如果您已将令牌存储在操作系统密钥环中,则不需要 env 完全阻塞:

{
  "mcpServers": {
    "ynab": {
      "command": "/path/to/mcp-ynab"
    }
  }
}

如果您更喜欢基于文件的:

{
  "mcpServers": {
    "ynab": {
      "command": "/path/to/mcp-ynab",
      "env": {
        "YNAB_API_TOKEN_FILE": "/Users/you/.config/ynab/token"
      }
    }
  }
}

做 不 将原始令牌粘贴到 claude_desktop_config.json --该文件通常是全球可读的,可以通过iCloud/Time Machine进行备份。请改用钥匙圈。

光标

编辑 ~/.cursor/mcp.json:

{
  "mcpServers": {
    "ynab": {
      "command": "mcp-ynab"
    }
  }
}

(令牌来自钥匙圈。)

其他MCP客户端

任何使用MCP stdio传输的客户端都可以启动 mcp-ynab 作为一个子流程。配置命令以运行二进制文件,并通过以下方式提供令牌 YNAB_API_TOKEN, YNAB_API_TOKEN_FILE,或OS钥匙圈(推荐)。

安全模型

此服务器保护什么

威胁 缓解
提示注入指令,告诉LLM删除/修改财务数据 写入工具被关闭 YNAB_ALLOW_WRITES=1 (在启动时选择加入),每次写入都会引发每次呼叫确认,外加1万美元的安全上限和回显覆盖。当env-var未设置时,没有注册写工具,也没有 POST/PATCH/PUT 可以到达代码路径。
通过日志语句泄漏令牌/ %+v 在配置结构体上 令牌被封装在密文中 Token 类型。 String, GoString, Format, MarshalJSON, MarshalText 全部返回 [REDACTED]原始值只能通过私有包访问 reveal() 只在一个地方打电话。
通过HTTP客户端错误导致令牌泄漏(错误模式中的axios样式配置) 对抗性回归测试:病理性RoundTripper返回包含文字令牌的错误;每个工具的错误路径都被断言不会与之产生回声
通过恶意URL过滤YNAB令牌(SSRF/spec注入) 自定义 http.RoundTripper 拒绝任何主机名不是的请求 api.ynab.com (不区分大小写,端口容忍)。条带 Authorization 防守。拒绝所有重定向。
LLM通过YNAB错误响应发现令牌泄漏 所有YNAB错误都会通过 sanitize(),哪些条带 Bearer <token> 和 Authorization: 模式。没有代码路径将原始令牌格式化为错误。
失控LLM耗尽YNAB的每令牌速率限制(200 req/小时) 令牌桶速率限制器:每20秒重新填充1个请求,突发10个。峰值:10次爆发+3600/20次补充=第一小时190次呼叫。突发耗尽后的稳态:180个呼叫/小时(3600/20)。在RoundTripper中强制执行。
具有凭据的LLM的无限制写访问 写入工具是可选的。 除非 YNAB_ALLOW_WRITES=1 在MCP服务器启动时设置,写入工具根本没有注册,无法调用。启用写入后,每次写入都会经过MCP启发确认,这是一个金额安全上限,对>1万美元的交易进行回显覆盖,并在响应中返回状态之前/之后,以便调用技能可以保留审计记录。
现有交易的金额更新错误 update_transaction 其输入结构体上没有amount字段——通过此工具在结构上无法更改amount。回归测试通过反射来强制字段的缺失。
上游挂起 每个请求超时30秒,8 MB响应体上限。
明文令牌存储 OS密钥环是推荐的存储路径;基于文件和env-var是折衷的后备方案。
仅对服务器 stdio传输进行入站网络攻击--没有侦听套接字,没有HTTP端点。
货币算术中的浮点漂移 所有货币存储为64 million单位。格式化仅限于整数算术。不 float64 在金钱之路上的任何地方。

纵深防御

  • 令牌类型: Token struct会阻塞每个标准格式/序列化路径。 UnmarshalJSON 和 UnmarshalText 拒绝,因此攻击者控制的有效载荷无法注入有效的令牌。
  • 模式验证:在任何处理程序运行之前,工具参数都会根据SDK派生的JSON模式进行验证。缺少必填字段将生成JSON-RPC -32602 invalid params SDK层的协议错误——我们的处理程序永远无法访问。
  • 工具边界处的错误清理:每个处理程序都会将其错误打包 sanitizedErr 在返回之前,作为最后的防御,即使内部错误没有经过客户端自己的清理就逃脱了。
  • 不执行shell:没有 os/exec 调用凭据或运行时路径中的任何位置。
  • 没有硬编码凭据:测试使用假哨兵值。

它不能保护什么

  • 一个完全妥协的上游 api.ynab.com (TLS验证已打开,但没有证书固定)。
  • 完全受损的MCP客户端(客户端以明文形式看到每个工具响应)。
  • 您自己机器上的对抗用户,具有对密钥环/令牌文件的读取权限。
  • YNAB错误响应体中出现的裸令牌值为非-Bearer-前缀子字符串(实践中不太可能)。

启用写入

默认情况下, mcp-ynab 是只读的。要启用写入工具,请设置 YNAB_ALLOW_WRITES=1 在MCP服务器的环境中:

{
  "mcpServers": {
    "ynab": {
      "command": "mcp-ynab",
      "env": {
        "YNAB_ALLOW_WRITES": "1"
      }
    }
  }
}

启用写入时:

  • 4个写入工具出现在 tools/list 除了13个阅读和任务型工具之外。
  • 每次写入都会经过MCP启发确认提示(在支持它的客户端上——Claude Code会,Claude Desktop不会;env-var-gate是没有启发的客户端的唯一防御)。
  • 金额>10亿美元的单位需要 amount_override_milliunits 参数等于主量,迫使LLM显式重新断言该值。
  • 每次写入都会在其响应中返回状态之前/之后;调用技能负责从这些响应中持久化审计跟踪(MCP本身不向磁盘写入日志)。

已知限制

  • 增量同步仅未经过滤。 list_accounts 未经过滤 list_transactions 使用 last_knowledge_of_server 以节省带宽。过滤 list_transactions (与 since_date, type,或作用域)总是进行完整的提取——YNAB在过滤端点上的增量语义记录不足。
  • 增量缓存没有TTL或大小上限 在v0.2.0版本中。对于单会话MCP流程(分钟到小时),这是可以的;对于长时间运行的守护进程,它会无限增长。看 delta.go 关于内存配置文件的讨论。
  • 仅限英语 假设——见 docs/ASSUMPTIONS.md 对于完整列表,特别是“信用卡支付”组名称匹配 ynab_status.
  • twiceAMonth 日程表 近似为递归迭代器的15天进展,因为YNAB的API不公开用户的两个锚定日。对于7天的仪表板窗口,这最多会减少一次。看 docs/ASSUMPTIONS.md.
  • 金额上限与货币无关 1000万单位。更正为美元(1万美元);比不同子单位规模的货币(例如,日元~70美元等值)的预期更严格。

路线图

从v0.2推迟:

  • 货币感知金额上限 --取回计划 currency_format.iso_code 并使用每种货币的阈值表。
  • 有界增量缓存 --LRU驱逐或基于时间的TTL长时间运行会话模式揭示了它是否重要。
  • twiceAMonth 精确膨胀 --使用 date_first 和 date_next 得出每月两个锚定日。
  • 过滤读取的增量同步 --一旦YNAB的文档通过查询过滤器澄清了语义。
  • 写入工具: delete_transaction --风险最高的写作,在有强有力的用户案例之前不会发货。

发展

go test -race ./...        # run tests with race detector
go vet ./...
go build ./...
staticcheck ./...
govulncheck ./...

Docker构建:

docker build -t mcp-ynab:dev --build-arg VERSION=dev .

在本地运行真正的YNAB API

echo -n "your-token" | mcp-ynab store-token   # one-time, uses OS keyring
./mcp-ynab                                     # run the server

服务器从stdin读取JSON-RPC消息,并将响应写入stdout。所有日志都会进入stderr——二进制文件永远不会从自己的代码中接触stdout,否则会损坏传输框架。

添加工具

  1. 在中定义输入/输出结构 tools.go 和 json 和 jsonschema 标签。
  2. 在上添加处理程序方法 *Client.
  3. 在中注册 registerTools 与适当 ToolAnnotations 提示。
  4. 在中添加测试 tools_test.go 使用 testClient 帮手。
  5. 在中添加子流程测试 subprocess_test.go 如果工具有必需的参数,则确认SDK验证层拒绝缺少的字段。

MCP SDK会自动从结构类型中导出JSON模式,并在处理程序运行之前验证传入的参数。

许可证

麻省理工学院——见 许可证.