mcpField
W
Wafeq

Wafeq

本地服务
github.com金融数据16 周前更新

关于 Wafeq

Wafeq 是面向 Wafeq 会计数据用户的 MCP 服务,将公共 API 转换为工具,并为操作标注安全类别。它支持 stdio 和 Streamable HTTP,并针对重试、幂等性、文件上传及 PDF 响应进行处理。

主要功能

  1. 01由 OpenAPI 生成 Wafeq API 工具
  2. 02为操作标注安全类别
  3. 03写入请求自动添加幂等键
  4. 04支持文件上传与 PDF 二进制响应
  5. 05验证组织身份并提供健康状态
  6. 06支持重试、限流及 stdio 和 HTTP

适用场景

  1. 01查询损益表及未付款发票
  2. 02创建发票草稿或下载 PDF
  3. 03将收据附加到费用记录
  4. 04核对银行流水与账簿

MCP 相关

该 MCP 服务将 Wafeq 公共 API 暴露为工具,支持 stdio 和 Streamable HTTP;启动时验证凭据所属组织。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ohneben-wafeq": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"MCP_TRANSPORT",
"-e",
"WAFEQ_API_KEY",
"-e",
"WAFEQ_ACCESS_TOKEN",
"-e",
"WAFEQ_TOOL_GROUPS",
"-e",
"WAFEQ_API_BASE_URL",
"-e",
"MCP_AUTH_TOKEN",
"ghcr.io/ohneben/wafeq-mcp:2.1.0"
],
"env": {
"MCP_TRANSPORT": "<MCP_TRANSPORT>",
"WAFEQ_API_KEY": "<WAFEQ_API_KEY>",
"WAFEQ_ACCESS_TOKEN": "<WAFEQ_ACCESS_TOKEN>",
"WAFEQ_TOOL_GROUPS": "<WAFEQ_TOOL_GROUPS>",
"WAFEQ_API_BASE_URL": "<WAFEQ_API_BASE_URL>",
"MCP_AUTH_TOKEN": "<MCP_AUTH_TOKEN>"
}
}
}
}
启动命令docker run -i --rm -e MCP_TRANSPORT -e WAFEQ_API_KEY -e WAFEQ_ACCESS_TOKEN -e WAFEQ_TOOL_GROUPS -e WAFEQ_API_BASE_URL -e MCP_AUTH_TOKEN ghcr.io/ohneben/wafeq-mcp:2.1.0
需要配置的环境变量
MCP_TRANSPORTServer transport. Set to 'stdio' for local MCP clients; the image otherwise starts the Streamable HTTP transport.
WAFEQ_API_KEY密钥Private organization API key, sent as `Authorization: Api-Key <key>`. Wafeq -> Settings -> Developer -> API keys. Required unless WAFEQ_ACCESS_TOKEN is set.
WAFEQ_ACCESS_TOKEN密钥OAuth2 app access token, sent as `Authorization: Bearer <token>`. Use instead of WAFEQ_API_KEY.
WAFEQ_TOOL_GROUPSComma-separated resource groups to expose, e.g. invoices,bills,reports. Empty exposes all 251 generated tools.
WAFEQ_API_BASE_URLOverrides the Wafeq API base URL. Defaults to https://api.wafeq.com/v1
MCP_AUTH_TOKEN密钥Only for MCP_TRANSPORT=http: bearer token required on /mcp. Without it a non-loopback bind refuses to start.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocighcr.io/ohneben/wafeq-mcp:2.1.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

ohneben 的 Wafeq MCP

Buy Me A Coffee


许可证与检查

CI License: MIT

MCP 注册目录

MCP Registry Listed on mcpservers.org Wafeq-MCP MCP server

通过 Claude、Cursor 等 AI 助手和其他 MCP 客户端,使用日常语言管理你的 Wafeq 账簿。

这个 Model Context Protocol 服务提供 Wafeq Public API——全部 251 个端点,直接根据 OpenAPI 规范生成 MCP 工具,另加两个手写工具。每个工具都带有安全类别(🟢 只读 / 🟡 写入 / 🟠 状态变更 / 🔴 不可逆或破坏性操作),让助手在调用前就了解操作的影响——包括保存发票与向税务机关申报发票之间的区别,而 CRUD 形式的封装无法告诉你这一点。它支持 stdio(Claude Desktop 和其他本地启动器)或 Streamable HTTP(在 Docker 中托管),并提供重试、客户端限流、请求超时、幂等键、多部分上传和二进制 PDF 处理功能,可稳定应对实际账簿的使用场景。

为什么你会需要它

有些 MCP 服务只是转发 API。这个服务旨在安全地交由大模型使用,并且易于连接真实会计数据:

