mcp-cogview
README.md
# mcp-cogview
基于 TypeScript / Node.js 的 **Streamable HTTP + SSE** MCP 服务,对接智谱 [CogView](https://open.bigmodel.cn/dev/api/normal_model/Image-generation-api) 文生图模型,同时内置 `live` 存活接口用于健康探针。
## 快速开始
```bash
# 1. 安装依赖
npm install
# 2. 配置 API Key(任选其一填到 .env)
# 也可以直接在 .env 里填 ZHIPU_API_KEY=sk-xxxx
cp .env.example .env
# Windows CMD: notepad .env
# Windows PowerShell: code .env (或任意编辑器)
# 3. 启动
npm start # 加载 .env,启动 http://127.0.0.1:3000
npm run start:mock # 强制 mock 模式(即使填了 Key 也不调用真实 API)
npm run start:real # 强制真实模式(缺 Key 时返回 401 而不是 mock)
npm run verify # 端到端验证(自身强制 mock,29 项断言)
npm run typecheck # 类型检查
npm run dev # 热重载开发
```
> **Windows 用户**:所有命令都通过 `npm` 触发,已用 `cross-env` + Node 内置 `--env-file=.env` 实现跨平台,**无需手动 `set` / `$env:`**。把 API Key 直接写到 `.env` 文件即可。
>
> 未设置 API Key 时自动进入 **mock 模式**:调用 `generate_image` 会返回基于 prompt 派生的 placehold.co 占位图,便于本地/无密钥环境跑通端到端验证。
## 目录结构
```
src/
├── framework/ # 与业务无关的框架层(沿用 starter)
│ ├── types.ts # McpModule / ModuleContext / ServerConfig 等契约
│ ├── logger.ts # JSON 行结构化日志
│ ├── event-store.ts # 内存事件存储(Last-Event-ID 断线续传)
│ ├── app.ts # 核心:模块注册表 + 会话管理 + 传输层装配
│ └── http.ts # Express 路由:MCP 端点 + live/ready 探针 + CORS
├── modules/
│ ├── live.ts # live 健康检查模块(tool + resource + prompt)
│ ├── cogview-client.ts # 智谱 CogView 文生图 API 客户端(fetch + 异步轮询)
│ └── cogview.ts # CogView MCP 模块:generate_image 工具 + 模型清单资源
├── config.ts # 服务配置 + CogView 模块配置
├── server.ts # 组装与启动
└── index.ts # 进程入口 + 信号处理
scripts/verify.ts # 端到端验证脚本(mock 模式自动启用)
```
## HTTP 接口
| 方法 | 路径 | 说明 |
| -------- | ------------ | ----------------------------------------- |
| `POST` | `/mcp` | Streamable HTTP 请求入口(JSON-RPC) |
| `GET` | `/mcp` | 独立 SSE 通知流,支持 `Last-Event-ID` 续传 |
| `DELETE` | `/mcp` | 终止会话 |
| `GET` | `/sse` | 旧版 HTTP+SSE 传输(2024-11-05,兼容用) |
| `POST` | `/messages` | 旧版传输的消息回传通道 |
| `GET` | `/live` | **存活探针**(k8s livenessProbe) |
| `GET` | `/ready` | 就绪探针(k8s readinessProbe) |
| `GET` | `/` | 服务元信息 |
`GET /live` 返回:
```json
{
"status": "live",
"service": "mcp-cogview",
"version": "0.1.0",
"protocol": "streamable-http",
"uptimeMs": 16048,
"startedAt": "2026-08-05T01:43:00.213Z",
"pid": 8780,
"sessions": { "active": 0, "created": 1 },
"requestsTotal": 7,
"toolCalls": 2,
"memory": { "rssMB": 79.1, "heapUsedMB": 22.4, "heapTotalMB": 24 },
"modules": [
{ "name": "live", "version": "1.0.0" },
{ "name": "cogview", "version": "1.0.0" }
],
"endpoints": { "mcp": "/mcp", "live": "/live", "...": "..." }
}
```
## MCP 能力
| 类型 | 名称 | 说明 |
| -------- | ----------------------- | ---- |
| tool | `live` | 返回完整运行期状态(带 `outputSchema` 结构化输出) |
| tool | `live_ping` | 最轻量连通性探测,回显 + 服务端时间戳 |
| tool | `live_watch` | 持续推送心跳(日志 + 进度通知),验证 SSE 下行流 |
| tool | `generate_image` | **同步文生图**:调用 CogView,返回图片 URL 或 base64 |
| tool | `generate_image_async` | **异步文生图**:提交任务并轮询,期间通过 SSE 推送进度 |
| resource | `live://status` | 以 JSON 资源形式暴露服务状态 |
| resource | `cogview://models` | 模型清单与尺寸白名单 |
| prompt | `live_diagnose` | 健康诊断提示词模板 |
| prompt | `cogview_prompt_polish` | 把用户需求改写为 CogView 文生图 prompt 的提示词 |
## CogView 文生图
`generate_image` 输入:
| 参数 | 类型 | 默认 | 说明 |
| ---- | ---- | ---- | ---- |
| `prompt` | string | 必填 | 文本提示词(≤ 1000 字符,中英文均可) |
| `model` | enum | `cogview-3-plus` | `cogview-3` / `cogview-3-plus` |
| `size` | enum | `1024x1024` | `1024x1024` / `768x1344` / `864x1152` / `1344x768` / `1152x864` |
| `userId` | string | — | 业务侧用户标识 |
| `responseFormat` | enum | `url` | `url` 返回可访问链接;`b64_json` 返回 base64 |
同步调用返回:
```json
{
"created": 1754361600,
"model": "cogview-3-plus",
"size": "1024x1024",
"images": [
{ "url": "https://...", "b64Bytes": null, "mimeType": "image/png" }
],
"usage": { "promptTokens": 12, "completionTokens": null, "totalTokens": null },
"mock": false
}
```
`generate_image_async` 的额外行为:
- 提交任务后立即返回 `request_id` 并不阻塞(异步版本)。
- 内部按 `COGVIEW_POLL_INTERVAL_MS` 轮询,直到任务 `SUCCESS` 或 `FAILURE`,或超过 `COGVIEW_POLL_TIMEOUT_MS`。
- 轮询过程通过 SSE 推送 `notifications/progress` 与 `notifications/message`,客户端可在 UI 展示进度。
- 最终结构化输出包含 `requestId / elapsedMs / polls / taskStatus / images`。
## 配置(环境变量)
| 变量 | 默认值 | 说明 |
| -------------------------- | ------------------------------------------------------------ | ---- |
| `PORT` | `3000` | 监听端口 |
| `HOST` | `127.0.0.1` | 监听地址 |
| `MCP_NAME` / `MCP_VERSION` | `mcp-cogview` / `0.1.0` | initialize 中上报的服务标识 |
| `MCP_PATH` | `/mcp` | Streamable HTTP 端点 |
| `MCP_LIVE_PATH` | `/live` | 存活探针路径 |
| `MCP_STATELESS` | `false` | 无状态模式(每请求独立 server) |
| `MCP_SESSION_IDLE_MS` | `600000` | 会话空闲回收阈值,0 表示不回收 |
| `MCP_ALLOWED_ORIGINS` | — | 逗号分隔白名单,配置后开启 DNS 重绑定防护 |
| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
| `ZHIPU_API_KEY` | — | 智谱 API Key(也接受 `GLM_API_KEY` / `COGVIEW_API_KEY`) |
| `COGVIEW_ENDPOINT` | `https://open.bigmodel.cn/api/paas/v4/images/generations` | 文生图 API 端点 |
| `COGVIEW_MODEL` | `cogview-3-plus` | 默认模型 |
| `COGVIEW_SIZE` | `1024x1024` | 默认尺寸 |
| `COGVIEW_RESPONSE_FORMAT` | `url` | 默认返回格式 |
| `COGVIEW_POLL_INTERVAL_MS` | `2000` | 异步任务轮询间隔 |
| `COGVIEW_POLL_TIMEOUT_MS` | `120000` | 异步任务最大等待时间 |
| `COGVIEW_REQUEST_TIMEOUT_MS` | `60000` | 单次 HTTP 请求超时 |
| `COGVIEW_MOCK` | 未设置 API Key 时自动为 `true` | 启用后调用不会访问智谱 API,返回占位响应 |
## 在 Claude Code 中接入
```bash
claude mcp add --transport http cogview http://127.0.0.1:3000/mcp
```
仓库根目录的 `.mcp.json` 已声明 `cogview` 入口,Claude Code 启动即可自动发现。
## curl 手动验证
```bash
# 0. 启动服务(写入 .env 后,无需手动 set/export)
# .env 里有 ZHIPU_API_KEY=sk-xxxxxxxx
npm start &
# 1. 存活探针
curl -s http://127.0.0.1:3000/live | jq
# 2. initialize(从响应头取 Mcp-Session-Id)
SID=$(curl -s -D - -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
| awk -F': ' 'tolower($1)=="mcp-session-id"{print $2}' | tr -d '\r')
curl -s -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: $SID" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# 3. 列出可用模型
curl -s -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: $SID" \
-d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"cogview://models"}}'
# 4. 文生图
curl -s -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: $SID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"generate_image","arguments":{"prompt":"赛博朋克少女,霓虹背景","size":"1024x1024"}}}'
```
> Windows CMD/PowerShell 下 `SID=$(...)` 不工作,可以改用:
> ```powershell
> # PowerShell
> $sid = (curl -s -D - -X POST http://127.0.0.1:3000/mcp `
> -H 'Content-Type: application/json' `
> -H 'Accept: application/json, text/event-stream' `
> -d '{...}' `
> | Select-String -Pattern '(?im)^Mcp-Session-Id:\s*(\S+)' `
> | ForEach-Object { $_.Matches[0].Groups[1].Value })
> $env:SID = $sid
> ```
> 也可以直接用项目自带的 `npm run verify` 做端到端验证,无需手动 curl。
## 扩展新模块
框架以「模块」为扩展单元。每个新会话都会用一个全新的 `McpServer` 实例回调一次 `register`:
```ts
// src/modules/echo.ts
import { z } from 'zod';
import type { McpModule } from '../framework/types.js';
export const echoModule: McpModule = {
name: 'echo',
version: '1.0.0',
register(server, ctx) {
server.registerTool(
'echo',
{ description: '回显输入', inputSchema: { text: z.string() } },
async ({ text }) => {
ctx.markToolCall();
return { content: [{ type: 'text', text }] };
}
);
}
};
```
在 [src/server.ts](src/server.ts) 里 `app.use(echoModule)` 即可生效。
## 生产化清单
- [ ] `InMemoryEventStore` → Redis / 持久化实现(多实例部署必需)
- [ ] 会话状态外置,或在网关层做会话粘性
- [ ] 接入 OAuth(SDK 提供 `server/auth` 模块)
- [ ] 结构化日志接入采集链路,`/live` `/ready` 挂到 k8s 探针
- [ ] 智谱 API Key 通过密钥管理服务注入,避免明文落盘This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues