关于 Wafeq
Wafeq 是面向 Wafeq 会计数据用户的 MCP 服务,将公共 API 转换为工具,并为操作标注安全类别。它支持 stdio 和 Streamable HTTP,并针对重试、幂等性、文件上传及 PDF 响应进行处理。
主要功能
- 01由 OpenAPI 生成 Wafeq API 工具
- 02为操作标注安全类别
- 03写入请求自动添加幂等键
- 04支持文件上传与 PDF 二进制响应
- 05验证组织身份并提供健康状态
- 06支持重试、限流及 stdio 和 HTTP
适用场景
- 01查询损益表及未付款发票
- 02创建发票草稿或下载 PDF
- 03将收据附加到费用记录
- 04核对银行流水与账簿
MCP 相关
该 MCP 服务将 Wafeq 公共 API 暴露为工具,支持 stdio 和 Streamable HTTP;启动时验证凭据所属组织。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
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.0MCP_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/v1MCP_AUTH_TOKEN密钥Only for MCP_TRANSPORT=http: bearer token required on /mcp. Without it a non-loopback bind refuses to start.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/ohneben/wafeq-mcp:2.1.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
ohneben 的 Wafeq MCP
许可证与检查
MCP 注册目录
通过 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_SCHEME | auto | 强制使用 api-key 或 bearer。通常留空即可。 |
WAFEQ_API_BASE_URL | https://api.wafeq.com/v1 | Wafeq API 基础 URL。 |
WAFEQ_OPENAPI_PATH | 内置规范 | 使用其他 OpenAPI 文档(JSON 或 YAML)。 |
MCP_TRANSPORT | stdio | stdio 或 http。Docker 会设置为 http。 |
PORT | 8765 | HTTP 监听端口。 |
HOST | 0.0.0.0 | HTTP 绑定地址。 |
MCP_HTTP_PATH | /mcp | MCP 端点提供服务的路径。 |
MCP_AUTH_TOKEN | — | 访问 /mcp 时必须提供的 Bearer 令牌。除非 HOST 是环回地址,否则必须设置;否则服务会拒绝启动。此变量在 2.1.0 中从 MCP_SHARED_TOKEN 更名而来。 |
MCP_ALLOWED_HOSTS | — | 允许 Host 标头携带的主机名,以逗号分隔。留空时由服务自行确定:绑定到环回地址时使用环回主机名;位于反向代理后时不进行检查。 |
MCP_ALLOW_INSECURE | 0 | 在非环回地址上不使用令牌启动。仅适用于确实无法被其他人访问的端口。 |
MCP_BODY_LIMIT | 25mb | 允许的最大请求正文大小。 |
MCP_SESSION_TTL | 1800 | 空闲会话在清理前保留的秒数。 |
MCP_MAX_SESSIONS | 256 | 并发会话上限;达到上限后,会淘汰最近最少使用的会话。 |
WAFEQ_TOOL_GROUPS | — | 要公开的资源组,以逗号分隔,例如 invoices,bills,reports。留空 = 全部 251 项。运行 npm run list-tools 查看列表。 |
WAFEQ_MAX_REQUESTS | 20 | 客户端速率限制:每个时间窗口内的请求数。设为 0 可禁用限流。 |
WAFEQ_RATE_WINDOW_MS | 10000 | 速率限制窗口,单位为毫秒。 |
WAFEQ_MAX_RETRIES | 3 | 遇到 429 / 5xx / 网络错误时的重试次数。 |
WAFEQ_TIMEOUT_MS | 30000 | 单次请求尝试的超时时间。 |
WAFEQ_ALLOW_LOCAL_FILE_UPLOAD | false | 允许上传工具通过 file_path 读取此计算机的文件系统。参见安全性。 |
WAFEQ_MAX_UPLOAD_BYTES | 26214400 | 解码后的最大上传大小(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 个)。
| 标识 | 工具 | readOnlyHint | destructiveHint | 涵盖内容 |
|---|---|---|---|---|
🟢 READ-ONLY | 85 | ✅ | ❌ | 所有 GET,以及账户分类账便捷工具。 |
🟢 READ-ONLY · 返回 PDF | 9 | ✅ | ❌ | 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 透传工具(其影响无法预先得知)。 |
| 253 | 98 | 44 |
三个 🔴 分组都设置了 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)
| Tool | Endpoint |
|---|---|
wafeq_bills_download_retrieve | GET /bills/{id}/download/ |
wafeq_credit_notes_download_retrieve | GET /credit-notes/{id}/download/ |
wafeq_debit_notes_download_retrieve | GET /debit-notes/{id}/download/ |
wafeq_invoices_download_retrieve | GET /invoices/{id}/download/ |
wafeq_payments_download_retrieve | GET /payments/{id}/download/ |
wafeq_payslips_download_retrieve | GET /payslips/{id}/download/ |
wafeq_purchase_orders_download_retrieve | GET /purchase-orders/{id}/download/ |
wafeq_quotes_download_retrieve | GET /quotes/{id}/download/ |
wafeq_simplified_invoices_download_retrieve | GET /simplified-invoices/{id}/download/ |
🟢 READ-ONLY (PREVIEW) (4)
| Tool | Endpoint |
|---|---|
wafeq_amortizations_preview_create | POST /amortizations/preview/ |
wafeq_amortizations_preview_end_early_create | POST /amortizations/{id}/preview-end-early/ |
wafeq_revenue_recognitions_preview_create | POST /revenue-recognitions/preview/ |
wafeq_revenue_recognitions_preview_end_early_create | POST /revenue-recognitions/{id}/preview-end-early/ |
🟡 WRITE · CREATES (39)
| Tool | Endpoint |
|---|---|
wafeq_accounts_create | POST /accounts/ |
wafeq_bank_accounts_create | POST /bank-accounts/ |
wafeq_bank_accounts_ledger_transactions_create | POST /bank-accounts/{bank_account_id}/ledger-transactions/ |
wafeq_bank_accounts_statement_transactions_create | POST /bank-accounts/{bank_account_id}/statement-transactions/ |
wafeq_beneficiaries_create | POST /beneficiaries/ |
wafeq_bills_create | POST /bills/ |
wafeq_bills_line_items_create | POST /bills/{bill_id}/line-items/ |
wafeq_branches_create | POST /branches/ |
wafeq_contacts_create | POST /contacts/ |
wafeq_cost_centers_create | POST /cost-centers/ |
wafeq_credit_notes_create | POST /credit-notes/ |
wafeq_credit_notes_line_items_create | POST /credit-notes/{credit_note_id}/line-items/ |
wafeq_custom_fields_create | POST /custom-fields/ |
wafeq_debit_notes_create | POST /debit-notes/ |
wafeq_debit_notes_line_items_create | POST /debit-notes/{debit_note_id}/line-items/ |
wafeq_employees_create | POST /employees/ |
wafeq_expenses_create | POST /expenses/ |
wafeq_invoices_create | POST /invoices/ |
wafeq_invoices_line_items_create | POST /invoices/{invoice_id}/line-items/ |
wafeq_item_units_of_measure_create | POST /item-units-of-measure/ |
wafeq_items_create | POST /items/ |
wafeq_manual_journals_create | POST /manual-journals/ |
wafeq_payment_requests_create | POST /payment_requests/ |
wafeq_payments_create | POST /payments/ |
wafeq_payslips_create | POST /payslips/ |
wafeq_payslips_pay_items_create | POST /payslips/{payslip_id}/pay-items/ |
wafeq_projects_create | POST /projects/ |
wafeq_purchase_orders_bill_create | POST /purchase-orders/{id}/bill/ |
wafeq_purchase_orders_create | POST /purchase-orders/ |
wafeq_purchase_orders_line_items_create | POST /purchase-orders/{purchase_order_id}/line-items/ |
wafeq_quotes_create | POST /quotes/ |
wafeq_quotes_invoice_create | POST /quotes/{id}/invoice/ |
wafeq_quotes_line_items_create | POST /quotes/{quote_id}/line-items/ |
wafeq_simplified_invoices_create | POST /simplified-invoices/ |
wafeq_simplified_invoices_line_items_create | POST /simplified-invoices/{invoice_id}/line-items/ |
wafeq_units_of_measure_create | POST /units-of-measure/ |
wafeq_upload_file | POST /files/ |
wafeq_upload_file_raw | POST /files/raw/ |
wafeq_warehouses_create | POST /warehouses/ |
🟡 WRITE · UPDATES (70)
| Tool | Endpoint |
|---|---|
wafeq_accounts_partial_update | PATCH /accounts/{id}/ |
wafeq_accounts_update | PUT /accounts/{id}/ |
wafeq_bank_accounts_ledger_transactions_partial_update | PATCH /bank-accounts/{bank_account_id}/ledger-transactions/{id}/ |
wafeq_bank_accounts_ledger_transactions_update | PUT /bank-accounts/{bank_account_id}/ledger-transactions/{id}/ |
wafeq_bank_accounts_partial_update | PATCH /bank-accounts/{id}/ |
wafeq_bank_accounts_statement_transactions_partial_update | PATCH /bank-accounts/{bank_account_id}/statement-transactions/{id}/ |
wafeq_bank_accounts_statement_transactions_update | PUT /bank-accounts/{bank_account_id}/statement-transactions/{id}/ |
wafeq_bank_accounts_update | PUT /bank-accounts/{id}/ |
wafeq_beneficiaries_partial_update | PATCH /beneficiaries/{id}/ |
wafeq_beneficiaries_update | PUT /beneficiaries/{id}/ |
wafeq_bills_line_items_partial_update | PATCH /bills/{bill_id}/line-items/{id}/ |
wafeq_bills_line_items_update | PUT /bills/{bill_id}/line-items/{id}/ |
wafeq_bills_partial_update | PATCH /bills/{id}/ |
wafeq_bills_update | PUT /bills/{id}/ |
wafeq_branches_partial_update | PATCH /branches/{id}/ |
wafeq_branches_update | PUT /branches/{id}/ |
wafeq_contacts_partial_update | PATCH /contacts/{id}/ |
wafeq_contacts_update | PUT /contacts/{id}/ |
wafeq_cost_centers_partial_update | PATCH /cost-centers/{id}/ |
wafeq_cost_centers_update | PUT /cost-centers/{id}/ |
wafeq_credit_notes_line_items_partial_update | PATCH /credit-notes/{credit_note_id}/line-items/{id}/ |
wafeq_credit_notes_line_items_update | PUT /credit-notes/{credit_note_id}/line-items/{id}/ |
wafeq_credit_notes_partial_update | PATCH /credit-notes/{id}/ |
wafeq_credit_notes_update | PUT /credit-notes/{id}/ |
wafeq_custom_fields_partial_update | PATCH /custom-fields/{id}/ |
wafeq_custom_fields_update | PUT /custom-fields/{id}/ |
wafeq_debit_notes_line_items_partial_update | PATCH /debit-notes/{debit_note_id}/line-items/{id}/ |
wafeq_debit_notes_line_items_update | PUT /debit-notes/{debit_note_id}/line-items/{id}/ |
wafeq_debit_notes_partial_update | PATCH /debit-notes/{id}/ |
wafeq_debit_notes_update | PUT /debit-notes/{id}/ |
wafeq_employees_partial_update | PATCH /employees/{id}/ |
wafeq_employees_update | PUT /employees/{id}/ |
wafeq_expenses_partial_update | PATCH /expenses/{id}/ |
wafeq_expenses_update | PUT /expenses/{id}/ |
wafeq_invoices_line_items_partial_update | PATCH /invoices/{invoice_id}/line-items/{id}/ |
wafeq_invoices_line_items_update | PUT /invoices/{invoice_id}/line-items/{id}/ |
wafeq_invoices_partial_update | PATCH /invoices/{id}/ |
wafeq_invoices_update | PUT /invoices/{id}/ |
wafeq_item_units_of_measure_partial_update | PATCH /item-units-of-measure/{id}/ |
wafeq_item_units_of_measure_update | PUT /item-units-of-measure/{id}/ |
wafeq_items_partial_update | PATCH /items/{id}/ |
wafeq_items_update | PUT /items/{id}/ |
wafeq_manual_journals_partial_update | PATCH /manual-journals/{id}/ |
wafeq_manual_journals_update | PUT /manual-journals/{id}/ |
wafeq_payment_requests_partial_update | PATCH /payment_requests/{id}/ |
wafeq_payment_requests_update | PUT /payment_requests/{id}/ |
wafeq_payments_partial_update | PATCH /payments/{id}/ |
wafeq_payments_update | PUT /payments/{id}/ |
wafeq_payslips_partial_update | PATCH /payslips/{id}/ |
wafeq_payslips_pay_items_partial_update | PATCH /payslips/{payslip_id}/pay-items/{id}/ |
wafeq_payslips_pay_items_update | PUT /payslips/{payslip_id}/pay-items/{id}/ |
wafeq_payslips_update | PUT /payslips/{id}/ |
wafeq_projects_partial_update | PATCH /projects/{id}/ |
wafeq_projects_update | PUT /projects/{id}/ |
wafeq_purchase_orders_line_items_partial_update | PATCH /purchase-orders/{purchase_order_id}/line-items/{id}/ |
wafeq_purchase_orders_line_items_update | PUT /purchase-orders/{purchase_order_id}/line-items/{id}/ |
wafeq_purchase_orders_partial_update | PATCH /purchase-orders/{id}/ |
wafeq_purchase_orders_update | PUT /purchase-orders/{id}/ |
wafeq_quotes_line_items_partial_update | PATCH /quotes/{quote_id}/line-items/{id}/ |
wafeq_quotes_line_items_update | PUT /quotes/{quote_id}/line-items/{id}/ |
wafeq_quotes_partial_update | PATCH /quotes/{id}/ |
wafeq_quotes_update | PUT /quotes/{id}/ |
wafeq_simplified_invoices_line_items_partial_update | PATCH /simplified-invoices/{invoice_id}/line-items/{id}/ |
wafeq_simplified_invoices_line_items_update | PUT /simplified-invoices/{invoice_id}/line-items/{id}/ |
wafeq_simplified_invoices_partial_update | PATCH /simplified-invoices/{id}/ |
wafeq_simplified_invoices_update | PUT /simplified-invoices/{id}/ |
wafeq_units_of_measure_partial_update | PATCH /units-of-measure/{id}/ |
wafeq_units_of_measure_update | PUT /units-of-measure/{id}/ |
wafeq_warehouses_partial_update | PATCH /warehouses/{id}/ |
wafeq_warehouses_update | PUT /warehouses/{id}/ |
🟠 STATE CHANGE (2)
| Tool | Endpoint |
|---|---|
wafeq_expenses_mark_as_draft_create | POST /expenses/{id}/mark-as-draft/ |
wafeq_expenses_mark_as_posted_create | POST /expenses/{id}/mark-as-posted/ |
🔴 IRREVERSIBLE · EXTERNAL FILING (3)
| Tool | Endpoint |
|---|---|
wafeq_credit_notes_tax_authority_report_create | POST /credit-notes/{id}/tax-authority/report/ |
wafeq_invoices_tax_authority_report_create | POST /invoices/{id}/tax-authority/report/ |
wafeq_simplified_invoices_tax_authority_report_create | POST /simplified-invoices/{id}/tax-authority/report/ |
🔴 IRREVERSIBLE · LEDGER (2)
| Tool | Endpoint |
|---|---|
wafeq_amortizations_end_early_create | POST /amortizations/{id}/end-early/ |
wafeq_revenue_recognitions_end_early_create | POST /revenue-recognitions/{id}/end-early/ |
🔴 DESTRUCTIVE · DELETES (39)
| Tool | Endpoint |
|---|---|
wafeq_accounts_destroy | DELETE /accounts/{id}/ |
wafeq_amortizations_destroy | DELETE /amortizations/{id}/ |
wafeq_bank_accounts_destroy | DELETE /bank-accounts/{id}/ |
wafeq_bank_accounts_ledger_transactions_destroy | DELETE /bank-accounts/{bank_account_id}/ledger-transactions/{id}/ |
wafeq_bank_accounts_statement_transactions_destroy | DELETE /bank-accounts/{bank_account_id}/statement-transactions/{id}/ |
wafeq_beneficiaries_destroy | DELETE /beneficiaries/{id}/ |
wafeq_bills_destroy | DELETE /bills/{id}/ |
wafeq_bills_line_items_destroy | DELETE /bills/{bill_id}/line-items/{id}/ |
wafeq_branches_destroy | DELETE /branches/{id}/ |
wafeq_contacts_destroy | DELETE /contacts/{id}/ |
wafeq_cost_centers_destroy | DELETE /cost-centers/{id}/ |
wafeq_credit_notes_destroy | DELETE /credit-notes/{id}/ |
wafeq_credit_notes_line_items_destroy | DELETE /credit-notes/{credit_note_id}/line-items/{id}/ |
wafeq_custom_fields_destroy | DELETE /custom-fields/{id}/ |
wafeq_debit_notes_destroy | DELETE /debit-notes/{id}/ |
wafeq_debit_notes_line_items_destroy | DELETE /debit-notes/{debit_note_id}/line-items/{id}/ |
wafeq_employees_destroy | DELETE /employees/{id}/ |
wafeq_expenses_destroy | DELETE /expenses/{id}/ |
wafeq_files_destroy | DELETE /files/{id}/ |
wafeq_invoices_destroy | DELETE /invoices/{id}/ |
wafeq_invoices_line_items_destroy | DELETE /invoices/{invoice_id}/line-items/{id}/ |
wafeq_item_units_of_measure_destroy | DELETE /item-units-of-measure/{id}/ |
wafeq_items_destroy | DELETE /items/{id}/ |
wafeq_manual_journals_destroy | DELETE /manual-journals/{id}/ |
wafeq_payment_requests_destroy | DELETE /payment_requests/{id}/ |
wafeq_payments_destroy | DELETE /payments/{id}/ |
wafeq_payslips_destroy | DELETE /payslips/{id}/ |
wafeq_payslips_pay_items_destroy | DELETE /payslips/{payslip_id}/pay-items/{id}/ |
wafeq_projects_destroy | DELETE /projects/{id}/ |
wafeq_purchase_orders_destroy | DELETE /purchase-orders/{id}/ |
wafeq_purchase_orders_line_items_destroy | DELETE /purchase-orders/{purchase_order_id}/line-items/{id}/ |
wafeq_quotes_destroy | DELETE /quotes/{id}/ |
wafeq_quotes_line_items_destroy | DELETE /quotes/{quote_id}/line-items/{id}/ |
wafeq_request | hand-written |
wafeq_revenue_recognitions_destroy | DELETE /revenue-recognitions/{id}/ |
wafeq_simplified_invoices_destroy | DELETE /simplified-invoices/{id}/ |
wafeq_simplified_invoices_line_items_destroy | DELETE /simplified-invoices/{invoice_id}/line-items/{id}/ |
wafeq_units_of_measure_destroy | DELETE /units-of-measure/{id}/ |
wafeq_warehouses_destroy | DELETE /warehouses/{id}/ |
覆盖范围
| 范围 | 工具 | 🟢 读取 | 🟡 写入 | 🔴 不可逆 | 🔴 删除 |
|---|---|---|---|---|---|
| 销售与应收款 | 69 | 25 | 31 | 3 | 10 |
| 采购与应付款 | 54 | 19 | 27 | 0 | 8 |
| 银行业务 | 18 | 6 | 9 | 0 | 3 |
| 总账与报告 | 31 | 19 | 6 | 2 | 4 |
| 薪资 | 19 | 7 | 9 | 0 | 3 |
| 主数据与维度 | 54 | 18 | 27 | 0 | 9 |
| 文件与组织 | 6 | 3 | 2 | 0 | 1 |
| 备用接口与便捷工具 | 2 | 1 | 0 | 0 | 1 |
| 总计 | 253 | 98 | 111 | 5 | 39 |
“写入”包括两个 🟠 状态变更工具。各范围对应的 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 标签完成。 版本号无需手动维护。推送标签会运行完整流程:
- 根据标签确定一次版本号。
- 构建镜像并推送到
ghcr.io/ohneben/wafeq-mcp,镜像标签包括版本号、MAJOR.MINOR、短 SHA,以及main上的latest。 - 将条目发布到 MCP Registry,并将
server.json固定到该确切镜像标签。镜像上的io.modelcontextprotocol.server.name标签用于证明所有权,其值必须与server.json中的name匹配——测试会确保这一点。 - 将发布的版本号写回
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 无关联,也未获得其认可。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。