Skip to main content
Glama
README.md
# tai-mcp

`tai-mcp` 是一个可独立部署的 MCP 服务,把 `tai-openapi` 的 OpenAI 兼容图片生成和编辑接口
封装为 MCP 工具。MCP 客户端调用 `generate_image` 或 `edit_image` 后会直接收到标准 MCP
`ImageContent`,不依赖 `tai-openapi` 返回的本地图片 URL。

## 功能

- `generate_image`:调用 `POST /v1/images/generations`,返回一张可直接显示的图片及元数据。
- `edit_image`:调用 `POST /v1/images/edits`,支持多张输入图、mask、size 和模型专属参数。
- `get_image_model_capabilities`:调用
  `GET /v1/image-models/{model}/capabilities`,查询 edit 模型实际接受的输入。
- `list_image_models`:调用 `GET /v1/models`,查看当前上游启用的模型。
- Streamable HTTP:MCP 地址为 `http(s)://<host>:8001/mcp`。
- 无状态 JSON 响应,适合反向代理和横向扩容。
- MCP 入口支持独立 Bearer Token;上游 API Key 不暴露给 MCP 客户端。
- `/healthz` 检查本服务,`/readyz` 同时检查 `tai-openapi`。

## 与 tai-openapi 的接口契约

本项目使用官方 [OpenAI Python SDK](https://github.com/openai/openai-python) 的
`AsyncOpenAI` 与上游通信。图片生成、图片编辑和模型列表分别直接使用
`client.images.generate()`、`client.images.edit()` 和 `client.models.list()`;
`get_image_model_capabilities` 与 `/readyz` 所需的 capabilities、health 接口属于
`tai-openapi` 扩展,通过同一个官方客户端的底层 `get()` 方法访问。

`seed` 和 `parameters` 不是 OpenAI Image API 的标准字段,分别通过 SDK 的
`extra_body` 合并到 generation JSON 和 edits multipart 请求中。多图编辑采用官方 SDK
生成的 `image[]` 字段,`tai-openapi` 会将其规范化为内部的 `image` 输入。

`generate_image` 固定向上游发送:

```json
{
  "model": "tai-text-to-image",
  "prompt": "A quiet library at night",
  "size": "1024x1024",
  "seed": 42,
  "response_format": "b64_json"
}
```

`size` 和 `seed` 未传时不会出现在请求中。固定使用 `b64_json` 是为了让远程 MCP
客户端直接获得图片,并避免公网部署时误返回仅服务器本机可访问的图片 URL。
一次 MCP 调用只发起一次上游生图请求;超时或网络中断不会自动重试,以免重复生成。

编辑前应先调用 `get_image_model_capabilities(model)`。返回结果会明确说明:

- `input.min_images` / `input.max_images`:`images` 所需的图片数量。
- `input.mask`:`forbidden`、`optional` 或 `required`。
- `input.size`:是否允许传 `size`。
- `input.prompt_required`:是否需要 prompt 字段。
- `parameters_schema`:`parameters` 必须满足的 JSON Schema。
- `parameters_example`:该模型扩展参数的可用示例。

`edit_image.images` 和 `mask` 接受 PNG、JPEG、WebP 的纯 base64 字符串或
`data:image/png;base64,...` 形式的 Data URL。MCP 服务把它们解码后作为文件上传给上游;
`response_format` 固定为 `b64_json`。例如 `tai-image-to-image` 的典型参数为:

```json
{
  "prompt": "Make the bodywork brighter",
  "images": ["data:image/png;base64,<base64-data>"],
  "model": "tai-image-to-image",
  "parameters": {"denoise": 0.55}
}
```

图片顺序会原样保留,具体每个位置代表原图还是参考图由所选模型的 capabilities 决定。
`seed` 等 edit 专属字段放在 `parameters` 中,不能作为顶层参数传入。

## 本地运行

要求 Python 3.12+ 和 [uv](https://docs.astral.sh/uv/)。

```powershell
Copy-Item .env.example .env
# 编辑 .env,至少填写 TAI_OPENAPI_API_KEY
uv sync --extra dev
uv run python -m tai_mcp
```

默认监听 `0.0.0.0:8001`:

- MCP:`http://127.0.0.1:8001/mcp`
- 存活检查:`http://127.0.0.1:8001/healthz`
- 就绪检查:`http://127.0.0.1:8001/readyz`

### stdio 模式

本地 Codex 可以通过 stdio 按需启动 MCP,无需监听端口:

```powershell
uv run python -m tai_mcp --transport stdio
```

stdio 模式会继续从项目目录的 `.env` 读取上游地址、API Key 和默认模型;
`MCP_HOST`、`MCP_PORT`、`MCP_API_KEY` 和 CORS 配置在该模式下不生效。

注册到 Codex 时使用项目启动脚本。脚本会先切换到项目目录,确保 `.env` 能被读取:

```powershell
codex mcp add tai-images -- `
  powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass `
  -File C:\Users\xiuzhenpeng\Desktop\vscode-dev\tai-mcp\run-stdio.ps1
```

不要在 `codex mcp add --env` 中直接写 API Key;该值会进入 Codex 配置。启动脚本让密钥
继续只保存在项目的 `.env` 中。

用 MCP Inspector 验证:

```powershell
npx -y @modelcontextprotocol/inspector
```

在 Inspector 中把地址设为 `http://127.0.0.1:8001/mcp`。如果配置了
`MCP_API_KEY`,添加请求头:

```text
Authorization: Bearer <MCP_API_KEY>
```

## 配置

| 环境变量 | 默认值 | 说明 |
| --- | --- | --- |
| `TAI_OPENAPI_BASE_URL` | `http://127.0.0.1:8000/v1` | 上游地址,必须以 `/v1` 结尾 |
| `TAI_OPENAPI_API_KEY` | 无 | 上游 `tai-openapi` 原始 Bearer Token,必填 |
| `TAI_IMAGE_MODEL` | `tai-text-to-image` | `generate_image` 的默认模型 |
| `TAI_IMAGE_EDIT_MODEL` | `tai-image-to-image` | `edit_image` 的默认模型 |
| `MCP_API_KEY` | 空 | MCP 客户端 Bearer Token;公网部署必须设置 |
| `MCP_HOST` | `0.0.0.0` | 监听地址 |
| `MCP_PORT` | `8001` | 监听端口 |
| `MCP_REQUEST_TIMEOUT_SECONDS` | `330` | 包含生图时间的上游总超时 |
| `MCP_CONNECT_TIMEOUT_SECONDS` | `10` | 上游连接超时 |
| `MCP_MAX_IMAGE_BYTES` | `20971520` | 允许回传的最大解码图片大小 |
| `MCP_CORS_ORIGINS` | 空 | 浏览器客户端的精确 Origin,逗号分隔 |

工具的 `model` 参数可以覆盖默认模型,但仍受上游 API Key 权限和模型配置约束。

### 生成 MCP_API_KEY

`MCP_API_KEY` 是客户端访问 `/mcp` 时使用的 Bearer Token。公网部署必须使用独立的
强随机值,不要复用 `TAI_OPENAPI_API_KEY`。

在项目目录中运行以下命令,生成一个基于 48 字节随机数据的 URL 安全密钥:

```powershell
uv run python -c "import secrets; print(secrets.token_urlsafe(48))"
```

如果系统已经安装 OpenSSL,也可以生成 32 字节随机值并输出为 64 位十六进制:

```bash
openssl rand -hex 32
```

复制命令输出的完整一行,写入 `.env`:

```dotenv
MCP_API_KEY=这里替换为刚生成的随机密钥
```

不要把真实密钥写入 `.env.example`、提交到 Git 或粘贴到公开日志。修改密钥后需要重启
容器:

```powershell
docker compose up -d --force-recreate
```

MCP 客户端访问 `https://<你的域名>/mcp` 时需要发送:

```text
Authorization: Bearer <MCP_API_KEY>
```

## Docker

先创建配置:

```powershell
Copy-Item .env.example .env
```

如果 `tai-openapi` 跑在 Docker 宿主机的 8000 端口,把 `.env` 改为:

```dotenv
TAI_OPENAPI_BASE_URL=http://host.docker.internal:8000/v1
```

然后启动:

```powershell
docker compose up -d --build
docker compose ps
```

如果两个服务位于同一个 Compose 网络,建议直接使用服务名,例如
`TAI_OPENAPI_BASE_URL=http://tai-openapi:8000/v1`。

## 公网发布检查清单

1. 为 `MCP_API_KEY` 生成至少 32 字节的随机值,不要和上游 API Key 共用。
2. 只通过 HTTPS 暴露 MCP;可参考 `deploy/nginx.conf.example`。
3. 防火墙只开放 80/443,不直接暴露 8001;上游 8000 更不应直接暴露。
4. Nginx 的读写超时要大于 `tai-openapi` 的最大生图时间。
5. 将 `.env` 保留在服务器,不提交到 Git。
6. 若启用 CORS,只配置实际 MCP Web 客户端的 Origin,不要在公网使用 `*`。
7. 发布后验证 `/healthz`、`/readyz`、工具列表以及一次真实生图。

静态 Bearer Token 适合私有客户端或能够配置自定义请求头的 MCP Host。如果需要面向
不受控第三方用户开放,应在反向代理或独立授权服务中实现 MCP OAuth 2.1,而不是共享
同一个静态 Token。

## 测试与质量检查

```powershell
uv run --extra dev pytest
uv run --extra dev ruff check .
uv run --extra dev ruff format --check .
```

TDQS

A4.2/5.0

Scored across 4 tools

Disambiguation5/5

Each tool targets a distinct operation: generate_image creates new images, edit_image modifies existing ones, list_image_models enumerates available models, and get_image_model_capabilities inspects edit-model specifics. There is no overlap or ambiguity between these actions.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern: generate_image, edit_image, list_image_models, get_image_model_capabilities. The verb and noun are clear, and the style is uniform throughout.

Tool Count5/5

Four tools is well-scoped for an image generation and editing server. Each tool serves a distinct and necessary function, with no missing or redundant entries.

Completeness4/5

The core lifecycle is covered: generate, edit, list models, and inspect model capabilities. A minor gap is that capabilities are only available for edit models, not for generation models, which could be useful when choosing sizes or parameters for generate_image.