你将获得重要原因
全部 251 个端点,由规范驱动全面覆盖发票、账单、报价单、贷项通知单和借项通知单、付款、银行业务、日记账、薪资、项目、库存和报告——不挑选,也不遗漏。
九种安全类别,而非四种 🟢 / 🟡 / 🟠 / 🔴Wafeq 的十多个 POST 请求并非创建操作。预览不会写入任何内容;提前结束摊销会过账到分类账,且无法撤销;向税务机关申报发票会让你的组织永久留下记录。每种操作都有自己的提示横幅,而不是一股脑归为“创建”。
连接时发送服务端说明客户端会预先了解如何解读安全提示横幅,以及少数 Wafeq 约定——日期格式、小数分隔符、整周期报告范围——而不必先因调用出错才发现这些规则。
机器可读的 MCP 注解(readOnlyHint、destructiveHint)支持注解的宿主(包括 Claude)可以自动信任 98 个只读工具,并在执行 44 个会删除内容或无法撤销的工具前要求确认。
每份报告使用正确的参数四份报告各有自己的架构:资产负债表使用 date + period_count;损益表和现金流量表使用 date_after + date_before;试算平衡表使用 from_date + to_date。Wafeq 会静默忽略拼写错误的查询参数,因此错误的参数名称看起来就像调用成功了一样。
发送前验证完整周期损益表和现金流量表会拒绝与完整月份或年份不匹配的范围。服务端会在本地检查,并返回最接近的有效范围,而不是花费一次往返请求才收到 HTTP 400。
自动生成幂等键支持 X-Wafeq-Idempotency-Key 的 146 个写入端点都会自动获得一个 UUID v4,并在重试时复用,因此网络中断绝不会导致发票重复。你也可以提供自己的键,让有意重跑操作同样安全。
真正可用的文件上传POST /files/ 仅支持 multipart,POST /files/raw/ 需要 Content-Disposition 标头。两种方式都已处理;你只需传入 base64 内容和文件名。
将二进制 PDF 作为字节处理九个 PDF 端点会将内容编码为 base64,并放入包含大小和内容类型的小型封装中,而不是将其当作文本读取并导致损坏。
自动重试并采用退避策略对暂时性的 429 / 5xx 响应进行重试,采用带抖动的指数退避,并遵循 Retry-After——使用相同的幂等键,完全符合 Wafeq 集成指南的要求。
内置速率限制自动限制速率,避免工具调用突发导致触发 429。Wafeq 未公布具体数值限制,因此默认设置刻意保守,并且可以配置。
启动时验证租户Wafeq API 密钥的作用范围为组织。服务会在开始提供服务前调用 GET /organization/,并在 /health 上公布结果,因此密钥配置错误时,你可以通过名称检查出来,而不是让写入操作落到错误公司的账目中。
两种传输方式:stdio 和 Streamable HTTP可在本地的 Claude Desktop 中使用,也可运行一个始终在线的服务,让任意数量的 MCP 客户端通过 HTTP 连接。
Docker + docker-compose、健康检查和自动重启运行 docker compose up 即可启动并保持运行,且仅绑定到 localhost。
HTTP 端点支持可选的 bearer token 身份验证服务一旦可以从 localhost 以外访问,就用共享密钥保护它。
你的密钥绝不会传给模型凭据保存在服务端的环境中,并在每次请求时注入。透传工具无法覆盖 Authorization,也无法将凭据指向其他主机。
可直接替换的规范更新Wafeq 发布了新版规范?替换一个文件并重新构建即可——新端点会自动成为新工具,无需修改代码。
对比
功能本项目通用 OpenAPI→MCP 封装器*
将全部 251 个 Wafeq 端点作为工具✅✅
每个工具都有安全类别和横幅提示✅❌
将向税务机关提交申报标记为不可逆操作,而非“创建”✅❌
MCP 注解 readOnlyHint / destructiveHint✅➖
从创建/更新请求体中剔除只读字段✅❌
从架构中精简重复的枚举说明✅❌
每种报表均使用正确的日期参数✅✅
发送前验证整个期间范围✅❌
自动生成 X-Wafeq-Idempotency-Key,并在重试期间保持不变✅❌
支持 multipart 和原始二进制文件上传✅➖
将二进制 PDF 响应编码为 base64,而非错误处理✅➖
恢复日记账分录行项目的交易日期✅❌
在 429 / 5xx 时自动重试(遵循 Retry-After)✅❌
客户端限流✅❌
启动时验证组织身份✅❌
stdio 传输✅✅
Streamable-HTTP 传输✅➖
Docker + docker-compose、健康检查、自动重启✅❌
端点上的可选 bearer-token 身份验证✅❌
许可证MIT因工具而异

*通用 OpenAPI→MCP 封装器可将任何规范转换为 MCP 工具。它们可以访问相同的端点,但会将所有操作一视同仁——具体到 Wafeq 的规范,它们还会遇到 MIGRATION.md 中所述的只读必填字段问题。“➖” = 因工具而异 / 不保证。

你可以做什么

连接后,可以向助手提出以下请求:

  • “今年上半年的损益是多少?”
  • “列出所有逾期超过 30 天的未付款发票,并附上客户名称。”
  • “为 Acme Ltd 创建一张咨询服务草稿发票,服务 3 天,每天 €800。”
  • “将发票 INV-2026-014 下载为 PDF。”
  • “1 月的 €7,000 转账记入了哪个账户?”
  • “将这张收据附加到费用 EXP-118。”
  • “将 3 月的银行对账单行与分类账进行核对。”
  • “将报价 QUO-31 转为发票并记录付款。”

工作原理

Claude / Cursor / any MCP client  ──MCP──►  this server  ──HTTPS──►  Wafeq API (your organization)

启动时,服务会将捆绑的 OpenAPI 规范解析为 MCP 工具——解析 $ref、防止递归架构,并从请求体中剔除由服务端分配的(readOnly)字段——为每个工具添加安全类别,验证凭据所属的 Wafeq 组织,然后在每个发出的请求中注入你的凭据。你的密钥保存在服务的环境中;模型不会看到或处理它。

要求

  • 具有 API 访问权限的 Wafeq 组织——可以使用私有API 密钥(Wafeq → 设置 → 开发者 → API 密钥)或 OAuth2 访问令牌。请参阅获取 API 凭据。
  • 以下快速入门需要 Docker(macOS/Windows 上使用 Docker Desktop)——或者需要 Node.js ≥ 20 才能从源代码运行。

快速开始(Docker)

1. 添加凭据。 复制示例配置并填写:

cp .env.example .env

然后编辑 .env 并设置 WAFEQ_API_KEY。除非 HOST 是环回地址,否则服务还需要 MCP_AUTH_TOKEN,且没有该令牌时会拒绝启动,因此请将其设为较长的随机字符串:MCP_AUTH_TOKEN=$(openssl rand -hex 32)。

2. 启动服务:

docker compose up -d --build

docker-compose.yml 仅绑定到 127.0.0.1:8765,因此你的计算机可以访问该服务,但网络中的其他设备无法访问。

3. 确认服务正在运行,并且指向了正确的账簿:

curl -s http://localhost:8765/health
{
  "status": "ok",
  "server": "wafeq-mcp",
  "version": "2.0.0",
  "tools": 253,
  "organization": {
    "status": "ok",
    "id": "org_...",
    "name": "Your Company FZCO",
    "base_currency": "EUR",
    "country": "AE"
  },
  "auth_required": false
}

检查 name 字段。 该字段表示密钥会写入数据的组织。如果不是你预期的公司,请先停止操作并修正密钥。凭据无法验证时,/health 会返回 503 和 "status": "degraded"。

4. 将 MCP 客户端指向该服务: http://localhost:8765/mcp(Streamable HTTP)。

