#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";
const API_KEY = process.env.MODELSCOPE_API_KEY || "";
const BASE_URL = "https://api-inference.modelscope.cn/";
const DEFAULT_MODEL = "Qwen/Qwen-Image";
interface ImageGenerationParams {
model: string;
prompt: string;
negative_prompt?: string;
size?: string;
seed?: number;
steps?: number;
guidance?: number;
image_url?: string;
}
interface TaskResponse {
task_id: string;
}
interface TaskStatusResponse {
task_status: "PENDING" | "RUNNING" | "SUCCEED" | "FAILED";
output_images?: string[];
error_message?: string;
}
// 生成图片的异步函数(内部处理轮询,对外同步)
async function generateImage(params: ImageGenerationParams): Promise<string> {
if (!API_KEY) {
throw new Error("MODELSCOPE_API_KEY environment variable is not set");
}
const headers = {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// 提交图片生成任务
const response = await axios.post<TaskResponse>(
`${BASE_URL}v1/images/generations`,
params,
{
headers: {
...headers,
"X-ModelScope-Async-Mode": "true",
},
}
);
const taskId = response.data.task_id;
// 轮询任务状态,直到完成
while (true) {
await new Promise((resolve) => setTimeout(resolve, 3000)); // 等待 3 秒
const statusResponse = await axios.get<TaskStatusResponse>(
`${BASE_URL}v1/tasks/${taskId}`,
{
headers: {
...headers,
"X-ModelScope-Task-Type": "image_generation",
},
}
);
const status = statusResponse.data;
if (status.task_status === "SUCCEED") {
if (status.output_images && status.output_images.length > 0) {
return status.output_images[0];
} else {
throw new Error("Image generation succeeded but no output images found");
}
} else if (status.task_status === "FAILED") {
throw new Error(
`Image generation failed: ${status.error_message || "Unknown error"}`
);
}
// 继续轮询(PENDING 或 RUNNING 状态)
}
}
// 创建 MCP 服务器
const server = new Server(
{
name: "mcp-modelscope-image",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 定义工具列表
const TOOLS: Tool[] = [
{
name: "generate_image",
description:
"Generate an image using ModelScope image generation models. The tool will wait until the image is generated and return the image URL.",
inputSchema: {
type: "object",
properties: {
model: {
type: "string",
description:
"Model to use for image generation. Available options: 'Qwen/Qwen-Image' (default), 'Tongyi-MAI/Z-Image-Turbo'",
enum: ["Qwen/Qwen-Image", "Tongyi-MAI/Z-Image-Turbo"],
default: "Qwen/Qwen-Image",
},
prompt: {
type: "string",
description:
"Positive prompt for image generation (English recommended). Max length: 2000 characters",
},
negative_prompt: {
type: "string",
description:
"Negative prompt to specify what to avoid in the image. Max length: 2000 characters",
},
size: {
type: "string",
description:
"Image resolution size (e.g., '1024x1024'). Range for Qwen-Image: [64x64, 1664x1664]. Default: 1024x1024",
},
seed: {
type: "number",
description: "Random seed for reproducibility. Range: [0, 2^31-1]",
},
steps: {
type: "number",
description: "Number of sampling steps. Range: [1, 100]",
},
guidance: {
type: "number",
description: "Guidance scale for prompt adherence. Range: [1.5, 20]",
},
image_url: {
type: "string",
description:
"URL of the image to edit (only for image editing mode). Must be publicly accessible",
},
},
required: ["prompt"],
},
},
];
// 处理 list_tools 请求
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: TOOLS,
};
});
// 处理 call_tool 请求
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "generate_image") {
try {
if (!args) {
throw new Error("Missing arguments");
}
const params: ImageGenerationParams = {
model: (args.model as string) || DEFAULT_MODEL,
prompt: args.prompt as string,
};
// 添加可选参数
if (args.negative_prompt) {
params.negative_prompt = args.negative_prompt as string;
}
if (args.size) {
params.size = args.size as string;
}
if (args.seed !== undefined) {
params.seed = args.seed as number;
}
if (args.steps !== undefined) {
params.steps = args.steps as number;
}
if (args.guidance !== undefined) {
params.guidance = args.guidance as number;
}
if (args.image_url) {
params.image_url = args.image_url as string;
}
const imageUrl = await generateImage(params);
return {
content: [
{
type: "text",
text: `Image generated successfully using ${params.model}!\n\nImage URL: ${imageUrl}\n\nYou can download or view the image at the URL above.`,
},
],
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : "Unknown error occurred";
return {
content: [
{
type: "text",
text: `Error generating image: ${errorMessage}`,
},
],
isError: true,
};
}
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("ModelScope Image Generation MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});