siliconflow-image-mcp 1.0.4 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,5 @@
1
- #!/usr/bin/env node
2
- // SiliconFlow Image MCP - Universal launcher for all platforms
3
- // Works on Windows (via npm wrapper), Linux, macOS
4
-
5
- import { spawn } from 'child_process';
6
- import { fileURLToPath } from 'url';
7
- import { dirname, join } from 'path';
8
- import { existsSync } from 'fs';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
-
13
- // Find dist/index.js relative to this file
14
- const distPath = join(__dirname, '..', 'dist', 'index.js');
15
-
16
- if (!existsSync(distPath)) {
17
- console.error('Error: Could not find siliconflow-image-mcp dist/index.js');
18
- console.error('Searched in:', distPath);
19
- process.exit(1);
20
- }
21
-
22
- // Run the main program
23
- const child = spawn(process.execPath, [distPath, ...process.argv.slice(2)], {
24
- stdio: 'inherit',
25
- windowsHide: true
26
- });
27
-
28
- child.on('exit', (code) => {
29
- process.exit(code);
30
- });
1
+ #!/bin/sh
2
+ # Cross-platform launcher - works on Linux, macOS, and Unix-like systems
3
+ # On Windows, npm will use the .cmd wrapper instead
4
+ DIR="$(dirname "$0")"
5
+ exec node "$DIR/../dist/index.js" "$@"
@@ -0,0 +1,5 @@
1
+ @echo off
2
+ REM Windows launcher for siliconflow-image-mcp
3
+ REM This wrapper is used by npm on Windows systems
4
+ setlocal
5
+ node "%~dp0..\dist\index.js" %*
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ var SiliconFlowService = class {
30
30
  method: "POST",
31
31
  headers: {
32
32
  "Content-Type": "application/json",
33
- "Authorization": `Bearer ${this.apiKey}`
33
+ Authorization: `Bearer ${this.apiKey}`
34
34
  },
35
35
  body: JSON.stringify(body)
36
36
  });