远程端点可作为自定义连接器添加到 Claude(设置 → 连接器),也可以通过 mcp-remote 在本地桥接。使用桥接时,请将以下内容添加到客户端配置中的 mcpServers 下,然后完全重启应用:

{
  "mcpServers": {
    "wafeq": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://localhost:8765/mcp",
        "--header", "Authorization: Bearer YOUR_MCP_AUTH_TOKEN"
      ]
    }
  }
}

(只有在服务未使用令牌运行时,才可以删除 --header 这一行;服务仅绑定到环回地址时允许不使用令牌。)

想要直接使用现成的镜像?

每个版本都会向 GitHub Container Registry 发布可直接运行的镜像,因此你可以完全跳过本地构建:

docker run -d --name wafeq-mcp -p 127.0.0.1:8765:8765 --env-file .env \
  ghcr.io/ohneben/wafeq-mcp:latest

如果希望自行选择升级到哪个版本,请固定版本(:2.0.0),而不要使用 latest。

从 MCP Registry 安装

该服务已作为 io.github.ohneben/wafeq-mcp 发布到 MCP Registry,因此支持 Registry 的客户端可以按名称安装。Registry 条目会通过 stdio 启动镜像——等效的手写配置请参阅通过 stdio 运行容器。

curl -s "https://registry.modelcontextprotocol.io/v0.1/servers/io.github.ohneben%2Fwafeq-mcp/versions/latest"

获取 API 凭据

私有 API 密钥(适用于大多数人): 在 Wafeq 中,前往设置 → 开发者 → API 密钥并创建密钥。该密钥仅适用于一个组织。将其放入 .env 并设为 WAFEQ_API_KEY;服务会以 Authorization: Api-Key <key> 的形式发送该密钥。

OAuth2 应用: 如果你有 Wafeq OAuth2 应用的访问令牌,请将其放入 .env 并设为 WAFEQ_ACCESS_TOKEN。服务会自动切换为 Authorization: Bearer <token>。仅当两个变量同时存在且你需要强制使用其中一种方案时,才设置 WAFEQ_AUTH_SCHEME。

配置

所有配置项均为环境变量。除了凭据外,其他配置都有可用的默认值。

变量默认值功能
WAFEQ_API_KEY—组织专用 API 密钥。以 Api-Key <key> 的形式发送。必须提供一种凭据。
WAFEQ_ACCESS_TOKEN—OAuth2 访问令牌。以 Bearer <token> 的形式发送。优先级高于 WAFEQ_API_KEY。
WAFEQ_AUTH_SCHEMEauto强制使用 api-key 或 bearer。通常留空即可。
WAFEQ_API_BASE_URLhttps://api.wafeq.com/v1Wafeq API 基础 URL。
WAFEQ_OPENAPI_PATH内置规范使用其他 OpenAPI 文档(JSON 或 YAML)。
MCP_TRANSPORTstdiostdio 或 http。Docker 会设置为 http。
PORT8765HTTP 监听端口。
HOST0.0.0.0HTTP 绑定地址。
MCP_HTTP_PATH/mcpMCP 端点提供服务的路径。
MCP_AUTH_TOKEN—访问 /mcp 时必须提供的 Bearer 令牌。除非 HOST 是环回地址,否则必须设置;否则服务会拒绝启动。此变量在 2.1.0 中从 MCP_SHARED_TOKEN 更名而来。
MCP_ALLOWED_HOSTS—允许 Host 标头携带的主机名,以逗号分隔。留空时由服务自行确定:绑定到环回地址时使用环回主机名;位于反向代理后时不进行检查。
MCP_ALLOW_INSECURE0在非环回地址上不使用令牌启动。仅适用于确实无法被其他人访问的端口。
MCP_BODY_LIMIT25mb允许的最大请求正文大小。
MCP_SESSION_TTL1800空闲会话在清理前保留的秒数。
MCP_MAX_SESSIONS256并发会话上限;达到上限后,会淘汰最近最少使用的会话。
WAFEQ_TOOL_GROUPS—要公开的资源组,以逗号分隔,例如 invoices,bills,reports。留空 = 全部 251 项。运行 npm run list-tools 查看列表。
WAFEQ_MAX_REQUESTS20客户端速率限制:每个时间窗口内的请求数。设为 0 可禁用限流。
WAFEQ_RATE_WINDOW_MS10000速率限制窗口,单位为毫秒。
WAFEQ_MAX_RETRIES3遇到 429 / 5xx / 网络错误时的重试次数。
WAFEQ_TIMEOUT_MS30000单次请求尝试的超时时间。
WAFEQ_ALLOW_LOCAL_FILE_UPLOADfalse允许上传工具通过 file_path 读取此计算机的文件系统。参见安全性。
WAFEQ_MAX_UPLOAD_BYTES26214400解码后的最大上传大小(25 MiB)。
工具太多?

251 个工具数量不少。完整目录在 tools/list 中约占 0.5 MB JSON(约 133k 个令牌),某些宿主在工具数量如此之多时会变慢或准确性下降。以下两种方法会有所帮助。

这些架构已压缩。 Wafeq 的规范会将每个枚举的值同时写入其描述和 enum 中——仅货币列表就约有 4 KB,并在 203 处内联。生成器会合并这些仅包含单个成员的 allOf 包装器,并移除重复的项目符号列表,在不删减任何允许值的前提下将载荷缩小约 29%。

缩小目录范围,让目录进一步精简——无需修改代码:

WAFEQ_TOOL_GROUPS=invoices,bills,contacts,payments,reports,accounts,items,tax-rates

两个手写工具始终可用,因此不会导致任何功能无法访问——任何被筛除的功能仍可通过 wafeq_request 调用。

工具安全类别

每个工具的描述开头都有一个横幅,每个工具也都带有相应的 MCP 注释。数量基于内置规范计算(251 个生成的工具 + 2 个手写工具 = 253 个)。

