Skip to main content
Glama
jonathanxuu

double-check-mcp

by jonathanxuu
README.md
# double-check-mcp

一个面向生产接入的答案核查 MCP Server:外部主模型负责回答和调用业务工具,MCP 内部的 **`verifier_platform`** 负责独立核查、策略判定、Ed25519 签名和审计留痕。

> 数字签名证明结果来自指定的 `verifier_platform` 且签名后未被篡改;它本身不证明答案一定正确。核查质量仍取决于验证模型、证据质量和置信度校准。

## 数据流

```text
用户问题
   │
   ▼
主 Agent(回答、检索、调用银行内部工具)
   │  question + answer + reasoning + context + execution + webSearch
   ▼
verifier_platform_verify(MCP)
   ├─ [可选] DeepSeek 原生 web_search(复用模型 API Key,返回 5 或 10 条)
   ├─ 独立验证模型核查
   ├─ 严格结构化校验
   ├─ confidence 策略门槛
   ├─ RFC 8785 JCS canonical payload
   ├─ SHA-256 + Ed25519 签名
   └─ audit.jsonl + traces/<runId>.json
   │
   ▼
verification + policy + signature + publicKeyPem + payload
```

MCP 内部没有名为 agent 的组件;核查执行主体统一命名为 `verifier_platform`。

## 功能

- `verifier_platform_verify`:核查已有答案,可使用 DeepSeek 原生联网结果,生成策略结果并签名、审计。
- 默认检索随项目部署的 `UOB_Public_Knowledge_Base_v0.1.md` 本地知识库;命中内容会作为独立证据参与核查。
- `verifier_platform_verify_signature`:不调用模型,离线验证 payload 签名及可选 SHA-256。
- `verifier_platform_status`:不调用模型,返回脱敏配置、策略、审计状态和签名公钥。
- 同时支持 stdio 和本地 Streamable HTTP;HTTP 默认地址为 `http://127.0.0.1:4200/mcp`。
- 可选择 `openai`、`deepseek`、`anthropic`、`gemini` 或任意 `openai-compatible` 模型服务。
- OpenAI 使用 Responses API;Anthropic 使用 Messages API;Gemini 使用 `generateContent`;DeepSeek/自定义服务使用 Chat Completions。
- 严格 Zod 输入/输出 Schema,MCP 同时返回人类可读文本和 `structuredContent`。
- Ed25519 私钥首次使用自动生成,权限自动收紧到 `0600`。
- 使用 RFC 8785 JCS 规范化 JSON,跨语言验签不依赖对象字段插入顺序。
- 审计 JSONL 串行追加并执行 `fsync`;单次 trace 使用临时文件后原子落盘。
- 模型超时、请求取消、输入大小限制、密钥配对检查及明确错误码。

## 环境要求

- Node.js 22 或更高版本
- 一个兼容 OpenAI `/chat/completions` 的验证模型 API

## 安装和启动

```sh
npm install
cp .env.example .env
```

本项目不会自动加载 `.env`。可以由 MCP Host 设置环境变量,或在 shell/进程管理器中注入。

先检查配置并初始化签名密钥:

```sh
VERIFIER_PLATFORM_LLM_API_KEY=your-key npm run dev -- doctor
```

本地开发启动:

```sh
VERIFIER_PLATFORM_LLM_API_KEY=your-key npm run dev
```

构建后启动:

```sh
npm run build
VERIFIER_PLATFORM_LLM_API_KEY=your-key node dist/cli.js server
```

`stdio` 的 stdout 是 MCP 协议通道,运行期间不要向 stdout 写普通日志。

## 本地 HTTP 模式

在 `.env` 中配置:

```dotenv
VERIFIER_PLATFORM_HTTP_HOST=127.0.0.1
VERIFIER_PLATFORM_HTTP_PORT=4200
VERIFIER_PLATFORM_HTTP_MCP_PATH=/mcp

# 可选;其他 App 支持 Authorization 请求头时建议设置
# VERIFIER_PLATFORM_HTTP_BEARER_TOKEN=一段足够长的随机字符串
```

启动:

```sh
node --env-file=.env dist/cli.js http
```

启动成功后终端会显示:

```text
verifier-platform-mcp listening on http://127.0.0.1:4200/mcp
health check: http://127.0.0.1:4200/health
```

健康检查:

```sh
curl http://127.0.0.1:4200/health
```

在其他支持远程/Streamable HTTP MCP 的 App 中填写:

