企业级 MCP 授权网关技能 - 聚合财务发票查询与银行账户交易数据(客户/账户/余额/交易/对账单/回单/小时余额),统一走 Keycloak OAuth 2.1 + PKCE 认证与细粒度授权
Scanned 9/8/2026
Install to Claude Code
npx -y skills add infometa/workbuddyskills --skill skills --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of shanglv-mcp-gateway-skill?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/infometa-shanglv-mcp-gateway-skill)More formats (shields.io, HTML) on the badges page.
---
name: shanglv-mcp-gateway-skill
description: 企业级 MCP 授权网关技能 - 聚合财务发票查询与银行账户交易数据(客户/账户/余额/交易/对账单/回单/小时余额),统一走 Keycloak OAuth 2.1 + PKCE 认证与细粒度授权
version: "1.1.0"
author: "shanglv-aicoding-agent team"
---
# MCP Gateway Skill
本 Skill 通过 WorkBuddy Connector 接入企业级 MCP 授权网关(LiteLLM 聚合层),让 AI 能在**统一认证、统一授权、统一审计**的前提下,调用两类企业内部 MCP 工具:财务发票(finance)、银行账户交易数据(yztdata)。
> **关键差异点**:本 Connector 不是直接连到某个 MCP Server,而是连到 LiteLLM 网关。网关在每次 `tools/call` 前执行 PEP(Policy Enforcement Point)四步决策链:JWT 鉴权 → OpenFGA ReBAC(工具调用权 + 行级读取权)→ OPA ABAC(时段/角色/字段脱敏)→ 双写审计。AI 只需要关心工具本身的语义,授权由网关透明处理。
## 认证说明(OAuth 2.1 + PKCE)
- **认证流程**:WorkBuddy 在用户首次连接时,通过第 11 章 MCP 连接器 OAuth 流程自动完成。用户在浏览器中看到 Keycloak 授权页(realm `shanglv`),输入企业账号后点击允许即可。
- **PKCE**:采用 `code_challenge_method: S256`,WorkBuddy 作为公共客户端,无 `client_secret`。
- **Token 有效期**:`access_token` 1 小时,`refresh_token` ≥ 30 天,过期后 WorkBuddy 自动刷新,用户无感。
- **Token 作用域**:Token 中携带 `tenant_id`、`preferred_username` 等声明,网关据此做租户隔离和行级安全(RLS)。
- **重新授权**:如 `refresh_token` 也过期,WorkBuddy 会引导用户重新授权。AI 遇到 401 且无法自动刷新时,提示用户前往 WorkBuddy 设置页重连。
- **授权端点**(由网关 `/.well-known/oauth-protected-resource` + `/.well-known/oauth-authorization-server` 自动发现,无需手填):
- 授权:`https://mcp-gateway.yql.net/realms/shanglv/protocol/openid-connect/auth`
- Token:`https://mcp-gateway.yql.net/realms/shanglv/protocol/openid-connect/token`
- JWKS:`https://mcp-gateway.yql.net/realms/shanglv/protocol/openid-connect/certs`
## 可用工具
网关将上游两个 MCP Server 的工具聚合成 `<server>-<tool>` 命名格式(如 `finance-query_invoice`、`yztdata-search_customers`)。调用 `tools/list` 可拉取当前用户有权访问的完整工具清单。当前共 **13 个工具**:1 个 finance + 12 个 yztdata。
---
## 一、finance 工具
### finance-query_invoice — 查询发票
查询当前租户下指定发票明细,或列出所有可见发票。受 PostgreSQL 行级安全(RLS)保护,跨租户查询直接被数据库拦截,不返回任何行。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| invoice_id | string | ✅ | 发票号。传 `*` 列出当前租户所有可见发票(最多 100 条) |
**返回**:
- 单条:`{id, tenant_id, amount, description, created_at}`
- 列表:`{invoices: [...]}`
- 无权限:`{error: "not found or not accessible (NO_PERM_ROW_LEVEL)"}`(不区分「不存在」与「无权限」,防探测)
**使用示例**:
- 查单张发票:`finance-query_invoice(invoice_id="INV-001")`
- 列当前租户发票:`finance-query_invoice(invoice_id="*")`
**注意事项**:
- 金额(`amount`)字段:当调用方角色非 `finance_admin` 时,OPA 会下发脱敏义务,网关将 `amount` 替换为 `***`。AI 看到脱敏值应理解为「有权限查存在性,无权限看金额」。
- 跨租户查询(如 alice 查 dept_b 的 INV-002)会被 RLS 静默拦掉,返回 `not found or not accessible`,**不是**报权限错误。
- 全局黑名单工具 `finance:delete_all` / `finance:bulk_export` 不在本 Connector 暴露,任何调用都会被 PEP 拦截。
---
## 二、yztdata 工具(银行账户交易数据)
yztdata 上游提供企业银行账户的完整数据能力:从客户档案 → 银行账户 → 实时余额 → 交易明细 → 对账单 → 回单 → 小时级余额监控。工具命名前缀为 `yztdata-`。
### yztdata-search_customers — 搜索客户
按客户名称、统一社会信用代码、联系人、手机号模糊匹配,返回客户列表。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| keyword | string | - | 搜索关键词,留空返回所有客户 |
| limit | integer | - | 返回数量上限,默认 20,最大 100 |
**返回**:客户列表数组,每条含客户编号、名称、统一社会信用代码、联系人、手机号等。
**使用示例**:
- 按名称模糊搜索:`yztdata-search_customers(keyword="科技")`
- 列出所有客户(前 50 条):`yztdata-search_customers(limit=50)`
**注意事项**:
- 模糊匹配可能返回多条,AI 应让用户确认目标客户后再深入查档案。
- 脱敏:非授权角色看到的手机号、统一社会信用代码可能被脱敏为 `***`。
---
### yztdata-get_customer_profile — 获取客户完整档案
获取客户基本信息 + 关联的所有银行账户 + 每个账户的最新余额快照。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| customer_id | string | ✅ | 客户编号,如 `CUST2024001` |
**返回**:`{basic_info: {...}, bank_accounts: [{account_no, bank_name, balance, ...}]}`
**使用示例**:
- 查客户全档案:`yztdata-get_customer_profile(customer_id="CUST2024001")`
**注意事项**:
- 返回的账户余额是**最新快照**,如需历史时点余额用 `yztdata-get_account_balance` 传 `snapshot_date`。
- 跨主体(集团场景)下,basic_info 里可能含 `entity_id`,后续按子公司过滤交易时需要。
---
### yztdata-list_customer_accounts — 列出客户银行账户
列出客户所有银行账户,可按银行过滤,每个账户附带最新余额快照。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| customer_id | string | ✅ | 客户编号 |
| bank_name | string | - | 银行名称(模糊匹配,可选) |
**返回**:账户数组,每条含账户号、银行名、账户类型、最新余额、更新时间。
**使用示例**:
- 列某客户所有账户:`yztdata-list_customer_accounts(customer_id="CUST2024001")`
- 只看建行账户:`yztdata-list_customer_accounts(customer_id="CUST2024001", bank_name="建设")`
---
### yztdata-get_account_balance — 获取账户余额
获取指定银行账户的余额,支持查询历史时点余额。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号(支持脱敏形式如 `6225****1234`) |
| snapshot_date | string | - | 快照日期 `YYYY-MM-DD` 或 `YYYY-MM`,不传返回最新 |
**返回**:`{account_no, balance, snapshot_date, currency, ...}`
**使用示例**:
- 查最新余额:`yztdata-get_account_balance(account_no="6225****1234")`
- 查上月末余额:`yztdata-get_account_balance(account_no="6225****1234", snapshot_date="2026-06")`
**注意事项**:
- 历史时点余额依赖快照表,若该日期无快照可能返回 `null` 或最近邻快照,AI 应提示用户日期可能无数据。
---
### yztdata-query_transactions — 查询交易明细
查询账户交易明细,支持日期范围、借贷方向、金额区间、对方户名、关键词、业务类型多维筛选,分页返回。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号 |
| start_date | string | - | 起始日期 `YYYY-MM-DD`(包含) |
| end_date | string | - | 结束日期 `YYYY-MM-DD`(包含) |
| txn_type | string | - | `DEBIT`(借/出)或 `CREDIT`(贷/入) |
| min_amount | number | - | 最小金额 |
| max_amount | number | - | 最大金额 |
| counterparty | string | - | 对方户名(模糊匹配) |
| keyword | string | - | 关键词(匹配摘要/对方/渠道) |
| biz_category | string | - | 业务类型,可选值见 `yztdata-list_biz_categories` |
| biz_scenario | string | - | 业务场景(精确匹配,如「外贸TT收汇-跨境汇款」) |
| counterparty_type | string | - | 对方类型(`客户` / `供应商`) |
| entity_id | string | - | 法人主体 ID(集团场景按子公司过滤,先调 `get_customer_profile` 获取) |
| page | integer | - | 页码,默认 1 |
| page_size | integer | - | 每页条数,默认 20,最大 200 |
**返回**:`{transactions: [...], total: N, page: P, page_size: S}`
**使用示例**:
- 查最近一个月贷方入账且金额 > 10000:`yztdata-query_transactions(account_no="6225****1234", start_date="2026-06-15", end_date="2026-07-15", txn_type="CREDIT", min_amount=10000)`
- 按对方户名查:`yztdata-query_transactions(account_no="6225****1234", counterparty="某供应商")`
**注意事项**:
- 大结果集自动分页,AI 拉到第一页后应看 `total` 判断是否还有更多。
- `biz_category` 建议先调 `yztdata-list_biz_categories` 拿可选值再传,避免无效筛选。
---
### yztdata-list_biz_categories — 列出业务类型
列出当前数据中出现的所有业务类型(biz_category),例如:融资类、票据类、扣款类、利息类、税务类、电商及三方支付、日常经营。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | - | 账户号(可选,默认全量统计) |
**返回**:`{categories: ["融资类", "票据类", ...]}`
**使用示例**:
- 列全量业务类型:`yztdata-list_biz_categories()`
- 只看某账户出现的类型:`yztdata-list_biz_categories(account_no="6225****1234")`
---
### yztdata-list_statements — 列出对账单
列出账户的对账单(按月),可按年份过滤。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号 |
| year | string | - | 年份,如 `2025`(可选) |
**返回**:`{statements: [{period: "2025-06", ...}]}`
**使用示例**:
- 列 2026 年所有对账单:`yztdata-list_statements(account_no="6225****1234", year="2026")`
---
### yztdata-get_statement_detail — 获取对账单详情
获取指定期间对账单的完整内容,含借贷汇总、期初/期末余额、生成时间。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号 |
| period | string | ✅ | 期间 `YYYY-MM` |
**返回**:`{period, debit_total, credit_total, opening_balance, closing_balance, generated_at, ...}`
**使用示例**:
- 查 2026 年 6 月对账单:`yztdata-get_statement_detail(account_no="6225****1234", period="2026-06")`
---
### yztdata-list_receipts — 列出回单
列出账户的电子回单,每个回单附 `download_url`,可通过 HTTP GET 下载真实 PDF/图片文件。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | - | 账户号(与 `customer_id` 二选一) |
| customer_id | string | - | 客户编号(查该客户所有账户的回单) |
| txn_id | string | - | 交易 ID(精确匹配) |
| start_date | string | - | 起始日期 `YYYY-MM-DD` |
| end_date | string | - | 结束日期 `YYYY-MM-DD` |
| limit | integer | - | 返回上限,默认 50,最大 500 |
**返回**:`{receipts: [{txn_id, date, amount, download_url, ...}]}`
**使用示例**:
- 查某账户最近 100 条回单:`yztdata-list_receipts(account_no="6225****1234", limit=100)`
- 按客户查所有回单:`yztdata-list_receipts(customer_id="CUST2024001")`
**注意事项**:
- `download_url` 可能是带签名的临时链接,有时效性,AI 应提示用户尽快下载。
- 非授权用户可能拿到回单列表但 `download_url` 被脱敏为 `***`(仅能看存在性,不能下载)。
---
### yztdata-get_hourly_balance — 获取某月小时余额
获取某账户某月所有小时余额快照(每天 24 小时,一个月约 720-744 条)。可用于观察日内余额波动、监控大额进出、识别异常时段。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号(脱敏形式如 `6216****2782`) |
| period | string | ✅ | 月份 `YYYY-MM`,如 `2026-07` |
**返回**:`{snapshots: [{timestamp: "2026-07-01T00:00:00", balance: ...}], count: N}`
**使用示例**:
- 查 2026 年 7 月小时余额:`yztdata-get_hourly_balance(account_no="6216****2782", period="2026-07")`
**注意事项**:
- 数据量大(720+ 条/月),AI 调用前应向用户确认是否真的需要小时粒度,通常 `get_hourly_balance_stats` 摘要更够用。
- 异常时段(如凌晨 3 点大额进出)可能是资金异常信号,AI 看到应主动提示用户关注。
---
### yztdata-get_hourly_balance_range — 查询时间区间小时余额
查询指定时间区间内的小时余额快照(自动覆盖涉及的月份),可用于拉取连续时段数据。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号 |
| start_time | string | ✅ | 起始时间 `YYYY-MM-DD` 或 `YYYY-MM-DD HH:MM:SS` |
| end_time | string | ✅ | 结束时间 `YYYY-MM-DD` 或 `YYYY-MM-DD HH:MM:SS` |
**返回**:`{snapshots: [...], count: N}`
**使用示例**:
- 拉最近一周小时余额:`yztdata-get_hourly_balance_range(account_no="6216****2782", start_time="2026-07-08", end_time="2026-07-15")`
---
### yztdata-get_hourly_balance_stats — 小时余额统计摘要
获取某月小时余额的统计摘要:峰值/谷值/平均值/最大单小时变动额/有交易的小时数。适合做监控指标。
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| account_no | string | ✅ | 账户号 |
| period | string | ✅ | 月份 `YYYY-MM` |
**返回**:`{peak, trough, average, max_hourly_delta, active_hours, period}`
**使用示例**:
- 查 2026-07 余额统计摘要:`yztdata-get_hourly_balance_stats(account_no="6216****2782", period="2026-07")`
**注意事项**:
- 优先调本工具做快速诊断,发现异常(如 `max_hourly_delta` 异常大)再调 `get_hourly_balance` 拉明细排查。
---
## 错误处理
PEP 拒绝时返回标准 MCP `isError: true` 响应(HTTP 仍为 200),AI **必须检查 `result.isError` 而非 HTTP status**。常见错误细分:
| 错误码 | 含义 | AI 应对 |
|--------|------|---------|
| `UNAUTHENTICATED` | JWT 缺失/过期且刷新失败 | 提示用户前往 WorkBuddy 设置页重新授权 |
| `NO_TOOL_PERMISSION` | 用户无该工具的 `caller` 关系 | 告知用户无此工具权限,建议联系管理员申请 |
| `NO_READER_PERMISSION` | 有工具调用权但无行级读取权 | 告知用户对该数据行无读取权限,结果不可见 |
| `BLOCKED_TOOL` | 工具在全局黑名单 | 不应尝试调用,说明该操作被策略禁止 |
| `OFF_BUSINESS_HOURS` | 非工作时间调用敏感工具 | 告知用户当前时段不可用,建议工作时间重试 |
| `OPA_UNAVAILABLE` | OPA 熔断(连续 5 次失败) | 网关自动降级为 FGA-only 模式,AI 应提示「授权降级中,部分策略可能不生效」 |
## 注意事项
1. **租户隔离是硬约束**:所有工具调用都带 `X-Tenant-Id`,由网关从 JWT 注入,AI 无法绕过。AI 不应在参数里手填租户字段。
2. **审计全覆盖**:每次 `tools/call`(含 ALLOW 和 DENY)都双写 PostgreSQL + ClickHouse。AI 应告知用户操作会被审计。
3. **降级语义**:OPA 熔断时网关降级为「FGA-only + 标记 DEGRADED」,AI 看到此标记应提示用户当前为降级授权模式。
4. **缓存失效**:权限变更后可由运维调 `POST /pep/cache/invalidate-user` 立即生效;AI 无需关心,但可提示用户「权限变更后可能需要几分钟生效或联系运维立即刷新」。
5. **优先调 tools/list**:上游工具集会随版本演进,AI 拿到可用工具清单后再决定调用,不要假设工具固定不变。
6. **账户号脱敏形式**:yztdata 工具的 `account_no` 参数普遍支持脱敏形式(如 `6225****1234`),AI 不应尝试还原完整卡号,直接用脱敏形式调用即可。
7. **业务类型枚举**:调用 `yztdata-query_transactions` 传 `biz_category` 前,建议先调 `yztdata-list_biz_categories` 拿可选值,避免无效筛选。
8. **集团多主体**:`yztdata-query_transactions` 的 `entity_id` 用于集团场景按子公司过滤,需先调 `yztdata-get_customer_profile` 获取 `entity_id` 再传。
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!