标识工具readOnlyHintdestructiveHint涵盖内容
🟢 READ-ONLY85✅❌所有 GET,以及账户分类账便捷工具。
🟢 READ-ONLY · 返回 PDF9✅❌PDF 下载:发票、简化发票、贷项通知单、借项通知单、账单、报价单、采购订单、付款、工资单。以 base64 编码返回。
🟢 READ-ONLY · 预览 / 模拟4✅❌摊销和收入确认预览。使用 POST,但文档说明不会写入任何内容。
🟡 WRITE · 创建数据39❌❌集合创建、两种文件上传,以及两种转换(报价单→发票、采购订单→账单)。这些操作本质上不具幂等性,因此会自动添加幂等键。
🟡 WRITE · 更新数据70❌❌所有 PUT 和 PATCH。
🟠 STATE CHANGE · 将文档移入或移出分类账2❌❌将费用标记为已过账 / 草稿。可撤销——每个操作都能撤销另一个操作。
🔴 IRREVERSIBLE · 向外部税务机关提交文档3❌✅向税务机关申报发票 / 贷项通知单 / 简化发票。文档会离开你的组织,且无法撤回。
🔴 IRREVERSIBLE · 将剩余余额过账到分类账2❌✅提前结束摊销 / 收入确认。API 不支持撤销——请先运行对应的预览。
🔴 DESTRUCTIVE · 删除39❌✅所有 DELETE,以及 wafeq_request 透传工具(其影响无法预先得知)。
2539844

三个 🔴 分组都设置了 destructiveHint: true,因此会遵循注解的宿主会在执行其中任何操作之前暂停并征求确认——而不只是删除操作。向税务机关提交发票至少与删除发票一样影响重大,而且不同于删除,它会影响你的组织之外。

无需凭据,随时打印实时目录:

