Skip to main content
Glama
xencio

NexBank MCP Server

by xencio
README.md
# NexBank MCP Server

独立的 NexBank API v2 MCP Server。提供 Stateless Streamable HTTP、Bearer Token、可选 OAuth 授权码接入、MySQL Connection 解析和六个业务工具。

## 准备

1. 使用 Node.js 20 或更高版本安装依赖:`npm install`。
2. 数据库执行唯一脚本 `sql/001_init.sql`。脚本第一部分会删除旧表及其全部数据,第二部分创建统一使用 `nxb_mcp_` 前缀的新表;执行前必须确认旧数据无需保留或已完成备份。
3. 复制 `config/application.example.yaml` 为部署配置,并填写 MySQL 与两个环境的 API Base URL。
4. 通过 `--config <文件>` 或 `NEXBANK_MCP_CONFIG_FILE` 指定配置文件。

不要把真实 AppSecret、Bearer Token 或生产配置提交到仓库。

Token 有效期使用可读 Duration 配置,支持 `s`(秒)、`m`(分钟)、`h`(小时)、`d`(天)。Access Token 默认 1 小时,Refresh Token 默认 365 天:

```yaml
auth:
  accessTokenTtl: "1h"
  refreshTokenTtl: "365d"
```

OAuth `/token` 响应中的 `expires_in` 始终按秒返回。验证自动刷新时,可在重新授权前暂时设置 `accessTokenTtl: "30s"` 和 `refreshTokenTtl: "10m"`。

## 运行

```text
npm run build
npm start -- --config ./config/application.yaml
```

开发模式:

```text
npm run dev -- --config ./config/application.yaml
```

日志默认同时输出到控制台和 `./logs`,并按照服务所在时区的自然日写入
`nexbank-mcp-server-YYYY-MM-DD.log`。可以通过部署配置调整文件日志:

```yaml
logging:
  level: "info"
  file:
    enabled: true
    directory: "./logs"
    prefix: "nexbank-mcp-server"
```

设置 `file.enabled: false` 可以只保留控制台日志。日志滚动不会改变既有敏感字段脱敏规则。

MCP 入口默认为 `POST /mcp`,请求必须包含:

```http
Authorization: Bearer <26-character-ULID>.<22-character-random-secret>
```

完整 Token 固定为 49 个字符。第一部分是 26 字符 ULID,第二部分是 16 字节密码学安全随机数的 Base64URL 编码。

Stateless 模式不提供 GET/SSE 会话,`GET /mcp` 和 `DELETE /mcp` 返回 405。

## OAuth 简易绑定

在配置中增加 `oauth` 后,Server 同时支持原有 Bearer Token 和 OAuth 2.1 授权码流程:

```yaml
oauth:
  issuerUrl: "https://mcp.example.com"
  bindingEnvironment: "sandbox"
```

- `issuerUrl` 必须是对外可访问的 HTTPS Origin;仅本机开发允许 `localhost`、`127.0.0.1` 或 `[::1]` 使用 HTTP。
- `bindingEnvironment` 决定授权页凭证绑定到沙盒还是正式环境。页面本身只填写 `App ID`、`AppSecret`、`customerCode`,不要求用户登录。
- 服务端保留 PKCE、OAuth state、redirect URI 和 resource 校验。授权成功后在同一个数据库事务中创建 Bearer Token 与 `nxb_mcp_connection`,OAuth `/token` 返回的 `expires_in` 与数据库 `expires_at` 的剩余有效期一致。
- OAuth 公共客户端注册信息持久化在 `nxb_mcp_oauth_client`;未完成授权请求和授权码仍只在进程内短期保存。
- 授权码交换同时返回 Access Token、`expires_in` 和 Refresh Token。Refresh Token 只保存 SHA-256 哈希,并绑定 `client_id`、scope 和 resource;每次刷新都会同时轮换 Access Token 与 Refresh Token。
- 已使用 Refresh Token 被再次提交时,服务端会撤销整个 Token Family 及当前 Access Token,客户端必须重新授权。
- OAuth 请求参数会写入结构化日志。`AppSecret`、授权码、PKCE verifier、token 不记录原值,`appId`、`customerCode` 只记录脱敏值。

启用 OAuth 的部署必须通过 HTTPS 暴露授权端点。删除整个 `oauth` 配置块即可继续只使用手工生成的 Bearer Token。

## Token 与 Connection

生成 Token:

```text
npm run token:generate -- --name "调用方名称" --config ./config/application.yaml
```

完整 Token 只显示一次,同时会显示过期时间。随后在 `nxb_mcp_connection` 中按生成记录的数据库主键维护 Sandbox Connection;付费并取得正式配置后,为同一 `access_token_id` 增加 Production Connection。

只有未过期、`enabled=1` 且 `status='active'` 的 Connection 能调用 NexBank。停用 Token 可直接把 `nxb_mcp_access_token.enabled` 更新为 `0`。

## 测试

```text
npm test
npm run test:e2e
```

MySQL 集成测试只允许连接专用测试库:

```text
NEXBANK_MCP_TEST_CONFIG_FILE=./config/application.test.yaml
NEXBANK_MCP_ALLOW_DB_TESTS=true
npm run test:integration
```

集成测试不会创建或删除数据库。真实 Sandbox 联调仅执行只读工具;签约链接生成不属于默认自动化测试。