# 20 MCP 凭据 > 状态:**已实施(2026-11-04)**。补充 ADR-0021(第三方 MCP 服务)。 ## 1. 问题 每个 MCP 服务各自保存请求头(`mcp_servers.headers`)或环境变量(`env`)。同一个后端提供多个 MCP 服务时,令牌要在每个服务里重复填写;更换令牌时要逐个修改。 ## 2. 方案 凭据是一组命名的密钥值,服务**引用**而不是复制: | 字段 | 说明 | | --- | --- | | 名称、说明 | 名称唯一 | | 类型 | `http`:请求头,用于远程服务;`stdio`:环境变量,用于本地进程 | | 值 | 若干键值(如 `Authorization: Bearer …`),保存后不再返回明文 | | 适用地址 | 仅 http:若干地址前缀;不填表示可用于任意地址 | - 服务增加 `credential_id`。连接时合并:`{ ...凭据的值, ...服务自己的值 }`,同名以服务自己填写的为准。 - 合并发生在 `McpServerService` 交给连接层之前(`rows()`、列工具、测试连接),连接层的连接键包含合并后的值:凭据修改后,下次使用自动按新值重新连接;修改值时还会主动刷新使用它的服务。 - 凭据只在服务端使用;新建、修改、删除记入审计(只记名称、键名与适用地址,不记值)。 ## 3. 适用地址(前缀匹配) - 规范化:只接受 http / https,不能带查询参数、片段、用户名;协议与主机小写。 - 匹配:协议、主机、端口完全相同;路径按段匹配——`https://a.com/mcp` 匹配 `/mcp`、`/mcp/orders`,不匹配 `/mcp2`;`https://a.com` 不匹配 `https://a.com.evil.com`。 - 服务端强制:http 服务只能选择适用地址包含其地址的凭据;修改凭据的适用地址时,若有使用它的服务不再匹配则拒绝;连接时再次检查,不匹配时不合并。 - 没有适用地址的凭据可用于任意地址,后台选择时提醒确认服务地址可信。 ## 4. 后台 - "MCP 服务"页增加"凭据"一栏:列出类型、键名、适用地址、使用它的服务;正在使用的凭据不能删除、不能改变类型。 - 添加服务时有"凭据"下拉框(只列类型一致的凭据,地址不匹配的不可选)。新建服务时按地址自动选择:正好有一个**限定了适用地址**的凭据匹配时选中它;手动选择后不再自动改动。 - 编辑已有服务时可以"把这个服务的请求头 / 环境变量保存为凭据":用已保存的值新建凭据(默认适用地址为服务地址的协议 + 主机),服务改为引用它,自己的请求头 / 环境变量清空。 - 接口:`POST /admin/v1/mcp/credentials`、`PUT|DELETE /admin/v1/mcp/credentials/:id`、`POST /admin/v1/mcp/servers/:id/save-credential`;`GET /admin/v1/mcp` 返回 `credentials`。 ## 5. 不包括 OAuth 授权的服务:令牌与各自的授权流程绑定,按服务保存,不纳入凭据。 ## 6. 实施记录 - `services/mcp-credentials.ts`(凭据、前缀匹配、合并、保存为凭据),`services/mcp-servers.ts` 在连接前合并;迁移 `0022_mcp_credentials`。 - 后台 `McpView.vue`。 - 测试 `test/mcp-credentials.test.ts`:前缀匹配与规范化、引用凭据连接、同名覆盖、地址 / 类型限制、更换令牌后重新连接、保存为凭据、本地进程的环境变量。