npm run list-tools
🟢 只读(85) | Tool | Endpoint | |---|---| | `wafeq_account_ledger` | _hand-written_ | | `wafeq_accounts_list` | `GET /accounts/` | | `wafeq_accounts_retrieve` | `GET /accounts/{id}/` | | `wafeq_amortizations_list` | `GET /amortizations/` | | `wafeq_amortizations_retrieve` | `GET /amortizations/{id}/` | | `wafeq_bank_accounts_ledger_transactions_list` | `GET /bank-accounts/{bank_account_id}/ledger-transactions/` | | `wafeq_bank_accounts_ledger_transactions_retrieve` | `GET /bank-accounts/{bank_account_id}/ledger-transactions/{id}/` | | `wafeq_bank_accounts_list` | `GET /bank-accounts/` | | `wafeq_bank_accounts_retrieve` | `GET /bank-accounts/{id}/` | | `wafeq_bank_accounts_statement_transactions_list` | `GET /bank-accounts/{bank_account_id}/statement-transactions/` | | `wafeq_bank_accounts_statement_transactions_retrieve` | `GET /bank-accounts/{bank_account_id}/statement-transactions/{id}/` | | `wafeq_beneficiaries_list` | `GET /beneficiaries/` | | `wafeq_beneficiaries_retrieve` | `GET /beneficiaries/{id}/` | | `wafeq_bills_line_items_list` | `GET /bills/{bill_id}/line-items/` | | `wafeq_bills_line_items_retrieve` | `GET /bills/{bill_id}/line-items/{id}/` | | `wafeq_bills_list` | `GET /bills/` | | `wafeq_bills_retrieve` | `GET /bills/{id}/` | | `wafeq_branches_list` | `GET /branches/` | | `wafeq_branches_retrieve` | `GET /branches/{id}/` | | `wafeq_contacts_list` | `GET /contacts/` | | `wafeq_contacts_retrieve` | `GET /contacts/{id}/` | | `wafeq_cost_centers_list` | `GET /cost-centers/` | | `wafeq_cost_centers_retrieve` | `GET /cost-centers/{id}/` | | `wafeq_credit_notes_line_items_list` | `GET /credit-notes/{credit_note_id}/line-items/` | | `wafeq_credit_notes_line_items_retrieve` | `GET /credit-notes/{credit_note_id}/line-items/{id}/` | | `wafeq_credit_notes_list` | `GET /credit-notes/` | | `wafeq_credit_notes_retrieve` | `GET /credit-notes/{id}/` | | `wafeq_custom_fields_list` | `GET /custom-fields/` | | `wafeq_custom_fields_retrieve` | `GET /custom-fields/{id}/` | | `wafeq_debit_notes_line_items_list` | `GET /debit-notes/{debit_note_id}/line-items/` | | `wafeq_debit_notes_line_items_retrieve` | `GET /debit-notes/{debit_note_id}/line-items/{id}/` | | `wafeq_debit_notes_list` | `GET /debit-notes/` | | `wafeq_debit_notes_retrieve` | `GET /debit-notes/{id}/` | | `wafeq_employees_list` | `GET /employees/` | | `wafeq_employees_retrieve` | `GET /employees/{id}/` | | `wafeq_expenses_list` | `GET /expenses/` | | `wafeq_expenses_retrieve` | `GET /expenses/{id}/` | | `wafeq_files_list` | `GET /files/` | | `wafeq_files_retrieve` | `GET /files/{id}/` | | `wafeq_invoices_line_items_list` | `GET /invoices/{invoice_id}/line-items/` | | `wafeq_invoices_line_items_retrieve` | `GET /invoices/{invoice_id}/line-items/{id}/` | | `wafeq_invoices_list` | `GET /invoices/` | | `wafeq_invoices_retrieve` | `GET /invoices/{id}/` | | `wafeq_item_units_of_measure_list` | `GET /item-units-of-measure/` | | `wafeq_item_units_of_measure_retrieve` | `GET /item-units-of-measure/{id}/` | | `wafeq_items_list` | `GET /items/` | | `wafeq_items_retrieve` | `GET /items/{id}/` | | `wafeq_journal_line_items_list` | `GET /journal-line-items/` | | `wafeq_journal_line_items_retrieve` | `GET /journal-line-items/{id}/` | | `wafeq_manual_journals_list` | `GET /manual-journals/` | | `wafeq_manual_journals_retrieve` | `GET /manual-journals/{id}/` | | `wafeq_organization_retrieve` | `GET /organization/` | | `wafeq_payment_requests_list` | `GET /payment_requests/` | | `wafeq_payment_requests_retrieve` | `GET /payment_requests/{id}/` | | `wafeq_payments_list` | `GET /payments/` | | `wafeq_payments_retrieve` | `GET /payments/{id}/` | | `wafeq_payslips_list` | `GET /payslips/` | | `wafeq_payslips_pay_items_list` | `GET /payslips/{payslip_id}/pay-items/` | | `wafeq_payslips_pay_items_retrieve` | `GET /payslips/{payslip_id}/pay-items/{id}/` | | `wafeq_payslips_retrieve` | `GET /payslips/{id}/` | | `wafeq_projects_list` | `GET /projects/` | | `wafeq_projects_retrieve` | `GET /projects/{id}/` | | `wafeq_purchase_orders_line_items_list` | `GET /purchase-orders/{purchase_order_id}/line-items/` | | `wafeq_purchase_orders_line_items_retrieve` | `GET /purchase-orders/{purchase_order_id}/line-items/{id}/` | | `wafeq_purchase_orders_list` | `GET /purchase-orders/` | | `wafeq_purchase_orders_retrieve` | `GET /purchase-orders/{id}/` | | `wafeq_quotes_line_items_list` | `GET /quotes/{quote_id}/line-items/` | | `wafeq_quotes_line_items_retrieve` | `GET /quotes/{quote_id}/line-items/{id}/` | | `wafeq_quotes_list` | `GET /quotes/` | | `wafeq_quotes_retrieve` | `GET /quotes/{id}/` | | `wafeq_reports_balance_sheet_list` | `GET /reports/balance-sheet/` | | `wafeq_reports_cash_flow_list` | `GET /reports/cash-flow/` | | `wafeq_reports_profit_and_loss_list` | `GET /reports/profit-and-loss/` | | `wafeq_reports_trial_balance_list` | `GET /reports/trial-balance/` | | `wafeq_revenue_recognitions_list` | `GET /revenue-recognitions/` | | `wafeq_revenue_recognitions_retrieve` | `GET /revenue-recognitions/{id}/` | | `wafeq_simplified_invoices_line_items_list` | `GET /simplified-invoices/{invoice_id}/line-items/` | | `wafeq_simplified_invoices_line_items_retrieve` | `GET /simplified-invoices/{invoice_id}/line-items/{id}/` | | `wafeq_simplified_invoices_list` | `GET /simplified-invoices/` | | `wafeq_simplified_invoices_retrieve` | `GET /simplified-invoices/{id}/` | | `wafeq_tax_rates_list` | `GET /tax-rates/` | | `wafeq_units_of_measure_list` | `GET /units-of-measure/` | | `wafeq_units_of_measure_retrieve` | `GET /units-of-measure/{id}/` | | `wafeq_warehouses_list` | `GET /warehouses/` | | `wafeq_warehouses_retrieve` | `GET /warehouses/{id}/` |
🟢 READ-ONLY (PDF) (9)
ToolEndpoint
wafeq_bills_download_retrieveGET /bills/{id}/download/
wafeq_credit_notes_download_retrieveGET /credit-notes/{id}/download/
wafeq_debit_notes_download_retrieveGET /debit-notes/{id}/download/
wafeq_invoices_download_retrieveGET /invoices/{id}/download/
wafeq_payments_download_retrieveGET /payments/{id}/download/
wafeq_payslips_download_retrieveGET /payslips/{id}/download/
wafeq_purchase_orders_download_retrieveGET /purchase-orders/{id}/download/
wafeq_quotes_download_retrieveGET /quotes/{id}/download/
wafeq_simplified_invoices_download_retrieveGET /simplified-invoices/{id}/download/
🟢 READ-ONLY (PREVIEW) (4)
ToolEndpoint
wafeq_amortizations_preview_createPOST /amortizations/preview/
wafeq_amortizations_preview_end_early_createPOST /amortizations/{id}/preview-end-early/
wafeq_revenue_recognitions_preview_createPOST /revenue-recognitions/preview/
wafeq_revenue_recognitions_preview_end_early_createPOST /revenue-recognitions/{id}/preview-end-early/
🟡 WRITE · CREATES (39)
ToolEndpoint
wafeq_accounts_createPOST /accounts/
wafeq_bank_accounts_createPOST /bank-accounts/
wafeq_bank_accounts_ledger_transactions_createPOST /bank-accounts/{bank_account_id}/ledger-transactions/
wafeq_bank_accounts_statement_transactions_createPOST /bank-accounts/{bank_account_id}/statement-transactions/
wafeq_beneficiaries_createPOST /beneficiaries/
wafeq_bills_createPOST /bills/
wafeq_bills_line_items_createPOST /bills/{bill_id}/line-items/
wafeq_branches_createPOST /branches/
wafeq_contacts_createPOST /contacts/
wafeq_cost_centers_createPOST /cost-centers/
wafeq_credit_notes_createPOST /credit-notes/
wafeq_credit_notes_line_items_createPOST /credit-notes/{credit_note_id}/line-items/
wafeq_custom_fields_createPOST /custom-fields/
wafeq_debit_notes_createPOST /debit-notes/
wafeq_debit_notes_line_items_createPOST /debit-notes/{debit_note_id}/line-items/
wafeq_employees_createPOST /employees/
wafeq_expenses_createPOST /expenses/
wafeq_invoices_createPOST /invoices/
wafeq_invoices_line_items_createPOST /invoices/{invoice_id}/line-items/
wafeq_item_units_of_measure_createPOST /item-units-of-measure/
wafeq_items_createPOST /items/
wafeq_manual_journals_createPOST /manual-journals/
wafeq_payment_requests_createPOST /payment_requests/
wafeq_payments_createPOST /payments/
wafeq_payslips_createPOST /payslips/
wafeq_payslips_pay_items_createPOST /payslips/{payslip_id}/pay-items/
wafeq_projects_createPOST /projects/
wafeq_purchase_orders_bill_createPOST /purchase-orders/{id}/bill/
wafeq_purchase_orders_createPOST /purchase-orders/
wafeq_purchase_orders_line_items_createPOST /purchase-orders/{purchase_order_id}/line-items/
wafeq_quotes_createPOST /quotes/
wafeq_quotes_invoice_createPOST /quotes/{id}/invoice/
wafeq_quotes_line_items_createPOST /quotes/{quote_id}/line-items/
wafeq_simplified_invoices_createPOST /simplified-invoices/
wafeq_simplified_invoices_line_items_createPOST /simplified-invoices/{invoice_id}/line-items/
wafeq_units_of_measure_createPOST /units-of-measure/
wafeq_upload_filePOST /files/
wafeq_upload_file_rawPOST /files/raw/
wafeq_warehouses_createPOST /warehouses/
🟡 WRITE · UPDATES (70)
ToolEndpoint
wafeq_accounts_partial_updatePATCH /accounts/{id}/
wafeq_accounts_updatePUT /accounts/{id}/
wafeq_bank_accounts_ledger_transactions_partial_updatePATCH /bank-accounts/{bank_account_id}/ledger-transactions/{id}/
wafeq_bank_accounts_ledger_transactions_updatePUT /bank-accounts/{bank_account_id}/ledger-transactions/{id}/
wafeq_bank_accounts_partial_updatePATCH /bank-accounts/{id}/
wafeq_bank_accounts_statement_transactions_partial_updatePATCH /bank-accounts/{bank_account_id}/statement-transactions/{id}/
wafeq_bank_accounts_statement_transactions_updatePUT /bank-accounts/{bank_account_id}/statement-transactions/{id}/
wafeq_bank_accounts_updatePUT /bank-accounts/{id}/
wafeq_beneficiaries_partial_updatePATCH /beneficiaries/{id}/
wafeq_beneficiaries_updatePUT /beneficiaries/{id}/
wafeq_bills_line_items_partial_updatePATCH /bills/{bill_id}/line-items/{id}/
wafeq_bills_line_items_updatePUT /bills/{bill_id}/line-items/{id}/
wafeq_bills_partial_updatePATCH /bills/{id}/
wafeq_bills_updatePUT /bills/{id}/
wafeq_branches_partial_updatePATCH /branches/{id}/
wafeq_branches_updatePUT /branches/{id}/
wafeq_contacts_partial_updatePATCH /contacts/{id}/
wafeq_contacts_updatePUT /contacts/{id}/
wafeq_cost_centers_partial_updatePATCH /cost-centers/{id}/
wafeq_cost_centers_updatePUT /cost-centers/{id}/
wafeq_credit_notes_line_items_partial_updatePATCH /credit-notes/{credit_note_id}/line-items/{id}/
wafeq_credit_notes_line_items_updatePUT /credit-notes/{credit_note_id}/line-items/{id}/
wafeq_credit_notes_partial_updatePATCH /credit-notes/{id}/
wafeq_credit_notes_updatePUT /credit-notes/{id}/
wafeq_custom_fields_partial_updatePATCH /custom-fields/{id}/
wafeq_custom_fields_updatePUT /custom-fields/{id}/
wafeq_debit_notes_line_items_partial_updatePATCH /debit-notes/{debit_note_id}/line-items/{id}/
wafeq_debit_notes_line_items_updatePUT /debit-notes/{debit_note_id}/line-items/{id}/
wafeq_debit_notes_partial_updatePATCH /debit-notes/{id}/
wafeq_debit_notes_updatePUT /debit-notes/{id}/
wafeq_employees_partial_updatePATCH /employees/{id}/
wafeq_employees_updatePUT /employees/{id}/
wafeq_expenses_partial_updatePATCH /expenses/{id}/
wafeq_expenses_updatePUT /expenses/{id}/
wafeq_invoices_line_items_partial_updatePATCH /invoices/{invoice_id}/line-items/{id}/
wafeq_invoices_line_items_updatePUT /invoices/{invoice_id}/line-items/{id}/
wafeq_invoices_partial_updatePATCH /invoices/{id}/
wafeq_invoices_updatePUT /invoices/{id}/
wafeq_item_units_of_measure_partial_updatePATCH /item-units-of-measure/{id}/
wafeq_item_units_of_measure_updatePUT /item-units-of-measure/{id}/
wafeq_items_partial_updatePATCH /items/{id}/
wafeq_items_updatePUT /items/{id}/
wafeq_manual_journals_partial_updatePATCH /manual-journals/{id}/
wafeq_manual_journals_updatePUT /manual-journals/{id}/
wafeq_payment_requests_partial_updatePATCH /payment_requests/{id}/
wafeq_payment_requests_updatePUT /payment_requests/{id}/
wafeq_payments_partial_updatePATCH /payments/{id}/
wafeq_payments_updatePUT /payments/{id}/
wafeq_payslips_partial_updatePATCH /payslips/{id}/
wafeq_payslips_pay_items_partial_updatePATCH /payslips/{payslip_id}/pay-items/{id}/
wafeq_payslips_pay_items_updatePUT /payslips/{payslip_id}/pay-items/{id}/
wafeq_payslips_updatePUT /payslips/{id}/
wafeq_projects_partial_updatePATCH /projects/{id}/
wafeq_projects_updatePUT /projects/{id}/
wafeq_purchase_orders_line_items_partial_updatePATCH /purchase-orders/{purchase_order_id}/line-items/{id}/
wafeq_purchase_orders_line_items_updatePUT /purchase-orders/{purchase_order_id}/line-items/{id}/
wafeq_purchase_orders_partial_updatePATCH /purchase-orders/{id}/
wafeq_purchase_orders_updatePUT /purchase-orders/{id}/
wafeq_quotes_line_items_partial_updatePATCH /quotes/{quote_id}/line-items/{id}/
wafeq_quotes_line_items_updatePUT /quotes/{quote_id}/line-items/{id}/
wafeq_quotes_partial_updatePATCH /quotes/{id}/
wafeq_quotes_updatePUT /quotes/{id}/
wafeq_simplified_invoices_line_items_partial_updatePATCH /simplified-invoices/{invoice_id}/line-items/{id}/
wafeq_simplified_invoices_line_items_updatePUT /simplified-invoices/{invoice_id}/line-items/{id}/
wafeq_simplified_invoices_partial_updatePATCH /simplified-invoices/{id}/
wafeq_simplified_invoices_updatePUT /simplified-invoices/{id}/
wafeq_units_of_measure_partial_updatePATCH /units-of-measure/{id}/
wafeq_units_of_measure_updatePUT /units-of-measure/{id}/
wafeq_warehouses_partial_updatePATCH /warehouses/{id}/
wafeq_warehouses_updatePUT /warehouses/{id}/
🟠 STATE CHANGE (2)
ToolEndpoint
wafeq_expenses_mark_as_draft_createPOST /expenses/{id}/mark-as-draft/
wafeq_expenses_mark_as_posted_createPOST /expenses/{id}/mark-as-posted/
🔴 IRREVERSIBLE · EXTERNAL FILING (3)
ToolEndpoint
wafeq_credit_notes_tax_authority_report_createPOST /credit-notes/{id}/tax-authority/report/
wafeq_invoices_tax_authority_report_createPOST /invoices/{id}/tax-authority/report/
wafeq_simplified_invoices_tax_authority_report_createPOST /simplified-invoices/{id}/tax-authority/report/
🔴 IRREVERSIBLE · LEDGER (2)
ToolEndpoint
wafeq_amortizations_end_early_createPOST /amortizations/{id}/end-early/
wafeq_revenue_recognitions_end_early_createPOST /revenue-recognitions/{id}/end-early/
🔴 DESTRUCTIVE · DELETES (39)
ToolEndpoint
wafeq_accounts_destroyDELETE /accounts/{id}/
wafeq_amortizations_destroyDELETE /amortizations/{id}/
wafeq_bank_accounts_destroyDELETE /bank-accounts/{id}/
wafeq_bank_accounts_ledger_transactions_destroyDELETE /bank-accounts/{bank_account_id}/ledger-transactions/{id}/
wafeq_bank_accounts_statement_transactions_destroyDELETE /bank-accounts/{bank_account_id}/statement-transactions/{id}/
wafeq_beneficiaries_destroyDELETE /beneficiaries/{id}/
wafeq_bills_destroyDELETE /bills/{id}/
wafeq_bills_line_items_destroyDELETE /bills/{bill_id}/line-items/{id}/
wafeq_branches_destroyDELETE /branches/{id}/
wafeq_contacts_destroyDELETE /contacts/{id}/
wafeq_cost_centers_destroyDELETE /cost-centers/{id}/
wafeq_credit_notes_destroyDELETE /credit-notes/{id}/
wafeq_credit_notes_line_items_destroyDELETE /credit-notes/{credit_note_id}/line-items/{id}/
wafeq_custom_fields_destroyDELETE /custom-fields/{id}/
wafeq_debit_notes_destroyDELETE /debit-notes/{id}/
wafeq_debit_notes_line_items_destroyDELETE /debit-notes/{debit_note_id}/line-items/{id}/
wafeq_employees_destroyDELETE /employees/{id}/
wafeq_expenses_destroyDELETE /expenses/{id}/
wafeq_files_destroyDELETE /files/{id}/
wafeq_invoices_destroyDELETE /invoices/{id}/
wafeq_invoices_line_items_destroyDELETE /invoices/{invoice_id}/line-items/{id}/
wafeq_item_units_of_measure_destroyDELETE /item-units-of-measure/{id}/
wafeq_items_destroyDELETE /items/{id}/
wafeq_manual_journals_destroyDELETE /manual-journals/{id}/
wafeq_payment_requests_destroyDELETE /payment_requests/{id}/
wafeq_payments_destroyDELETE /payments/{id}/
wafeq_payslips_destroyDELETE /payslips/{id}/
wafeq_payslips_pay_items_destroyDELETE /payslips/{payslip_id}/pay-items/{id}/
wafeq_projects_destroyDELETE /projects/{id}/
wafeq_purchase_orders_destroyDELETE /purchase-orders/{id}/
wafeq_purchase_orders_line_items_destroyDELETE /purchase-orders/{purchase_order_id}/line-items/{id}/
wafeq_quotes_destroyDELETE /quotes/{id}/
wafeq_quotes_line_items_destroyDELETE /quotes/{quote_id}/line-items/{id}/
wafeq_requesthand-written
wafeq_revenue_recognitions_destroyDELETE /revenue-recognitions/{id}/
wafeq_simplified_invoices_destroyDELETE /simplified-invoices/{id}/
wafeq_simplified_invoices_line_items_destroyDELETE /simplified-invoices/{invoice_id}/line-items/{id}/
wafeq_units_of_measure_destroyDELETE /units-of-measure/{id}/
wafeq_warehouses_destroyDELETE /warehouses/{id}/