```text
Transport: Streamable HTTP
URL: http://127.0.0.1:4200/mcp
```

如果启用了 Bearer Token,再配置请求头:

```text
Authorization: Bearer 你配置的Token
```

Codex URL 接入示例(未开启 Token):

```sh
codex mcp add doublecheck-http --url http://127.0.0.1:4200/mcp
```

启用 Token 时,可以让 Codex 从环境变量读取:

```sh
export VERIFIER_PLATFORM_MCP_TOKEN='与你在服务端配置相同的Token'
codex mcp add doublecheck-http \
  --url http://127.0.0.1:4200/mcp \
  --bearer-token-env-var VERIFIER_PLATFORM_MCP_TOKEN
```

HTTP 服务兼容当前 MCP 请求格式,并为旧版 Host 提供 stateless 兼容入口。默认仅监听 `127.0.0.1`。不要为了本地测试改成 `0.0.0.0`;确实需要局域网访问时,必须同时配置 Bearer Token、防火墙和 `VERIFIER_PLATFORM_HTTP_ALLOWED_HOSTS`。

## MCP Host 配置

先执行 `npm run build`,然后在支持 stdio MCP 的 Host 中配置:

```json
{
  "mcpServers": {
    "doublecheck": {
      "command": "node",
      "args": [
        "/absolute/path/to/double-check-mcp/dist/cli.js",
        "server"
      ],
      "env": {
        "VERIFIER_PLATFORM_LLM_PROVIDER": "deepseek",
        "DEEPSEEK_API_KEY": "YOUR_API_KEY",
        "VERIFIER_PLATFORM_LLM_MODEL": "deepseek-chat",
        "VERIFIER_PLATFORM_CONFIDENCE_FLOOR": "0.70"
      }
    }
  }
}
```

## 选择 verifier 模型

provider 和 model 是服务端部署配置,不是 `verifier_platform_verify` 的调用参数。这样可以防止外部主 Agent 在单次调用中擅自换成未经审核的模型。实际使用的 provider/model 会进入签名 payload。

### OpenAI

```json
{
  "VERIFIER_PLATFORM_LLM_PROVIDER": "openai",
  "OPENAI_API_KEY": "YOUR_OPENAI_KEY",
  "VERIFIER_PLATFORM_LLM_MODEL": "gpt-5.6"
}
```

### DeepSeek

```json
{
  "VERIFIER_PLATFORM_LLM_PROVIDER": "deepseek",
  "DEEPSEEK_API_KEY": "YOUR_DEEPSEEK_KEY",
  "VERIFIER_PLATFORM_LLM_MODEL": "deepseek-chat"
}
```

### Anthropic

```json
{
  "VERIFIER_PLATFORM_LLM_PROVIDER": "anthropic",
  "ANTHROPIC_API_KEY": "YOUR_ANTHROPIC_KEY",
  "VERIFIER_PLATFORM_LLM_MODEL": "claude-sonnet-5"
}
```

### Gemini

```json
{
  "VERIFIER_PLATFORM_LLM_PROVIDER": "gemini",
  "GEMINI_API_KEY": "YOUR_GEMINI_KEY",
  "VERIFIER_PLATFORM_LLM_MODEL": "gemini-3.5-flash"
}
```

### 自定义 OpenAI-compatible 服务

```json
{
  "VERIFIER_PLATFORM_LLM_PROVIDER": "openai-compatible",
  "VERIFIER_PLATFORM_LLM_BASE_URL": "https://llm-gateway.example.com/v1",
  "VERIFIER_PLATFORM_LLM_API_KEY": "YOUR_GATEWAY_KEY",
  "VERIFIER_PLATFORM_LLM_MODEL": "bank-verifier-v2",
  "VERIFIER_PLATFORM_LLM_HEADERS_JSON": "{\"X-Tenant\":\"bank-a\"}"
}
```

`VERIFIER_PLATFORM_LLM_API_KEY` 是所有 provider 通用的覆盖值;未设置时会读取对应的 `OPENAI_API_KEY`、`DEEPSEEK_API_KEY`、`ANTHROPIC_API_KEY` 或 `GEMINI_API_KEY`。

不同 Host 可能将工具显示为 `mcp__doublecheck__verifier_platform_verify`,其中 `doublecheck` 是 Host 配置的服务器名称,实际工具名称仍是 `verifier_platform_verify`。

## 推荐的主 Agent 指令