@@ -55,7 +55,7 @@ var SiliconFlowService = class {
55
55
  const response = await fetch(url.toString(), {
56
56
  method: "GET",
57
57
  headers: {
58
- "Authorization": `Bearer ${this.apiKey}`
58
+ Authorization: `Bearer ${this.apiKey}`
59
59
  }
60
60
  });
61
61
  if (!response.ok) {
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/services/siliconflow.ts", "../src/types/index.ts", "../src/utils/file.ts", "../src/tools/generate.ts", "../src/tools/edit.ts", "../src/tools/list-models.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n/**\n * SiliconFlow Image MCP Server\n *\n * A Model Context Protocol server for image generation and editing using SiliconFlow\n * Optimized for China users\n */\n\n// Handle build flag for compilation\nif (process.argv.includes('--build')) {\n console.log('\u2705 Project structure is ready. Use \"npm start\" to run the server.');\n console.log('\uD83D\uDCDD Note: This project uses tsx for runtime execution.');\n process.exit(0);\n}\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { SiliconFlowService } from \"./services/siliconflow.js\";\nimport { createGenerateImageTool } from \"./tools/generate.js\";\nimport { createEditImageTool } from \"./tools/edit.js\";\nimport { createListModelsTool } from \"./tools/list-models.js\";\n\n// Environment variable check\nconst SILICONFLOW_API_KEY = process.env.SILICONFLOW_API_KEY;\nconst MOCK_MODE = process.env.SILICONFLOW_MOCK === \"true\";\n\nif (!SILICONFLOW_API_KEY && !MOCK_MODE) {\n console.error(\"\u274C Error: SILICONFLOW_API_KEY environment variable is required\");\n console.error(\"\\nTo use this MCP server:\");\n console.error(\"1. Get your API key from https://siliconflow.cn\");\n console.error(\"2. Set it as an environment variable:\");\n console.error(\" export SILICONFLOW_API_KEY=your-api-key-here\");\n console.error(\"\\nFor Claude Desktop, add to your config.json:\");\n console.error(`{\n \"mcpServers\": {\n \"siliconflow-image-mcp\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"siliconflow-image-mcp\"],\n \"env\": {\n \"SILICONFLOW_API_KEY\": \"your-api-key-here\"\n }\n }\n }\n}`);\n console.error(\"\\nOptional configurations:\");\n console.error(\" export SILICONFLOW_MOCK=true # Use mock mode for testing\");\n console.error(\" export SILICONFLOW_IMAGE_DIR=/path # Custom directory for saved images\");\n process.exit(1);\n}\n\n// Initialize services and server\nasync function main() {\n console.error(\"\uD83D\uDE80 Starting SiliconFlow Image MCP Server...\");\n\n try {\n // Initialize SiliconFlow service\n let siliconFlowService;\n if (MOCK_MODE) {\n console.error(\"\u26A0\uFE0F Running in MOCK mode - API calls will be simulated\");\n // Create a mock service that returns fake data\n siliconFlowService = {\n generateImage: async () => [{ data: \"mock-base64-data\", mimeType: \"image/png\" }],\n editImage: async () => ({ data: \"mock-base64-data\", mimeType: \"image/png\" }),\n listImageModels: async () => [\n { id: \"black-forest-labs/FLUX.1-dev\", name: \"FLUX.1-dev\", description: \"Mock model\", output_modalities: [\"image\"] }\n ],\n testConnection: async () => true\n };\n } else {\n siliconFlowService = new SiliconFlowService(SILICONFLOW_API_KEY);\n\n // Test connection\n const isConnected = await siliconFlowService.testConnection();\n if (!isConnected) {\n console.error(\"\u274C Failed to connect to SiliconFlow. Please check your API key.\");\n process.exit(1);\n }\n console.error(\"\u2705 Connected to SiliconFlow successfully\");\n }\n\n // Create MCP server\n const server = new McpServer({\n name: \"siliconflow-image-mcp\",\n version: \"1.0.0\",\n capabilities: {\n tools: {\n listChanged: true,\n },\n },\n });\n\n // Register tools\n const generateTool = createGenerateImageTool(siliconFlowService);\n const editTool = createEditImageTool(siliconFlowService);\n const listModelsTool = createListModelsTool(siliconFlowService);\n\n server.registerTool(\n generateTool.name,\n {\n description: generateTool.description,\n inputSchema: generateTool.inputSchema,\n },\n generateTool.handler\n );\n\n server.registerTool(\n editTool.name,\n {\n description: editTool.description,\n inputSchema: editTool.inputSchema,\n },\n editTool.handler\n );\n\n server.registerTool(\n listModelsTool.name,\n {\n description: listModelsTool.description,\n inputSchema: listModelsTool.inputSchema,\n },\n listModelsTool.handler\n );\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"\u2705 SiliconFlow Image MCP Server is running\");\n console.error(\"\uD83D\uDCCB Available tools: generate_image, edit_image, list_image_models\");\n console.error(\"\uD83D\uDCDD Waiting for requests...\");\n\n // Handle graceful shutdown\n process.on(\"SIGINT\", async () => {\n console.error(\"\\n\uD83D\uDED1 Shutting down gracefully...\");\n await server.close();\n process.exit(0);\n });\n\n } catch (error) {\n console.error(\"\u274C Failed to start server:\", error instanceof Error ? error.message : \"Unknown error\");\n process.exit(1);\n }\n}\n\n// Run the server\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});", "/**\n * SiliconFlow service wrapper for image generation and editing\n * Optimized for China users\n */\n\nimport { ImageResult, ModelInfo } from \"../types/index.js\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nexport class SiliconFlowService {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(apiKey: string) {\n if (!apiKey || apiKey.trim() === \"\") {\n throw new Error(\"SiliconFlow API key is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = \"https://api.siliconflow.cn/v1\";\n }\n\n /**\n * Make API call to SiliconFlow\n */\n private async makeApiCall<T = any>(endpoint: string, body: any): Promise<T> {\n const response = await fetch(`${this.baseUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error (${response.status}): ${errorText}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Make GET API call to SiliconFlow\n */\n private async makeGetCall<T = any>(endpoint: string, params?: Record<string, any>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n \"Authorization\": `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error (${response.status}): ${errorText}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Generate images using SiliconFlow's image generation models\n */\n async generateImage(\n prompt: string,\n model: string = \"black-forest-labs/FLUX.1-dev\",\n aspectRatio?: string,\n imageSize?: string,\n count: number = 1,\n negativePrompt?: string,\n seed?: number\n ): Promise<ImageResult[]> {\n try {\n // Build request body\n const requestBody: any = {\n model,\n prompt,\n batch_size: Math.min(count, 4), // SiliconFlow max is 4\n };\n\n // Handle image size mapping from aspect ratio\n if (aspectRatio || imageSize) {\n requestBody.image_size = this.mapAspectRatioToSize(aspectRatio, imageSize, model);\n }\n\n if (negativePrompt) {\n requestBody.negative_prompt = negativePrompt;\n }\n\n if (seed !== undefined) {\n requestBody.seed = seed;\n }\n\n // Use SiliconFlow's image generation endpoint\n const result = await this.makeApiCall(\"/images/generations\", requestBody);\n\n if (!result.images || result.images.length === 0) {\n throw new Error(\"No images were generated\");\n }\n\n // Download images and convert to base64\n const images: ImageResult[] = [];\n for (const img of result.images) {\n const imageUrl = img.url;\n const imageResponse = await fetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Failed to download image: ${imageResponse.status}`);\n }\n\n const imageBuffer = await imageResponse.arrayBuffer();\n const base64Data = Buffer.from(imageBuffer).toString('base64');\n\n // Determine mime type from URL or default to PNG\n const mimeType = imageUrl.includes('.png') ? 'image/png' :\n imageUrl.includes('.jpg') || imageUrl.includes('.jpeg') ? 'image/jpeg' :\n 'image/png';\n\n images.push({\n data: base64Data,\n mimeType,\n });\n }\n\n return images;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Image generation failed: ${error.message}`);\n }\n throw new Error(\"Image generation failed with unknown error\");\n }\n }\n\n /**\n * Edit an existing image using SiliconFlow\n */\n async editImage(\n image: string,\n prompt: string,\n model: string = \"Qwen/Qwen-Image-Edit-2509\"\n ): Promise<ImageResult> {\n try {\n // Determine if image is base64, URL, or local file path\n let imageContent: string;\n\n if (image.startsWith(\"data:image/\")) {\n // Already a data URL\n imageContent = image;\n } else if (image.startsWith(\"http://\") || image.startsWith(\"https://\")) {\n // URL - SiliconFlow accepts URLs directly\n imageContent = image;\n } else if (fs.existsSync(image) && fs.statSync(image).isFile()) {\n // Local file path - read and convert to base64 data URL\n const imageBuffer = fs.readFileSync(image);\n const base64Data = imageBuffer.toString('base64');\n\n // Determine mime type from file extension\n const ext = path.extname(image).toLowerCase();\n const mimeType = ext === '.png' ? 'image/png' :\n ext === '.jpg' || ext === '.jpeg' ? 'image/jpeg' :\n ext === '.webp' ? 'image/webp' :\n 'image/png';\n\n imageContent = `data:${mimeType};base64,${base64Data}`;\n } else {\n // Assume raw base64 string, convert to data URL\n imageContent = `data:image/png;base64,${image}`;\n }\n\n // Build request body for image editing\n const requestBody: any = {\n model,\n prompt,\n image: imageContent,\n };\n\n const result = await this.makeApiCall(\"/images/generations\", requestBody);\n\n if (!result.images || result.images.length === 0) {\n throw new Error(\"No edited image was returned\");\n }\n\n // Download the edited image and convert to base64\n const img = result.images[0];\n const imageUrl = img.url;\n\n const imageResponse = await fetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Failed to download edited image: ${imageResponse.status}`);\n }\n\n const imageBuffer = await imageResponse.arrayBuffer();\n const base64Data = Buffer.from(imageBuffer).toString('base64');\n\n const mimeType = imageUrl.includes('.png') ? 'image/png' :\n imageUrl.includes('.jpg') || imageUrl.includes('.jpeg') ? 'image/jpeg' :\n 'image/png';\n\n return {\n data: base64Data,\n mimeType,\n };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Image editing failed: ${error.message}`);\n }\n throw new Error(\"Image editing failed with unknown error\");\n }\n }\n\n /**\n * List all available image generation models\n */\n async listImageModels(): Promise<ModelInfo[]> {\n try {\n // Get all models, then filter for image generation models\n const result = await this.makeGetCall(\"/models\", { type: \"image\" });\n\n if (!result.data || result.data.length === 0) {\n return [];\n }\n\n // Filter for text-to-image and image-to-image models\n const imageModels = result.data\n .filter((model: any) => {\n // SiliconFlow doesn't always provide detailed modality info in the basic list\n // We'll include models that are commonly known for image generation\n const modelId = model.id.toLowerCase();\n return (\n modelId.includes(\"flux\") ||\n modelId.includes(\"sd\") ||\n modelId.includes(\"stable-diffusion\") ||\n modelId.includes(\"qwen-image\") ||\n modelId.includes(\"kolors\") ||\n modelId.includes(\"dall\") ||\n modelId.includes(\"painting\")\n );\n })\n .map((model: any) => ({\n id: model.id,\n name: model.id,\n description: `Image generation model: ${model.id}`,\n output_modalities: [\"image\"],\n }));\n\n return imageModels;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to list models: ${error.message}`);\n }\n throw new Error(\"Failed to list models with unknown error\");\n }\n }\n\n /**\n * Test the API key validity\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.makeGetCall(\"/models\");\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Map aspect ratio and image size to SiliconFlow's image_size format\n */\n private mapAspectRatioToSize(aspectRatio?: string, imageSize?: string, model?: string): string {\n // SiliconFlow uses \"widthxheight\" format\n // Based on their documentation, we'll map common aspect ratios\n\n // For Qwen models\n if (model && model.includes(\"qwen\")) {\n const qwenSizes: { [key: string]: string } = {\n \"1:1\": \"1328x1328\",\n \"16:9\": \"1664x928\",\n \"9:16\": \"928x1664\",\n \"4:3\": \"1472x1140\",\n \"3:4\": \"1140x1472\",\n \"3:2\": \"1584x1056\",\n \"2:3\": \"1056x1584\",\n };\n if (aspectRatio && qwenSizes[aspectRatio]) {\n return qwenSizes[aspectRatio];\n }\n }\n\n // For Kolors models\n const kolorsSizes: { [key: string]: string } = {\n \"1:1\": \"1024x1024\",\n \"3:4\": \"960x1280\",\n \"4:3\": \"1280x960\",\n \"1:2\": \"720x1440\",\n \"9:16\": \"720x1280\",\n \"16:9\": \"1280x720\",\n };\n\n if (aspectRatio && kolorsSizes[aspectRatio]) {\n return kolorsSizes[aspectRatio];\n }\n\n // Default sizes\n if (imageSize === \"4K\") return \"2048x2048\";\n if (imageSize === \"2K\") return \"1536x1536\";\n\n // Default to 1024x1024\n return \"1024x1024\";\n }\n}", "/**\n * Type definitions for SiliconFlow Image MCP\n */\n\nimport { z } from \"zod\";\n\n// Image generation input schema\nexport const GenerateImageInputSchema = z.object({\n prompt: z\n .string()\n .min(1, \"Prompt is required\")\n .max(2000, \"Prompt must be 2000 characters or less\")\n .describe(\"Detailed description of the image to generate\"),\n\n model: z\n .string()\n .optional()\n .describe(\"Model to use for generation (defaults to black-forest-labs/FLUX.1-dev)\"),\n\n aspectRatio: z\n .enum([\"1:1\", \"2:3\", \"3:2\", \"3:4\", \"4:3\", \"4:5\", \"5:4\", \"9:16\", \"16:9\", \"21:9\"])\n .optional()\n .describe(\"Aspect ratio for generated images\"),\n\n imageSize: z\n .enum([\"1K\", \"2K\", \"4K\"])\n .optional()\n .describe(\"Image size (higher resolution)\"),\n\n count: z\n .number()\n .int()\n .min(1)\n .max(4)\n .optional()\n .default(1)\n .describe(\"Number of images to generate (1-4)\"),\n\n negativePrompt: z\n .string()\n .optional()\n .describe(\"Negative prompt - what to avoid in the image\"),\n\n seed: z\n .number()\n .int()\n .min(0)\n .max(9999999999)\n .optional()\n .describe(\"Seed for reproducible results\"),\n});\n\nexport type GenerateImageInput = z.infer<typeof GenerateImageInputSchema>;\n\n// Image editing input schema\nexport const EditImageInputSchema = z.object({\n image: z\n .string()\n .min(1, \"Image data is required\")\n .describe(\"Base64 encoded image data, image URL, or local file path to edit\"),\n\n prompt: z\n .string()\n .min(1, \"Edit prompt is required\")\n .max(2000, \"Edit prompt must be 2000 characters or less\")\n .describe(\"Instructions for editing the image\"),\n\n model: z\n .string()\n .optional()\n .describe(\"Model to use for editing (defaults to Qwen/Qwen-Image-Edit-2509)\"),\n});\n\nexport type EditImageInput = z.infer<typeof EditImageInputSchema>;\n\n// List models input schema (empty)\nexport const ListModelsInputSchema = z.object({});\n\nexport type ListModelsInput = z.infer<typeof ListModelsInputSchema>;\n\n// Image result type\nexport interface ImageResult {\n data: string; // Base64 encoded image data\n mimeType: string; // e.g., \"image/png\"\n size?: number; // File size in bytes\n width?: number; // Image width in pixels\n height?: number; // Image height in pixels\n}\n\n// Model information type\nexport interface ModelInfo {\n id: string;\n name: string;\n description?: string;\n output_modalities?: string[];\n pricing?: {\n prompt?: number;\n completion?: number;\n image?: number;\n };\n context_length?: number;\n}\n\n// Tool response type\nexport interface ToolResponse {\n content: Array<{\n type: \"text\" | \"image\" | \"resource\";\n text?: string;\n data?: string;\n mimeType?: string;\n uri?: string;\n name?: string;\n }>;\n isError?: boolean;\n}", "/**\n * File utility functions for saving images\n */\n\nimport { promises as fs } from \"fs\";\nimport path from \"path\";\nimport os from \"os\";\n\n/**\n * Get the base directory for storing images\n * Uses SILICONFLOW_IMAGE_DIR env var if set, otherwise defaults to system temp dir\n */\nfunction getImageBaseDir(): string {\n const customDir = process.env.SILICONFLOW_IMAGE_DIR;\n if (customDir) {\n return customDir;\n }\n return path.join(os.tmpdir(), \"siliconflow-images\");\n}\n\n/**\n * Save base64 image data to a temporary file\n * @param base64Data - Base64 encoded image data\n * @param prefix - Prefix for the filename\n * @param mimeType - MIME type to determine file extension\n * @returns Path to the saved file\n */\nexport async function saveImageToFile(\n base64Data: string,\n prefix: string,\n mimeType: string\n): Promise<string> {\n const tempDir = getImageBaseDir();\n await fs.mkdir(tempDir, { recursive: true });\n\n const extension = mimeType === \"image/jpeg\" ? \"jpg\" : \"png\";\n const timestamp = Date.now();\n const filename = `${prefix}_${timestamp}.${extension}`;\n const filepath = path.join(tempDir, filename);\n\n const buffer = Buffer.from(base64Data, 'base64');\n await fs.writeFile(filepath, buffer);\n\n return filepath;\n}\n\n/**\n * Get the temporary directory path for siliconflow images\n * Uses SILICONFLOW_IMAGE_DIR env var if set, otherwise defaults to system temp dir\n * @returns The temporary directory path\n */\nexport function getTempDir(): string {\n return getImageBaseDir();\n}\n", "/**\n * Image generation tool implementation for SiliconFlow\n */\n\nimport { z } from \"zod\";\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { GenerateImageInputSchema, ToolResponse } from \"../types/index.js\";\nimport { saveImageToFile, getTempDir } from \"../utils/file.js\";\n\nexport function createGenerateImageTool(service: SiliconFlowService) {\n return {\n name: \"generate_image\",\n description: \"Generate images using SiliconFlow's AI models. Supports various aspect ratios, image sizes, negative prompts, and seeds for reproducible results. Images are saved to temporary files and paths are returned.\",\n inputSchema: GenerateImageInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = GenerateImageInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n const { prompt, model, aspectRatio, imageSize, count, negativePrompt, seed } = parsed.data;\n\n try {\n const images = await service.generateImage(\n prompt,\n model || \"black-forest-labs/FLUX.1-dev\",\n aspectRatio,\n imageSize,\n count,\n negativePrompt,\n seed\n );\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No images were generated. Please try a different prompt.\",\n },\n ],\n isError: true,\n };\n }\n\n // Save images to files and return file paths\n const savedFiles: string[] = [];\n\n for (let i = 0; i < images.length; i++) {\n const img = images[i];\n const filepath = await saveImageToFile(img.data, `generated_${i + 1}`, img.mimeType);\n savedFiles.push(filepath);\n }\n\n // Create response with file paths\n const tempDir = getTempDir();\n const response: ToolResponse = {\n content: [\n {\n type: \"text\",\n text: `Successfully generated ${images.length} image${images.length > 1 ? \"s\" : \"\"} for prompt: \"${prompt}\"\\n` +\n `Saved to:\\n${savedFiles.map(f => `- ${f}`).join(\"\\n\")}\\n\\n` +\n `Temporary directory: ${tempDir}\\n` +\n `Note: These are temporary files. Use the file paths to access the images.`,\n },\n ],\n };\n\n return response;\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Image generation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}", "/**\n * Image editing tool implementation for SiliconFlow\n */\n\nimport { z } from \"zod\";\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { EditImageInputSchema, ToolResponse } from \"../types/index.js\";\nimport { saveImageToFile, getTempDir } from \"../utils/file.js\";\n\nexport function createEditImageTool(service: SiliconFlowService) {\n return {\n name: \"edit_image\",\n description: \"Edit existing images using SiliconFlow's AI models. Accepts base64 encoded image data, image URLs, or local file paths. Provide instructions for modifications. Uses Qwen/Qwen-Image-Edit-2509 by default. Images are saved to temporary files and paths are returned.\",\n inputSchema: EditImageInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = EditImageInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n const { image, prompt, model } = parsed.data;\n\n try {\n const editedImage = await service.editImage(\n image,\n prompt,\n model || \"Qwen/Qwen-Image-Edit-2509\"\n );\n\n // Save edited image to file\n const filepath = await saveImageToFile(editedImage.data, \"edited\", editedImage.mimeType);\n\n // Create response with file path\n const tempDir = getTempDir();\n const response: ToolResponse = {\n content: [\n {\n type: \"text\",\n text: `Successfully edited image with prompt: \"${prompt}\"\\n` +\n `Saved to: ${filepath}\\n\\n` +\n `Temporary directory: ${tempDir}\\n` +\n `Note: This is a temporary file. Use the file path to access the image.`,\n },\n ],\n };\n\n return response;\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Image editing failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}", "/**\n * List image models tool implementation for SiliconFlow\n */\n\nimport { z } from \"zod\";\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { ListModelsInputSchema, ToolResponse } from \"../types/index.js\";\n\nexport function createListModelsTool(service: SiliconFlowService) {\n return {\n name: \"list_image_models\",\n description: \"List all available image generation models from SiliconFlow. Shows model IDs and capabilities.\",\n inputSchema: ListModelsInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = ListModelsInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const models = await service.listImageModels();\n\n if (models.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No image generation models found. This might be a temporary issue with the API.\",\n },\n ],\n isError: true,\n };\n }\n\n // Format the response for readability\n const modelList = models\n .map((model, index) => {\n const parts = [\n `${index + 1}. **${model.name}** (\\`${model.id}\\`)`,\n model.description ? ` - ${model.description}` : null,\n model.output_modalities ? ` - Capabilities: ${model.output_modalities.join(\", \")}` : null,\n ].filter(Boolean);\n\n return parts.join(\"\\n\");\n })\n .join(\"\\n\\n\");\n\n // Add SiliconFlow-specific usage tips\n const usageTips = `\\n\\n---\\n\\n**Usage:** Use the \\`generate_image\\` tool with the model ID to generate images.\\n\\n**SiliconFlow Tips:**\\n- Recommended models: \\`black-forest-labs/FLUX.1-dev\\` (generation), \\`Qwen/Qwen-Image-Edit-2509\\` (editing)\\n- Supports advanced options: negative prompts, seeds, CFG values\\n- Image sizes: Use aspect ratios like \"1:1\", \"16:9\", \"9:16\"\\n- Optimized for China users with fast local network access`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `## Available Image Generation Models (SiliconFlow)\\n\\nFound ${models.length} model${models.length > 1 ? \"s\" : \"\"} that support image generation:\\n\\n${modelList}${usageTips}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Failed to list models: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n/**\n * SiliconFlow Image MCP Server\n *\n * A Model Context Protocol server for image generation and editing using SiliconFlow\n * Optimized for China users\n */\n\n// Handle build flag for compilation\nif (process.argv.includes('--build')) {\n console.log('\u2705 Project structure is ready. Use \"npm start\" to run the server.');\n console.log('\uD83D\uDCDD Note: This project uses tsx for runtime execution.');\n process.exit(0);\n}\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { SiliconFlowService } from \"./services/siliconflow.js\";\nimport { createGenerateImageTool } from \"./tools/generate.js\";\nimport { createEditImageTool } from \"./tools/edit.js\";\nimport { createListModelsTool } from \"./tools/list-models.js\";\n\n// Environment variable check\nconst SILICONFLOW_API_KEY = process.env.SILICONFLOW_API_KEY;\nconst MOCK_MODE = process.env.SILICONFLOW_MOCK === \"true\";\n\nif (!SILICONFLOW_API_KEY && !MOCK_MODE) {\n console.error(\"\u274C Error: SILICONFLOW_API_KEY environment variable is required\");\n console.error(\"\\nTo use this MCP server:\");\n console.error(\"1. Get your API key from https://siliconflow.cn\");\n console.error(\"2. Set it as an environment variable:\");\n console.error(\" export SILICONFLOW_API_KEY=your-api-key-here\");\n console.error(\"\\nFor Claude Desktop, add to your config.json:\");\n console.error(`{\n \"mcpServers\": {\n \"siliconflow-image-mcp\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"siliconflow-image-mcp\"],\n \"env\": {\n \"SILICONFLOW_API_KEY\": \"your-api-key-here\"\n }\n }\n }\n}`);\n console.error(\"\\nOptional configurations:\");\n console.error(\" export SILICONFLOW_MOCK=true # Use mock mode for testing\");\n console.error(\" export SILICONFLOW_IMAGE_DIR=/path # Custom directory for saved images\");\n process.exit(1);\n}\n\n// Initialize services and server\nasync function main() {\n console.error(\"\uD83D\uDE80 Starting SiliconFlow Image MCP Server...\");\n\n try {\n // Initialize SiliconFlow service\n let siliconFlowService;\n if (MOCK_MODE) {\n console.error(\"\u26A0\uFE0F Running in MOCK mode - API calls will be simulated\");\n // Create a mock service that returns fake data\n siliconFlowService = {\n generateImage: async () => [{ data: \"mock-base64-data\", mimeType: \"image/png\" }],\n editImage: async () => ({ data: \"mock-base64-data\", mimeType: \"image/png\" }),\n listImageModels: async () => [\n { id: \"black-forest-labs/FLUX.1-dev\", name: \"FLUX.1-dev\", description: \"Mock model\", output_modalities: [\"image\"] }\n ],\n testConnection: async () => true\n };\n } else {\n siliconFlowService = new SiliconFlowService(SILICONFLOW_API_KEY);\n\n // Test connection\n const isConnected = await siliconFlowService.testConnection();\n if (!isConnected) {\n console.error(\"\u274C Failed to connect to SiliconFlow. Please check your API key.\");\n process.exit(1);\n }\n console.error(\"\u2705 Connected to SiliconFlow successfully\");\n }\n\n // Create MCP server\n const server = new McpServer({\n name: \"siliconflow-image-mcp\",\n version: \"1.0.0\",\n capabilities: {\n tools: {\n listChanged: true,\n },\n },\n });\n\n // Register tools\n const generateTool = createGenerateImageTool(siliconFlowService);\n const editTool = createEditImageTool(siliconFlowService);\n const listModelsTool = createListModelsTool(siliconFlowService);\n\n server.registerTool(\n generateTool.name,\n {\n description: generateTool.description,\n inputSchema: generateTool.inputSchema,\n },\n generateTool.handler\n );\n\n server.registerTool(\n editTool.name,\n {\n description: editTool.description,\n inputSchema: editTool.inputSchema,\n },\n editTool.handler\n );\n\n server.registerTool(\n listModelsTool.name,\n {\n description: listModelsTool.description,\n inputSchema: listModelsTool.inputSchema,\n },\n listModelsTool.handler\n );\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(\"\u2705 SiliconFlow Image MCP Server is running\");\n console.error(\"\uD83D\uDCCB Available tools: generate_image, edit_image, list_image_models\");\n console.error(\"\uD83D\uDCDD Waiting for requests...\");\n\n // Handle graceful shutdown\n process.on(\"SIGINT\", async () => {\n console.error(\"\\n\uD83D\uDED1 Shutting down gracefully...\");\n await server.close();\n process.exit(0);\n });\n\n } catch (error) {\n console.error(\"\u274C Failed to start server:\", error instanceof Error ? error.message : \"Unknown error\");\n process.exit(1);\n }\n}\n\n// Run the server\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});", "/**\n * SiliconFlow service wrapper for image generation and editing\n * Optimized for China users\n */\n\nimport { ImageResult, ModelInfo } from \"../types/index.js\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\nexport class SiliconFlowService {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(apiKey: string) {\n if (!apiKey || apiKey.trim() === \"\") {\n throw new Error(\"SiliconFlow API key is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = \"https://api.siliconflow.cn/v1\";\n }\n\n /**\n * Make API call to SiliconFlow\n */\n private async makeApiCall<T = any>(endpoint: string, body: any): Promise<T> {\n const response = await fetch(`${this.baseUrl}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error (${response.status}): ${errorText}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Make GET API call to SiliconFlow\n */\n private async makeGetCall<T = any>(endpoint: string, params?: Record<string, any>): Promise<T> {\n const url = new URL(`${this.baseUrl}${endpoint}`);\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error (${response.status}): ${errorText}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Generate images using SiliconFlow's image generation models\n */\n async generateImage(\n prompt: string,\n model: string = \"black-forest-labs/FLUX.1-dev\",\n aspectRatio?: string,\n imageSize?: string,\n count: number = 1,\n negativePrompt?: string,\n seed?: number,\n ): Promise<ImageResult[]> {\n try {\n // Build request body\n const requestBody: any = {\n model,\n prompt,\n batch_size: Math.min(count, 4), // SiliconFlow max is 4\n };\n\n // Handle image size mapping from aspect ratio\n if (aspectRatio || imageSize) {\n requestBody.image_size = this.mapAspectRatioToSize(aspectRatio, imageSize, model);\n }\n\n if (negativePrompt) {\n requestBody.negative_prompt = negativePrompt;\n }\n\n if (seed !== undefined) {\n requestBody.seed = seed;\n }\n\n // Use SiliconFlow's image generation endpoint\n const result = await this.makeApiCall(\"/images/generations\", requestBody);\n\n if (!result.images || result.images.length === 0) {\n throw new Error(\"No images were generated\");\n }\n\n // Download images and convert to base64\n const images: ImageResult[] = [];\n for (const img of result.images) {\n const imageUrl = img.url;\n const imageResponse = await fetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Failed to download image: ${imageResponse.status}`);\n }\n\n const imageBuffer = await imageResponse.arrayBuffer();\n const base64Data = Buffer.from(imageBuffer).toString(\"base64\");\n\n // Determine mime type from URL or default to PNG\n const mimeType = imageUrl.includes(\".png\")\n ? \"image/png\"\n : imageUrl.includes(\".jpg\") || imageUrl.includes(\".jpeg\")\n ? \"image/jpeg\"\n : \"image/png\";\n\n images.push({\n data: base64Data,\n mimeType,\n });\n }\n\n return images;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Image generation failed: ${error.message}`);\n }\n throw new Error(\"Image generation failed with unknown error\");\n }\n }\n\n /**\n * Edit an existing image using SiliconFlow\n */\n async editImage(\n image: string,\n prompt: string,\n model: string = \"Qwen/Qwen-Image-Edit-2509\",\n ): Promise<ImageResult> {\n try {\n // Determine if image is base64, URL, or local file path\n let imageContent: string;\n\n if (image.startsWith(\"data:image/\")) {\n // Already a data URL\n imageContent = image;\n } else if (image.startsWith(\"http://\") || image.startsWith(\"https://\")) {\n // URL - SiliconFlow accepts URLs directly\n imageContent = image;\n } else if (fs.existsSync(image) && fs.statSync(image).isFile()) {\n // Local file path - read and convert to base64 data URL\n const imageBuffer = fs.readFileSync(image);\n const base64Data = imageBuffer.toString(\"base64\");\n\n // Determine mime type from file extension\n const ext = path.extname(image).toLowerCase();\n const mimeType =\n ext === \".png\"\n ? \"image/png\"\n : ext === \".jpg\" || ext === \".jpeg\"\n ? \"image/jpeg\"\n : ext === \".webp\"\n ? \"image/webp\"\n : \"image/png\";\n\n imageContent = `data:${mimeType};base64,${base64Data}`;\n } else {\n // Assume raw base64 string, convert to data URL\n imageContent = `data:image/png;base64,${image}`;\n }\n\n // Build request body for image editing\n const requestBody: any = {\n model,\n prompt,\n image: imageContent,\n };\n\n const result = await this.makeApiCall(\"/images/generations\", requestBody);\n\n if (!result.images || result.images.length === 0) {\n throw new Error(\"No edited image was returned\");\n }\n\n // Download the edited image and convert to base64\n const img = result.images[0];\n const imageUrl = img.url;\n\n const imageResponse = await fetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Failed to download edited image: ${imageResponse.status}`);\n }\n\n const imageBuffer = await imageResponse.arrayBuffer();\n const base64Data = Buffer.from(imageBuffer).toString(\"base64\");\n\n const mimeType = imageUrl.includes(\".png\")\n ? \"image/png\"\n : imageUrl.includes(\".jpg\") || imageUrl.includes(\".jpeg\")\n ? \"image/jpeg\"\n : \"image/png\";\n\n return {\n data: base64Data,\n mimeType,\n };\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Image editing failed: ${error.message}`);\n }\n throw new Error(\"Image editing failed with unknown error\");\n }\n }\n\n /**\n * List all available image generation models\n */\n async listImageModels(): Promise<ModelInfo[]> {\n try {\n // Get all models, then filter for image generation models\n const result = await this.makeGetCall(\"/models\", { type: \"image\" });\n\n if (!result.data || result.data.length === 0) {\n return [];\n }\n\n // Filter for text-to-image and image-to-image models\n const imageModels = result.data\n .filter((model: any) => {\n // SiliconFlow doesn't always provide detailed modality info in the basic list\n // We'll include models that are commonly known for image generation\n const modelId = model.id.toLowerCase();\n return (\n modelId.includes(\"flux\") ||\n modelId.includes(\"sd\") ||\n modelId.includes(\"stable-diffusion\") ||\n modelId.includes(\"qwen-image\") ||\n modelId.includes(\"kolors\") ||\n modelId.includes(\"dall\") ||\n modelId.includes(\"painting\")\n );\n })\n .map((model: any) => ({\n id: model.id,\n name: model.id,\n description: `Image generation model: ${model.id}`,\n output_modalities: [\"image\"],\n }));\n\n return imageModels;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to list models: ${error.message}`);\n }\n throw new Error(\"Failed to list models with unknown error\");\n }\n }\n\n /**\n * Test the API key validity\n */\n async testConnection(): Promise<boolean> {\n try {\n await this.makeGetCall(\"/models\");\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Map aspect ratio and image size to SiliconFlow's image_size format\n */\n private mapAspectRatioToSize(aspectRatio?: string, imageSize?: string, model?: string): string {\n // SiliconFlow uses \"widthxheight\" format\n // Based on their documentation, we'll map common aspect ratios\n\n // For Qwen models\n if (model && model.includes(\"qwen\")) {\n const qwenSizes: { [key: string]: string } = {\n \"1:1\": \"1328x1328\",\n \"16:9\": \"1664x928\",\n \"9:16\": \"928x1664\",\n \"4:3\": \"1472x1140\",\n \"3:4\": \"1140x1472\",\n \"3:2\": \"1584x1056\",\n \"2:3\": \"1056x1584\",\n };\n if (aspectRatio && qwenSizes[aspectRatio]) {\n return qwenSizes[aspectRatio];\n }\n }\n\n // For Kolors models\n const kolorsSizes: { [key: string]: string } = {\n \"1:1\": \"1024x1024\",\n \"3:4\": \"960x1280\",\n \"4:3\": \"1280x960\",\n \"1:2\": \"720x1440\",\n \"9:16\": \"720x1280\",\n \"16:9\": \"1280x720\",\n };\n\n if (aspectRatio && kolorsSizes[aspectRatio]) {\n return kolorsSizes[aspectRatio];\n }\n\n // Default sizes\n if (imageSize === \"4K\") return \"2048x2048\";\n if (imageSize === \"2K\") return \"1536x1536\";\n\n // Default to 1024x1024\n return \"1024x1024\";\n }\n}\n", "/**\n * Type definitions for SiliconFlow Image MCP\n */\n\nimport { z } from \"zod\";\n\n// Image generation input schema\nexport const GenerateImageInputSchema = z.object({\n prompt: z\n .string()\n .min(1, \"Prompt is required\")\n .max(2000, \"Prompt must be 2000 characters or less\")\n .describe(\"Detailed description of the image to generate\"),\n\n model: z\n .string()\n .optional()\n .describe(\"Model to use for generation (defaults to black-forest-labs/FLUX.1-dev)\"),\n\n aspectRatio: z\n .enum([\"1:1\", \"2:3\", \"3:2\", \"3:4\", \"4:3\", \"4:5\", \"5:4\", \"9:16\", \"16:9\", \"21:9\"])\n .optional()\n .describe(\"Aspect ratio for generated images\"),\n\n imageSize: z.enum([\"1K\", \"2K\", \"4K\"]).optional().describe(\"Image size (higher resolution)\"),\n\n count: z\n .number()\n .int()\n .min(1)\n .max(4)\n .optional()\n .default(1)\n .describe(\"Number of images to generate (1-4)\"),\n\n negativePrompt: z.string().optional().describe(\"Negative prompt - what to avoid in the image\"),\n\n seed: z\n .number()\n .int()\n .min(0)\n .max(9999999999)\n .optional()\n .describe(\"Seed for reproducible results\"),\n});\n\nexport type GenerateImageInput = z.infer<typeof GenerateImageInputSchema>;\n\n// Image editing input schema\nexport const EditImageInputSchema = z.object({\n image: z\n .string()\n .min(1, \"Image data is required\")\n .describe(\"Base64 encoded image data, image URL, or local file path to edit\"),\n\n prompt: z\n .string()\n .min(1, \"Edit prompt is required\")\n .max(2000, \"Edit prompt must be 2000 characters or less\")\n .describe(\"Instructions for editing the image\"),\n\n model: z\n .string()\n .optional()\n .describe(\"Model to use for editing (defaults to Qwen/Qwen-Image-Edit-2509)\"),\n});\n\nexport type EditImageInput = z.infer<typeof EditImageInputSchema>;\n\n// List models input schema (empty)\nexport const ListModelsInputSchema = z.object({});\n\nexport type ListModelsInput = z.infer<typeof ListModelsInputSchema>;\n\n// Image result type\nexport interface ImageResult {\n data: string; // Base64 encoded image data\n mimeType: string; // e.g., \"image/png\"\n size?: number; // File size in bytes\n width?: number; // Image width in pixels\n height?: number; // Image height in pixels\n}\n\n// Model information type\nexport interface ModelInfo {\n id: string;\n name: string;\n description?: string;\n output_modalities?: string[];\n pricing?: {\n prompt?: number;\n completion?: number;\n image?: number;\n };\n context_length?: number;\n}\n\n// Tool response type\nexport interface ToolResponse {\n content: Array<{\n type: \"text\" | \"image\" | \"resource\";\n text?: string;\n data?: string;\n mimeType?: string;\n uri?: string;\n name?: string;\n }>;\n isError?: boolean;\n}\n", "/**\n * File utility functions for saving images\n */\n\nimport { promises as fs } from \"fs\";\nimport path from \"path\";\nimport os from \"os\";\n\n/**\n * Get the base directory for storing images\n * Uses SILICONFLOW_IMAGE_DIR env var if set, otherwise defaults to system temp dir\n */\nfunction getImageBaseDir(): string {\n const customDir = process.env.SILICONFLOW_IMAGE_DIR;\n if (customDir) {\n return customDir;\n }\n return path.join(os.tmpdir(), \"siliconflow-images\");\n}\n\n/**\n * Save base64 image data to a temporary file\n * @param base64Data - Base64 encoded image data\n * @param prefix - Prefix for the filename\n * @param mimeType - MIME type to determine file extension\n * @returns Path to the saved file\n */\nexport async function saveImageToFile(\n base64Data: string,\n prefix: string,\n mimeType: string,\n): Promise<string> {\n const tempDir = getImageBaseDir();\n await fs.mkdir(tempDir, { recursive: true });\n\n const extension = mimeType === \"image/jpeg\" ? \"jpg\" : \"png\";\n const timestamp = Date.now();\n const filename = `${prefix}_${timestamp}.${extension}`;\n const filepath = path.join(tempDir, filename);\n\n const buffer = Buffer.from(base64Data, \"base64\");\n await fs.writeFile(filepath, buffer);\n\n return filepath;\n}\n\n/**\n * Get the temporary directory path for siliconflow images\n * Uses SILICONFLOW_IMAGE_DIR env var if set, otherwise defaults to system temp dir\n * @returns The temporary directory path\n */\nexport function getTempDir(): string {\n return getImageBaseDir();\n}\n", "/**\n * Image generation tool implementation for SiliconFlow\n */\n\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { GenerateImageInputSchema, ToolResponse } from \"../types/index.js\";\nimport { saveImageToFile, getTempDir } from \"../utils/file.js\";\n\nexport function createGenerateImageTool(service: SiliconFlowService) {\n return {\n name: \"generate_image\",\n description:\n \"Generate images using SiliconFlow's AI models. Supports various aspect ratios, image sizes, negative prompts, and seeds for reproducible results. Images are saved to temporary files and paths are returned.\",\n inputSchema: GenerateImageInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = GenerateImageInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n const { prompt, model, aspectRatio, imageSize, count, negativePrompt, seed } = parsed.data;\n\n try {\n const images = await service.generateImage(\n prompt,\n model || \"black-forest-labs/FLUX.1-dev\",\n aspectRatio,\n imageSize,\n count,\n negativePrompt,\n seed,\n );\n\n if (images.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No images were generated. Please try a different prompt.\",\n },\n ],\n isError: true,\n };\n }\n\n // Save images to files and return file paths\n const savedFiles: string[] = [];\n\n for (let i = 0; i < images.length; i++) {\n const img = images[i];\n const filepath = await saveImageToFile(img.data, `generated_${i + 1}`, img.mimeType);\n savedFiles.push(filepath);\n }\n\n // Create response with file paths\n const tempDir = getTempDir();\n const response: ToolResponse = {\n content: [\n {\n type: \"text\",\n text:\n `Successfully generated ${images.length} image${images.length > 1 ? \"s\" : \"\"} for prompt: \"${prompt}\"\\n` +\n `Saved to:\\n${savedFiles.map((f) => `- ${f}`).join(\"\\n\")}\\n\\n` +\n `Temporary directory: ${tempDir}\\n` +\n `Note: These are temporary files. Use the file paths to access the images.`,\n },\n ],\n };\n\n return response;\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Image generation failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}\n", "/**\n * Image editing tool implementation for SiliconFlow\n */\n\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { EditImageInputSchema, ToolResponse } from \"../types/index.js\";\nimport { saveImageToFile, getTempDir } from \"../utils/file.js\";\n\nexport function createEditImageTool(service: SiliconFlowService) {\n return {\n name: \"edit_image\",\n description:\n \"Edit existing images using SiliconFlow's AI models. Accepts base64 encoded image data, image URLs, or local file paths. Provide instructions for modifications. Uses Qwen/Qwen-Image-Edit-2509 by default. Images are saved to temporary files and paths are returned.\",\n inputSchema: EditImageInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = EditImageInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n const { image, prompt, model } = parsed.data;\n\n try {\n const editedImage = await service.editImage(\n image,\n prompt,\n model || \"Qwen/Qwen-Image-Edit-2509\",\n );\n\n // Save edited image to file\n const filepath = await saveImageToFile(editedImage.data, \"edited\", editedImage.mimeType);\n\n // Create response with file path\n const tempDir = getTempDir();\n const response: ToolResponse = {\n content: [\n {\n type: \"text\",\n text:\n `Successfully edited image with prompt: \"${prompt}\"\\n` +\n `Saved to: ${filepath}\\n\\n` +\n `Temporary directory: ${tempDir}\\n` +\n `Note: This is a temporary file. Use the file path to access the image.`,\n },\n ],\n };\n\n return response;\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Image editing failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}\n", "/**\n * List image models tool implementation for SiliconFlow\n */\n\nimport { SiliconFlowService } from \"../services/siliconflow.js\";\nimport { ListModelsInputSchema, ToolResponse } from \"../types/index.js\";\n\nexport function createListModelsTool(service: SiliconFlowService) {\n return {\n name: \"list_image_models\",\n description:\n \"List all available image generation models from SiliconFlow. Shows model IDs and capabilities.\",\n inputSchema: ListModelsInputSchema,\n\n handler: async (input: unknown): Promise<ToolResponse> => {\n const parsed = ListModelsInputSchema.safeParse(input);\n\n if (!parsed.success) {\n return {\n content: [\n {\n type: \"text\",\n text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(\", \")}`,\n },\n ],\n isError: true,\n };\n }\n\n try {\n const models = await service.listImageModels();\n\n if (models.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No image generation models found. This might be a temporary issue with the API.\",\n },\n ],\n isError: true,\n };\n }\n\n // Format the response for readability\n const modelList = models\n .map((model, index) => {\n const parts = [\n `${index + 1}. **${model.name}** (\\`${model.id}\\`)`,\n model.description ? ` - ${model.description}` : null,\n model.output_modalities\n ? ` - Capabilities: ${model.output_modalities.join(\", \")}`\n : null,\n ].filter(Boolean);\n\n return parts.join(\"\\n\");\n })\n .join(\"\\n\\n\");\n\n // Add SiliconFlow-specific usage tips\n const usageTips = `\\n\\n---\\n\\n**Usage:** Use the \\`generate_image\\` tool with the model ID to generate images.\\n\\n**SiliconFlow Tips:**\\n- Recommended models: \\`black-forest-labs/FLUX.1-dev\\` (generation), \\`Qwen/Qwen-Image-Edit-2509\\` (editing)\\n- Supports advanced options: negative prompts, seeds, CFG values\\n- Image sizes: Use aspect ratios like \"1:1\", \"16:9\", \"9:16\"\\n- Optimized for China users with fast local network access`;\n\n return {\n content: [\n {\n type: \"text\",\n text: `## Available Image Generation Models (SiliconFlow)\\n\\nFound ${models.length} model${models.length > 1 ? \"s\" : \"\"} that support image generation:\\n\\n${modelList}${usageTips}`,\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: \"text\",\n text: `Failed to list models: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n },\n ],\n isError: true,\n };\n }\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["imageBuffer", "base64Data", "mimeType", "fs", "path", "path", "fs"]
7
7
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siliconflow-image-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server for image generation and editing using SiliconFlow - Optimized for China users",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siliconflow-image-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server for image generation and editing using SiliconFlow - Optimized for China users",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -58,11 +58,12 @@
58
58
  "@types/node": "^20.11.5",
59
59
  "@typescript-eslint/eslint-plugin": "^6.19.0",
60
60
  "@typescript-eslint/parser": "^6.19.0",
61
+ "esbuild": "^0.25.0",
61
62
  "eslint": "^8.56.0",
62
63
  "prettier": "^3.2.4",
63
64
  "tsx": "^4.7.0",
64
65
  "typescript": "^5.3.3",
65
- "vitest": "^1.2.1"
66
+ "vitest": "^4.0.17"
66
67
  },
67
68
  "engines": {
68
69
  "node": ">=18.0.0"
@@ -27,7 +27,7 @@ export class SiliconFlowService {
27
27
  method: "POST",
28
28
  headers: {
29
29
  "Content-Type": "application/json",
30
- "Authorization": `Bearer ${this.apiKey}`,
30
+ Authorization: `Bearer ${this.apiKey}`,
31
31
  },
32
32
  body: JSON.stringify(body),
33
33
  });
@@ -56,7 +56,7 @@ export class SiliconFlowService {
56
56
  const response = await fetch(url.toString(), {
57
57
  method: "GET",
58
58
  headers: {
59
- "Authorization": `Bearer ${this.apiKey}`,
59
+ Authorization: `Bearer ${this.apiKey}`,
60
60
  },
61
61
  });
62
62
 
@@ -78,7 +78,7 @@ export class SiliconFlowService {
78
78
  imageSize?: string,
79
79
  count: number = 1,
80
80
  negativePrompt?: string,
81
- seed?: number
81
+ seed?: number,
82
82
  ): Promise<ImageResult[]> {
83
83
  try {
84
84
  // Build request body
@@ -118,12 +118,14 @@ export class SiliconFlowService {
118
118
  }
119
119
 
120
120
  const imageBuffer = await imageResponse.arrayBuffer();
121
- const base64Data = Buffer.from(imageBuffer).toString('base64');
121
+ const base64Data = Buffer.from(imageBuffer).toString("base64");
122
122
 
123
123
  // Determine mime type from URL or default to PNG
124
- const mimeType = imageUrl.includes('.png') ? 'image/png' :
125
- imageUrl.includes('.jpg') || imageUrl.includes('.jpeg') ? 'image/jpeg' :
126
- 'image/png';
124
+ const mimeType = imageUrl.includes(".png")
125
+ ? "image/png"
126
+ : imageUrl.includes(".jpg") || imageUrl.includes(".jpeg")
127
+ ? "image/jpeg"
128
+ : "image/png";
127
129
 
128
130
  images.push({
129
131
  data: base64Data,
@@ -146,7 +148,7 @@ export class SiliconFlowService {
146
148
  async editImage(
147
149
  image: string,
148
150
  prompt: string,
149
- model: string = "Qwen/Qwen-Image-Edit-2509"
151
+ model: string = "Qwen/Qwen-Image-Edit-2509",
150
152
  ): Promise<ImageResult> {
151
153
  try {
152
154
  // Determine if image is base64, URL, or local file path
@@ -161,14 +163,18 @@ export class SiliconFlowService {
161
163
  } else if (fs.existsSync(image) && fs.statSync(image).isFile()) {
162
164
  // Local file path - read and convert to base64 data URL
163
165
  const imageBuffer = fs.readFileSync(image);
164
- const base64Data = imageBuffer.toString('base64');
166
+ const base64Data = imageBuffer.toString("base64");
165
167
 
166
168
  // Determine mime type from file extension
167
169
  const ext = path.extname(image).toLowerCase();
168
- const mimeType = ext === '.png' ? 'image/png' :
169
- ext === '.jpg' || ext === '.jpeg' ? 'image/jpeg' :
170
- ext === '.webp' ? 'image/webp' :
171
- 'image/png';
170
+ const mimeType =
171
+ ext === ".png"
172
+ ? "image/png"
173
+ : ext === ".jpg" || ext === ".jpeg"
174
+ ? "image/jpeg"
175
+ : ext === ".webp"
176
+ ? "image/webp"
177
+ : "image/png";
172
178
 
173
179
  imageContent = `data:${mimeType};base64,${base64Data}`;
174
180
  } else {
@@ -199,11 +205,13 @@ export class SiliconFlowService {
199
205
  }
200
206
 
201
207
  const imageBuffer = await imageResponse.arrayBuffer();
202
- const base64Data = Buffer.from(imageBuffer).toString('base64');
208
+ const base64Data = Buffer.from(imageBuffer).toString("base64");
203
209
 
204
- const mimeType = imageUrl.includes('.png') ? 'image/png' :
205
- imageUrl.includes('.jpg') || imageUrl.includes('.jpeg') ? 'image/jpeg' :
206
- 'image/png';
210
+ const mimeType = imageUrl.includes(".png")
211
+ ? "image/png"
212
+ : imageUrl.includes(".jpg") || imageUrl.includes(".jpeg")
213
+ ? "image/jpeg"
214
+ : "image/png";
207
215
 
208
216
  return {
209
217
  data: base64Data,
@@ -317,4 +325,4 @@ export class SiliconFlowService {
317
325
  // Default to 1024x1024
318
326
  return "1024x1024";
319
327
  }
320
- }
328
+ }
@@ -68,7 +68,7 @@ describe("edit_image tool", () => {
68
68
  expect(saveImageToFile).toHaveBeenCalledWith(
69
69
  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
70
70
  "edited",
71
- "image/png"
71
+ "image/png",
72
72
  );
73
73
  expect(getTempDir).toHaveBeenCalled();
74
74
 
@@ -144,4 +144,4 @@ describe("edit_image tool", () => {
144
144
  expect(result.isError).toBe(true);
145
145
  expect(result.content[0].text).toContain("Image editing failed");
146
146
  });
147
- });
147
+ });
package/src/tools/edit.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * Image editing tool implementation for SiliconFlow
3
3
  */
4
4
 
5
- import { z } from "zod";
6
5
  import { SiliconFlowService } from "../services/siliconflow.js";
7
6
  import { EditImageInputSchema, ToolResponse } from "../types/index.js";
8
7
  import { saveImageToFile, getTempDir } from "../utils/file.js";
@@ -10,7 +9,8 @@ import { saveImageToFile, getTempDir } from "../utils/file.js";
10
9
  export function createEditImageTool(service: SiliconFlowService) {
11
10
  return {
12
11
  name: "edit_image",
13
- description: "Edit existing images using SiliconFlow's AI models. Accepts base64 encoded image data, image URLs, or local file paths. Provide instructions for modifications. Uses Qwen/Qwen-Image-Edit-2509 by default. Images are saved to temporary files and paths are returned.",
12
+ description:
13
+ "Edit existing images using SiliconFlow's AI models. Accepts base64 encoded image data, image URLs, or local file paths. Provide instructions for modifications. Uses Qwen/Qwen-Image-Edit-2509 by default. Images are saved to temporary files and paths are returned.",
14
14
  inputSchema: EditImageInputSchema,
15
15
 
16
16
  handler: async (input: unknown): Promise<ToolResponse> => {
@@ -21,7 +21,7 @@ export function createEditImageTool(service: SiliconFlowService) {
21
21
  content: [
22
22
  {
23
23
  type: "text",
24
- text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(", ")}`,
24
+ text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(", ")}`,
25
25
  },
26
26
  ],
27
27
  isError: true,
@@ -34,7 +34,7 @@ export function createEditImageTool(service: SiliconFlowService) {
34
34
  const editedImage = await service.editImage(
35
35
  image,
36
36
  prompt,
37
- model || "Qwen/Qwen-Image-Edit-2509"
37
+ model || "Qwen/Qwen-Image-Edit-2509",
38
38
  );
39
39
 
40
40
  // Save edited image to file
@@ -46,10 +46,11 @@ export function createEditImageTool(service: SiliconFlowService) {
46
46
  content: [
47
47
  {
48
48
  type: "text",
49
- text: `Successfully edited image with prompt: "${prompt}"\n` +
50
- `Saved to: ${filepath}\n\n` +
51
- `Temporary directory: ${tempDir}\n` +
52
- `Note: This is a temporary file. Use the file path to access the image.`,
49
+ text:
50
+ `Successfully edited image with prompt: "${prompt}"\n` +
51
+ `Saved to: ${filepath}\n\n` +
52
+ `Temporary directory: ${tempDir}\n` +
53
+ `Note: This is a temporary file. Use the file path to access the image.`,
53
54
  },
54
55
  ],
55
56
  };
@@ -68,4 +69,4 @@ export function createEditImageTool(service: SiliconFlowService) {
68
69
  }
69
70
  },
70
71
  };
71
- }
72
+ }
@@ -72,7 +72,7 @@ describe("generate_image tool", () => {
72
72
  expect(saveImageToFile).toHaveBeenCalledWith(
73
73
  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
74
74
  "generated_1",
75
- "image/png"
75
+ "image/png",
76
76
  );
77
77
  expect(getTempDir).toHaveBeenCalled();
78
78
 
@@ -147,9 +147,7 @@ describe("generate_image tool", () => {
147
147
  });
148
148
 
149
149
  it("should handle file system errors gracefully", async () => {
150
- mockService.generateImage.mockResolvedValue([
151
- { data: "img1", mimeType: "image/png" },
152
- ]);
150
+ mockService.generateImage.mockResolvedValue([{ data: "img1", mimeType: "image/png" }]);
153
151
 
154
152
  vi.mocked(saveImageToFile).mockRejectedValue(new Error("Permission denied"));
155
153
 
@@ -160,4 +158,4 @@ describe("generate_image tool", () => {
160
158
  expect(result.isError).toBe(true);
161
159
  expect(result.content[0].text).toContain("Image generation failed");
162
160
  });
163
- });
161
+ });
@@ -2,7 +2,6 @@
2
2
  * Image generation tool implementation for SiliconFlow
3
3
  */
4
4
 
5
- import { z } from "zod";
6
5
  import { SiliconFlowService } from "../services/siliconflow.js";
7
6
  import { GenerateImageInputSchema, ToolResponse } from "../types/index.js";
8
7
  import { saveImageToFile, getTempDir } from "../utils/file.js";
@@ -10,7 +9,8 @@ import { saveImageToFile, getTempDir } from "../utils/file.js";
10
9
  export function createGenerateImageTool(service: SiliconFlowService) {
11
10
  return {
12
11
  name: "generate_image",
13
- description: "Generate images using SiliconFlow's AI models. Supports various aspect ratios, image sizes, negative prompts, and seeds for reproducible results. Images are saved to temporary files and paths are returned.",
12
+ description:
13
+ "Generate images using SiliconFlow's AI models. Supports various aspect ratios, image sizes, negative prompts, and seeds for reproducible results. Images are saved to temporary files and paths are returned.",
14
14
  inputSchema: GenerateImageInputSchema,
15
15
 
16
16
  handler: async (input: unknown): Promise<ToolResponse> => {
@@ -21,7 +21,7 @@ export function createGenerateImageTool(service: SiliconFlowService) {
21
21
  content: [
22
22
  {
23
23
  type: "text",
24
- text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(", ")}`,
24
+ text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(", ")}`,
25
25
  },
26
26
  ],
27
27
  isError: true,
@@ -38,7 +38,7 @@ export function createGenerateImageTool(service: SiliconFlowService) {
38
38
  imageSize,
39
39
  count,
40
40
  negativePrompt,
41
- seed
41
+ seed,
42
42
  );
43
43
 
44
44
  if (images.length === 0) {
@@ -68,10 +68,11 @@ export function createGenerateImageTool(service: SiliconFlowService) {
68
68
  content: [
69
69
  {
70
70
  type: "text",
71
- text: `Successfully generated ${images.length} image${images.length > 1 ? "s" : ""} for prompt: "${prompt}"\n` +
72
- `Saved to:\n${savedFiles.map(f => `- ${f}`).join("\n")}\n\n` +
73
- `Temporary directory: ${tempDir}\n` +
74
- `Note: These are temporary files. Use the file paths to access the images.`,
71
+ text:
72
+ `Successfully generated ${images.length} image${images.length > 1 ? "s" : ""} for prompt: "${prompt}"\n` +
73
+ `Saved to:\n${savedFiles.map((f) => `- ${f}`).join("\n")}\n\n` +
74
+ `Temporary directory: ${tempDir}\n` +
75
+ `Note: These are temporary files. Use the file paths to access the images.`,
75
76
  },
76
77
  ],
77
78
  };
@@ -90,4 +91,4 @@ export function createGenerateImageTool(service: SiliconFlowService) {
90
91
  }
91
92
  },
92
93
  };
93
- }
94
+ }
@@ -2,14 +2,14 @@
2
2
  * List image models tool implementation for SiliconFlow
3
3
  */
4
4
 
5
- import { z } from "zod";
6
5
  import { SiliconFlowService } from "../services/siliconflow.js";
7
6
  import { ListModelsInputSchema, ToolResponse } from "../types/index.js";
8
7
 
9
8
  export function createListModelsTool(service: SiliconFlowService) {
10
9
  return {
11
10
  name: "list_image_models",
12
- description: "List all available image generation models from SiliconFlow. Shows model IDs and capabilities.",
11
+ description:
12
+ "List all available image generation models from SiliconFlow. Shows model IDs and capabilities.",
13
13
  inputSchema: ListModelsInputSchema,
14
14
 
15
15
  handler: async (input: unknown): Promise<ToolResponse> => {
@@ -20,7 +20,7 @@ export function createListModelsTool(service: SiliconFlowService) {
20
20
  content: [
21
21
  {
22
22
  type: "text",
23
- text: `Invalid input: ${parsed.error.errors.map(e => e.message).join(", ")}`,
23
+ text: `Invalid input: ${parsed.error.errors.map((e) => e.message).join(", ")}`,
24
24
  },
25
25
  ],
26
26
  isError: true,
@@ -48,7 +48,9 @@ export function createListModelsTool(service: SiliconFlowService) {
48
48
  const parts = [
49
49
  `${index + 1}. **${model.name}** (\`${model.id}\`)`,
50
50
  model.description ? ` - ${model.description}` : null,
51
- model.output_modalities ? ` - Capabilities: ${model.output_modalities.join(", ")}` : null,
51
+ model.output_modalities
52
+ ? ` - Capabilities: ${model.output_modalities.join(", ")}`
53
+ : null,
52
54
  ].filter(Boolean);
53
55
 
54
56
  return parts.join("\n");
@@ -22,10 +22,7 @@ export const GenerateImageInputSchema = z.object({
22
22
  .optional()
23
23
  .describe("Aspect ratio for generated images"),
24
24
 
25
- imageSize: z
26
- .enum(["1K", "2K", "4K"])
27
- .optional()
28
- .describe("Image size (higher resolution)"),
25
+ imageSize: z.enum(["1K", "2K", "4K"]).optional().describe("Image size (higher resolution)"),
29
26
 
30
27
  count: z
31
28
  .number()
@@ -36,10 +33,7 @@ export const GenerateImageInputSchema = z.object({
36
33
  .default(1)
37
34
  .describe("Number of images to generate (1-4)"),
38
35
 
39
- negativePrompt: z
40
- .string()
41
- .optional()
42
- .describe("Negative prompt - what to avoid in the image"),
36
+ negativePrompt: z.string().optional().describe("Negative prompt - what to avoid in the image"),
43
37
 
44
38
  seed: z
45
39
  .number()
@@ -80,11 +74,11 @@ export type ListModelsInput = z.infer<typeof ListModelsInputSchema>;
80
74
 
81
75
  // Image result type
82
76
  export interface ImageResult {
83
- data: string; // Base64 encoded image data
84
- mimeType: string; // e.g., "image/png"
85
- size?: number; // File size in bytes
86
- width?: number; // Image width in pixels
87
- height?: number; // Image height in pixels
77
+ data: string; // Base64 encoded image data
78
+ mimeType: string; // e.g., "image/png"
79
+ size?: number; // File size in bytes
80
+ width?: number; // Image width in pixels
81
+ height?: number; // Image height in pixels
88
82
  }
89
83
 
90
84
  // Model information type
@@ -112,4 +106,4 @@ export interface ToolResponse {
112
106
  name?: string;
113
107
  }>;
114
108
  isError?: boolean;
115
- }
109
+ }
package/src/utils/file.ts CHANGED
@@ -28,7 +28,7 @@ function getImageBaseDir(): string {
28
28
  export async function saveImageToFile(
29
29
  base64Data: string,
30
30
  prefix: string,
31
- mimeType: string
31
+ mimeType: string,
32
32
  ): Promise<string> {
33
33
  const tempDir = getImageBaseDir();
34
34
  await fs.mkdir(tempDir, { recursive: true });
@@ -38,7 +38,7 @@ export async function saveImageToFile(
38
38
  const filename = `${prefix}_${timestamp}.${extension}`;
39
39
  const filepath = path.join(tempDir, filename);
40
40
 
41
- const buffer = Buffer.from(base64Data, 'base64');
41
+ const buffer = Buffer.from(base64Data, "base64");
42
42
  await fs.writeFile(filepath, buffer);
43
43
 
44
44
  return filepath;