覆盖范围

范围工具🟢 读取🟡 写入🔴 不可逆🔴 删除
销售与应收款692531310
采购与应付款54192708
银行业务186903
总账与报告3119624
薪资197903
主数据与维度54182709
文件与组织63201
备用接口与便捷工具21001
总计25398111539

“写入”包括两个 🟠 状态变更工具。各范围对应的 Wafeq 资源如下——销售:发票、简化发票、报价、贷项通知单、付款、付款请求 · 采购:账单、采购订单、借项通知单、费用、受益人 · 银行业务:银行账户及其总账和对账单交易 · 总账与报告:科目、手动日记账、日记账分录行、四种报告、税率、摊销、收入确认 · 薪资:工资单、员工 · 主数据:联系人、商品、计量单位、仓库、项目、成本中心、分支机构、自定义字段。

两个手写工具

以上内容均为生成所得。以下两个工具为手写:

  • wafeq_account_ledger(🟢)——日记账分录行及其真实交易日期。Wafeq 的 /journal-line-items/ 行包含 created_ts(该行进入 Wafeq 的时间),这个时间通常与交易日期不在同一个月,而且完全没有日期字段。此工具利用端点自身的 date_after/date_before 筛选器还原日期;这些筛选器确实按交易日期筛选。工具按月探测,仅在存在行时才细分到按日查询,因此数据较少的时段每月只需一次请求;结果会报告 requests_made。
  • wafeq_request(🔴)——备用接口:支持任意方法、任意路径,以及查询参数、请求正文和请求头。对于打包规范未涵盖的内容,请使用此工具作为后备方案,而非主要接口。由于无法预知其影响,因此归类为破坏性工具。