```text
对于银行、金融、政策、利率、贷款、存款、外汇等要求高可信度的问题:
1. 先使用可用工具收集资料并形成回答;
2. 调用 verifier_platform_verify,将原问题、回答、完整可审计 reasoning、参考资料和工具执行记录传入;
3. policy.pass=false 时,不要把原回答当作已核查答案,应展示风险并建议人工复核;
4. policy.pass=true 时,向用户展示答案、verdict、confidence、问题清单和签名摘要;
5. `reasoning` 传入完整、可公开、可审计的推理记录;不要伪造主模型无法提供的隐藏思维链。
6. 需要最新资料时传入 `webSearch.enabled=true`;只有返回 `webSearch.performed=true` 才能声称已联网核查。
```

## `verifier_platform_verify` 输入

```json
{
  "question": "某类存款产品当前是否保本?",
  "answer": "主模型生成的完整回答",
  "reasoning": "必填:主模型提供的完整、可公开、可审计推理记录",
  "context": "可选:检索文本、监管资料或业务系统返回内容",
  "webSearch": {
    "enabled": true,
    "maxResults": 5,
    "query": "可选:自定义检索词;默认使用 question"
  },
  "requestId": "可选:外部链路 ID",
  "execution": {
    "provider": "deepseek-official",
    "model": "deepseek-v4-flash",
    "prompts": [
      { "role": "system", "content": "可选:需要纳入签名的公开 prompt" }
    ],
    "toolCalls": [
      {
        "id": "call-1",
        "name": "bank_product_lookup",
        "arguments": { "productId": "P001" },
        "result": { "principalProtected": true },
        "durationMs": 32
      }
    ],
    "metadata": {
      "sessionId": "public-audit-id"
    }
  }
}
```

`execution` 和 `context` 会被视为不可信证据,里面的文本不会被当作对 `verifier_platform` 的新指令。

`webSearch` 目前支持 DeepSeek verifier。它复用服务端已有的 DeepSeek API Key,通过 Anthropic-compatible Messages API 调用原生 `web_search_20250305`,不需要 Tavily、Brave 等额外搜索 Key。`maxResults` 只能为 `5` 或 `10`;DeepSeek 控制的是搜索使用次数,因此 MCP 会对最终结构化来源去重并截断到指定数量。


## 核查输出

```json
{
  "verification": {
    "confidence": 0.86,
    "verdict": "correct",
    "issues": [],
    "corrected_answer": "无",
    "reasoning": "核查依据摘要"
  },
  "policy": {
    "confidenceFloor": 0.7,
    "pass": true,
    "note": "核查结果达到可信度阈值,可以附带核查结论返回。"
  },
  "webSearch": {
    "requested": true,
    "performed": true,
    "status": "completed",
    "provider": "deepseek-native",
    "query": "某类存款产品当前是否保本?",
    "requestedResults": 5,
    "resultCount": 5,
    "searchedAt": "...",
    "sources": [
      { "title": "监管资料", "url": "https://...", "snippet": "..." }
    ]
  },
  "signature": {
    "alg": "Ed25519",
    "canonicalization": "RFC8785-JCS",
    "sigB64": "...",
    "payloadSha256": "...",
    "keyId": "sha256:..."
  },
  "runId": "...",
  "signedAt": "...",
  "publicKeyPem": "-----BEGIN PUBLIC KEY-----...",
  "payload": {},
  "tracePath": "..."
}
```

签名覆盖:问题、答案、完整可审计推理、上下文、执行记录、联网查询状态与来源、核查结果、策略结果、验证模型配置、验证提示词和原始输出。

## 配置

