Skip to main content
Glama
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 通过密钥管理服务注入,避免明文落盘