从源代码运行(stdio,无需 Docker)

npm ci
npm run build

然后在 MCP 客户端中注册该服务。对于 Claude Desktop,将其添加到 claude_desktop_config.json:

{
  "mcpServers": {
    "wafeq": {
      "command": "node",
      "args": ["/absolute/path/to/Wafeq MCP/dist/index.js"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "WAFEQ_API_KEY": "your-key-here"
      }
    }
  }
}

对于 Claude Code:

claude mcp add wafeq --env WAFEQ_API_KEY=your-key-here -- node /absolute/path/to/dist/index.js
通过 stdio 运行容器

你也可以让客户端直接启动已发布的镜像,无需 HTTP 服务,也无需本地构建:

{
  "mcpServers": {
    "wafeq": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT=stdio",
        "-e", "WAFEQ_API_KEY",
        "ghcr.io/ohneben/wafeq-mcp:latest"
      ],
      "env": {
        "WAFEQ_API_KEY": "your-key-here"
      }
    }
  }
}

此处必须设置 MCP_TRANSPORT=stdio:镜像默认使用 HTTP 传输。

实用脚本:

命令功能
npm run build将 TypeScript 编译到 dist/。
npm test运行 Vitest 测试套件。
npm run list-tools输出分类目录。无需凭据。
npm run start:stdio通过 stdio 运行。
npm run start:http运行 Streamable HTTP 服务。