| 环境变量 | 默认值 | 说明 |
|---|---:|---|
| `VERIFIER_PLATFORM_LLM_PROVIDER` | `deepseek` | `openai`、`deepseek`、`anthropic`、`gemini`、`openai-compatible` |
| `VERIFIER_PLATFORM_LLM_BASE_URL` | 随 provider | 覆盖内置 API 根地址;自定义 provider 必填 |
| `VERIFIER_PLATFORM_LLM_API_KEY` | 无 | 通用 API Key,优先于 provider 专用环境变量 |
| `OPENAI_API_KEY` / `DEEPSEEK_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` | 无 | 对应 provider 的 API Key |
| `VERIFIER_PLATFORM_LLM_MODEL` | 随 provider | 可自由选择的核查模型名称 |
| `VERIFIER_PLATFORM_LLM_TIMEOUT_MS` | `120000` | 模型调用超时,毫秒 |
| `VERIFIER_PLATFORM_LLM_TEMPERATURE` | `0` | 核查模型温度 |
| `VERIFIER_PLATFORM_LLM_MAX_OUTPUT_TOKENS` | `2048` | 核查输出 token 上限 |
| `VERIFIER_PLATFORM_DEEPSEEK_THINKING` | `disabled` | DeepSeek V4 的思考模式;核验 JSON 建议关闭以降低延迟 |
| `VERIFIER_PLATFORM_LLM_HEADERS_JSON` | `{}` | 网关或自定义 provider 的附加字符串请求头 |
| `VERIFIER_PLATFORM_CONFIDENCE_FLOOR` | `0.70` | `policy.pass` 门槛 |
| `VERIFIER_PLATFORM_MAX_INPUT_CHARS` | `200000` | 系统提示词和核查输入的总字符上限 |
| `VERIFIER_PLATFORM_KB_ENABLED` | `false` | 是否启用 verifier_platform 服务端 UOB 本地知识库检索;外部 Agent 已完成检索时建议关闭 |
| `VERIFIER_PLATFORM_KB_PATH` | 项目内 UOB Markdown | 自定义知识库文件绝对路径 |
| `VERIFIER_PLATFORM_KB_MAX_RESULTS` | `5` | 最多提供给 verifier 的知识库章节数,只能为 `5` 或 `10` |
| `VERIFIER_PLATFORM_WEB_SEARCH_ENABLED` | `false` | 调用方省略 `webSearch` 时是否默认联网;可用单次 `enabled:false` 关闭 |
| `VERIFIER_PLATFORM_WEB_SEARCH_DEFAULT_RESULTS` | `5` | 默认联网结果数量,只能为 `5` 或 `10` |
| `VERIFIER_PLATFORM_WEB_SEARCH_TIMEOUT_MS` | `60000` | DeepSeek 原生搜索超时,毫秒 |
| `VERIFIER_PLATFORM_DEEPSEEK_SEARCH_BASE_URL` | `https://api.deepseek.com/anthropic/v1` | DeepSeek Anthropic-compatible 搜索基址 |
| `VERIFIER_PLATFORM_DEEPSEEK_SEARCH_MAX_TOKENS` | `4096` | 原生搜索辅助轮次的输出上限 |
| `VERIFIER_PLATFORM_DEEPSEEK_SEARCH_MAX_USES` | `5` | 单次请求允许的原生搜索最大使用次数 |
| `VERIFIER_PLATFORM_DATA_DIR` | `~/.verifier-platform` | 默认密钥和审计目录 |
| `VERIFIER_PLATFORM_PRIVATE_KEY_PATH` | 数据目录下私钥 | 自定义 Ed25519 私钥路径 |
| `VERIFIER_PLATFORM_PUBLIC_KEY_PATH` | 数据目录下公钥 | 自定义公钥路径 |
| `VERIFIER_PLATFORM_SIGNING_KEY_ID` | 无 | 固定 Ed25519 公钥指纹(`sha256:` + 64 位十六进制);指纹不匹配时服务拒绝启动/签名 |
| `VERIFIER_PLATFORM_AUDIT_PATH` | 数据目录下 `audit.jsonl` | 审计日志路径 |
| `VERIFIER_PLATFORM_TRACE_DIR` | 数据目录下 `traces` | 单次完整记录目录 |
| `VERIFIER_PLATFORM_AUDIT_ENABLED` | `true` | 是否追加 JSONL 审计 |
| `VERIFIER_PLATFORM_TRACE_ENABLED` | `true` | 是否保存单次 trace |
| `VERIFIER_PLATFORM_SYSTEM_PROMPT` | 内置金融核查提示词 | 自定义核查规则 |
| `VERIFIER_PLATFORM_HTTP_HOST` | `127.0.0.1` | HTTP 监听地址 |
| `VERIFIER_PLATFORM_HTTP_PORT` | `4200` | HTTP 监听端口 |
| `VERIFIER_PLATFORM_HTTP_MCP_PATH` | `/mcp` | MCP URL 路径 |
| `VERIFIER_PLATFORM_HTTP_BEARER_TOKEN` | 无 | 可选的 HTTP Bearer Token |
| `VERIFIER_PLATFORM_HTTP_ALLOWED_HOSTS` | `localhost,127.0.0.1,[::1]` | Host/Origin 白名单 |

## 离线验签

### 固定签名密钥