保持规范更新

工具在启动时根据 spec/wafeq-public-api.json 生成——无需代码生成步骤,也没有手写工具列表。放入较新的 OpenAPI 文档(JSON 或 YAML),重新构建后,新的端点就会成为新工具。有关打包副本的来源以及更新后需要重新检查的内容,请参阅 spec/README.md。

针对已观察到行为的修正位于 src/overrides.ts 中,按 operationId 编目并注明日期,因此当某个条目对应的操作消失时,该条目便不再生效。

备注与约定

  • 日期使用 YYYY-MM-DD 格式。金额使用点号作为小数分隔符。
  • 分页:列表工具接受 limit 和 offset,并报告总数。
  • 报告接受各自专用的日期参数——资产负债表使用 date + period_count;损益表和现金流量表使用 date_after + date_before;试算表使用 from_date + to_date。Wafeq 会静默忽略拼写错误的查询参数,因此错误的名称看起来也像是调用成功;各报告专用的架构可以避免这种情况。
  • 完整期间:损益表和现金流量表的日期范围必须涵盖完整的月份或年份。服务会在本地检查,并回复最近的有效范围,而不是花费一次 HTTP 400 往返。
  • 文件上传(wafeq_files_*):传入 base64 内容和文件名。 POST /files/ 仅支持 multipart,POST /files/raw/ 需要 Content-Disposition 标头——这些都会自动为你处理。
  • PDF 下载会以一个包含大小和内容类型的小型封装返回 base64 编码内容,而不会变成乱码文本。
  • 幂等性:每个支持 X-Wafeq-Idempotency-Key 的写入端点都会自动获得一个 UUID v4,并在重试时重复使用。传入你自己的 UUID,即可安全地有意重新运行。
  • 重试:对于临时的 429 / 5xx 响应,会使用带抖动的指数退避进行重试,遵循 Retry-After,并使用相同的幂等键。
  • 速率限制:Wafeq 未公布具体数值限制,因此客户端默认值(每 WAFEQ_RATE_WINDOW_MS=10000 内 WAFEQ_MAX_REQUESTS=20)特意设得较为保守。如果你了解自己的额度,可以调高。
  • 每个凭据对应一个组织。 Wafeq API 密钥限定于某个组织;每次工具调用都会针对该组织,且 /health 会显示组织名称。

CI 与发布

每次推送和拉取请求都会在 Node 20 和 22 上构建并测试,并在不提供凭据的情况下生成工具目录——这可以在工具名称重复或违反架构规则时,在发布前发现问题。CI 还会在 .env 被纳入版本控制时失败。

发布仅通过 vX.Y.Z 标签完成。 版本号无需手动维护。推送标签会运行完整流程:

  1. 根据标签确定一次版本号。
  2. 构建镜像并推送到 ghcr.io/ohneben/wafeq-mcp,镜像标签包括版本号、MAJOR.MINOR、短 SHA,以及 main 上的 latest。
  3. 将条目发布到 MCP Registry,并将 server.json 固定到该确切镜像标签。镜像上的 io.modelcontextprotocol.server.name 标签用于证明所有权,其值必须与 server.json 中的 name 匹配——测试会确保这一点。
  4. 将发布的版本号写回 main 上的 package.json 和 server.json,并将标签移至该提交。因此,仓库始终记录最近发布的版本,服务也会通过 MCP 和 /health 报告该版本,无需修改代码。
npm version 2.0.1 --no-git-tag-version   # optional; CI stamps it either way
git tag v2.0.1 && git push origin v2.0.1

workflow_dispatch 可重新发布指定版本,无需创建新标签。推送到 main 会构建 -dev.g<sha> 镜像,并在此处停止——绝不会触及注册表。

安全

  • 凭据保留在服务端。 凭据从环境中读取,并按请求注入。模型只能看到工具输入和 API 响应,永远看不到密钥。透传工具无法覆盖 Authorization 标头,也不会向配置的 API 基础 URL 以外的任何主机发送凭据。
  • 切勿提交 .env。 该文件已加入 Git 忽略规则;如果它被纳入版本控制,CI 将会失败。.env.example 中仅包含占位符。
  • 绑定到 localhost,或设置令牌。 docker-compose.yml 仅在 127.0.0.1 上发布端口。如果绑定到其他地址,服务器会拒绝在未设置 MCP_AUTH_TOKEN 时启动;令牌会通过恒定时间比较进行校验。此外还会检查 Host 标头,防止网页通过 DNS 重绑定访问环回服务器。
  • 默认禁用本地文件上传。 WAFEQ_ALLOW_LOCAL_FILE_UPLOAD=false 表示服务器不会读取自身文件系统中的文件。启用后,任何能调用该服务器的对象都可以要求它读取本地路径;除非确有需要且信任每个客户端,否则请保持禁用。无论此设置如何,Base64 上传都可用。
  • 首次写入前,请通过 /health 检查组织。 API 密钥仅适用于一个组织;密钥错误时,不会报错,而是会将数据写入错误的公司。
  • 🔴 工具标记的风险不容小觑。 删除操作无法撤销,提前结束计划也没有 API 撤销功能,向税务机关提交的申报无法撤回。对于带有 destructiveHint 的操作,请务必保留主机确认。

如需报告漏洞,请参阅 SECURITY.md。

致谢与许可证

MIT — 详见 LICENSE.md。基于 Model Context Protocol TypeScript SDK 构建。 与 Wafeq 无关联,也未获得其认可。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。