签名密钥不会在每次请求时生成。服务会从 `VERIFIER_PLATFORM_PRIVATE_KEY_PATH` 和
`VERIFIER_PLATFORM_PUBLIC_KEY_PATH` 指向的文件读取同一对 Ed25519 密钥;只要这两个文件和数据目录持久化,重启不会换钥。
生产环境建议额外设置 `VERIFIER_PLATFORM_SIGNING_KEY_ID`,把公钥指纹锁定。指纹不匹配时服务会拒绝启动/签名,防止误删密钥后静默换钥。

当前初始化公钥的指纹为:

```text
sha256:e3d2b02b04e96bb598dab2268ea0a62af17869423c38b6f00361f6ba3e848be1
```

对应的非敏感公钥文件是 [`docs/verifier-platform-public-key.pem`](docs/verifier-platform-public-key.pem)。私钥绝不能提交 Git;部署服务器时将受保护的 `ed25519-private.pem` 放到服务器,并设置相同的 `VERIFIER_PLATFORM_SIGNING_KEY_ID`。外部 Agent Skill 应固定保存上面的指纹或公钥,不要信任响应中临时返回的公钥作为信任根。

通过 MCP 工具调用 `verifier_platform_verify_signature`,或者使用 CLI:

```sh
node dist/cli.js verify-record ~/.verifier-platform/traces/<runId>.json
```

成功时输出:

```json
{
  "valid": true,
  "payloadSha256": "...",
  "hashMatches": true,
  "keyId": "sha256:...",
  "alg": "Ed25519",
  "canonicalization": "RFC8785-JCS"
}
```

第三方实现必须对 `payload` 使用 RFC 8785 JCS 序列化后的 UTF-8 字节验 Ed25519,而不是直接使用本地 JSON 序列化顺序。

## 开发和发布检查

```sh
npm run check
npm pack --dry-run
```

测试覆盖核查结果解析、提示词隔离、模型适配器、策略边界、签名防篡改、审计落盘和真实 MCP 工具发现/调用。

## 从 DSH 插件迁移

| DSH 插件能力 | MCP v1 对应能力 |
|---|---|
| `doublecheck_verify` | `verifier_platform_verify` |
| `doublecheck_status` | `verifier_platform_status` |
| DSH `ctx.llm.stream` | 可选择的 OpenAI/DeepSeek/Anthropic/Gemini/custom provider adapter |
| DSH 工具 `render` | MCP `content` + `structuredContent` |
| `$DSH_HOME/doublecheck` | `~/.verifier-platform` 或自定义目录 |
| 普通 `JSON.stringify` 签名 | RFC 8785 JCS + Ed25519 |
| DSH 会话工具轨迹 | 调用方显式传入 `execution` |

原插件的 `doublecheck_answer` 没有放入 MCP v1:主 Agent 已经拥有全部业务工具和回答能力,MCP 只保持独立核查职责。未来如需“一步作答并核查”,可在不改变现有验签协议的情况下增加编排工具。

## 生产注意事项

- 模型A和 `verifier_platform` 应尽量使用不同模型或不同提供商,降低相关性错误。
- 用有标准答案的数据集校准 `confidenceFloor`,不要直接把模型自报置信度当作真实正确率。
- `context`、prompt、工具参数和结果会进入签名 payload 与审计文件;接入前应完成敏感字段脱敏。
- 单机文件密钥适合开发和受控部署。生产环境建议替换为 KMS/HSM/内部签名服务。
- `audit.jsonl` 是追加式应用日志,不等同于不可删除存储;高合规场景应同步到 WORM 或集中审计系统。
- HTTP 模式默认仅用于本机测试。跨机器或公网部署应增加 TLS、认证、租户隔离、限流和集中密钥管理。

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: full verification with optional web search, offline signature validation, and read-only status/config inspection. The only potential confusion between verify and verify_signature is resolved by the descriptions and by the fact that one calls models while the other validates cryptographic signatures.

Naming Consistency4/5

All tool names share the verifier_platform_ prefix and use snake_case, making the namespace predictable. Minor inconsistency: status is a noun rather than a verb, so the naming pattern is not consistently verb_noun.

Tool Count5/5

Three tools is a tight, well-scoped set for a verification-focused server. Each tool covers a necessary role: perform verification, verify output integrity, and inspect server status.

Completeness5/5

The server covers the full lifecycle of a double-check workflow: generate a signed verification result, independently validate that result's signature, and query platform status/config. No obvious missing operation is implied by the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues