#
tokens: 5500/50000 5/5 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitattributes
├── .gitignore
├── dist
│   ├── index.d.ts
│   └── index.js
├── icon.png
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── text-audio-api.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
# Auto detect text files and perform LF normalization
* text=auto

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "declaration": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "pollinations-mcp",
  "version": "1.0.0",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "mcp",
    "pollinations",
    "ai",
    "image",
    "generation"
  ],
  "author": "bendusy",
  "license": "ISC",
  "description": "使用MCP协议连接Pollinations.ai的图像生成服务",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "axios": "^1.8.1"
  },
  "devDependencies": {
    "@types/node": "^22.13.9",
    "typescript": "^5.8.2"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/bendusy/pollinations-mcp.git"
  },
  "homepage": "https://github.com/bendusy/pollinations-mcp",
  "bugs": {
    "url": "https://github.com/bendusy/pollinations-mcp/issues"
  },
  "icon": "icon.png"
}

```

--------------------------------------------------------------------------------
/src/text-audio-api.ts:
--------------------------------------------------------------------------------

```typescript
import axios from 'axios';
import * as fs from 'fs';
import * as path from 'path';

/**
 * Pollinations文本API实现
 */
export class PollinationsTextAudioAPI {
  private baseTextUrl = 'https://text.pollinations.ai';

  /**
   * 生成文本 (GET方法)
   * @param prompt 提示词
   * @param options 选项
   * @returns 生成的文本
   */
  async generateTextGet(prompt: string, options: {
    model?: string;
    seed?: number;
    json?: boolean;
    system?: string;
    private?: boolean;
  } = {}) {
    const { model = 'openai', seed, json = false, system, private: isPrivate = false } = options;
    
    let url = `${this.baseTextUrl}/${encodeURIComponent(prompt)}?model=${model}`;
    
    if (seed !== undefined) {
      url += `&seed=${seed}`;
    }
    
    if (json) {
      url += `&json=true`;
    }
    
    if (system) {
      url += `&system=${encodeURIComponent(system)}`;
    }
    
    if (isPrivate) {
      url += `&private=true`;
    }
    
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error(`文本生成失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 生成文本 (POST方法)
   * @param messages 消息数组
   * @param options 选项
   * @returns 生成的文本
   */
  async generateTextPost(messages: Array<{role: string, content: string | Array<{type: string, text?: string, image_url?: {url: string}}>}>, options: {
    model?: string;
    seed?: number;
    jsonMode?: boolean;
    private?: boolean;
  } = {}) {
    const { model = 'openai', seed, jsonMode = false, private: isPrivate = false } = options;
    
    try {
      const response = await axios.post(`${this.baseTextUrl}/`, {
        messages,
        model,
        seed,
        jsonMode,
        private: isPrivate
      });
      return response.data;
    } catch (error) {
      throw new Error(`文本生成失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 获取可用模型列表
   * @param type 模型类型 ('text')
   * @returns 模型列表
   */
  async getAvailableModels(type: 'text' = 'text') {
    try {
      const url = `${this.baseTextUrl}/models`;
      
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error(`获取模型列表失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }
} 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import * as fs from 'fs';
import * as path from 'path';

// 简化的错误类型枚举
enum PollinationsErrorType {
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  API_ERROR = 'API_ERROR',
  FILE_SYSTEM_ERROR = 'FILE_SYSTEM_ERROR'
}

// 简化的错误类
class PollinationsError extends Error {
  type: PollinationsErrorType;
  statusCode?: number;
  
  constructor(message: string, type: PollinationsErrorType, statusCode?: number) {
    super(message);
    this.name = 'PollinationsError';
    this.type = type;
    this.statusCode = statusCode;
  }
  
  toUserFriendlyMessage(): string {
    return `错误: ${this.message}${this.statusCode ? ` (状态码: ${this.statusCode})` : ''}`;
  }
}

// Pollinations文本API实现
class PollinationsTextAPI {
  private baseTextUrl = 'https://text.pollinations.ai';

  /**
   * 生成文本 (GET方法)
   * @param prompt 提示词
   * @param options 选项
   * @returns 生成的文本
   */
  async generateTextGet(prompt: string, options: {
    model?: string;
    seed?: number;
    json?: boolean;
    system?: string;
    private?: boolean;
  } = {}) {
    const { model = 'openai', seed, json = false, system, private: isPrivate = false } = options;
    
    let url = `${this.baseTextUrl}/${encodeURIComponent(prompt)}?model=${model}`;
    
    if (seed !== undefined) {
      url += `&seed=${seed}`;
    }
    
    if (json) {
      url += `&json=true`;
    }
    
    if (system) {
      url += `&system=${encodeURIComponent(system)}`;
    }
    
    if (isPrivate) {
      url += `&private=true`;
    }
    
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error(`文本生成失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }

  /**
   * 获取可用模型列表
   * @returns 模型列表
   */
  async getAvailableModels() {
    try {
      const url = `${this.baseTextUrl}/models`;
      
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error(`获取模型列表失败: ${error instanceof Error ? error.message : String(error)}`);
    }
  }
}

// Pollinations.ai 服务器实现
class PollinationsServer {
  private server: Server;
  private baseUrl = 'https://image.pollinations.ai';
  private textAPI: PollinationsTextAPI;

  constructor() {
    this.server = new Server(
      {
        name: 'pollinations-mcp-server',
        version: '0.1.0',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    // 初始化文本API
    this.textAPI = new PollinationsTextAPI();
    
    // 设置工具处理器
    this.setupToolHandlers();
    
    // 错误处理
    this.server.onerror = (error) => console.error('[MCP Error]', error);
    process.on('SIGINT', async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  // 错误处理方法
  private handleValidationError(message: string): PollinationsError {
    return new PollinationsError(message, PollinationsErrorType.VALIDATION_ERROR, 400);
  }
  
  private handleApiError(error: any): PollinationsError {
    if (axios.isAxiosError(error)) {
      const statusCode = error.response?.status || 0;
      const message = error.response?.data?.message || error.message;
      return new PollinationsError(`API错误: ${message}`, PollinationsErrorType.API_ERROR, statusCode);
    }
    return new PollinationsError(`未知错误: ${error.message || error}`, PollinationsErrorType.API_ERROR, 0);
  }
  
  private handleFileSystemError(error: any, operation: string): PollinationsError {
    return new PollinationsError(`文件操作失败 (${operation}): ${error.message || error}`, PollinationsErrorType.FILE_SYSTEM_ERROR);
  }

  // 设置MCP工具处理器
  private setupToolHandlers() {
    // 列出可用工具
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'generate_image',
          description: '使用Pollinations.ai生成图像',
          inputSchema: {
            type: 'object',
            properties: {
              prompt: {
                type: 'string',
                description: '图像描述提示词',
              },
              width: {
                type: 'number',
                description: '图像宽度(像素)',
                default: 1024,
              },
              height: {
                type: 'number',
                description: '图像高度(像素)',
                default: 1024,
              },
              seed: {
                type: 'number',
                description: '随机种子值(用于生成一致的图像)',
              },
              model: {
                type: 'string',
                description: '要使用的模型(如flux、variation等)',
                default: 'flux',
              },
              nologo: {
                type: 'boolean',
                description: '设置为true可去除水印',
                default: true,
              },
              enhance: {
                type: 'boolean',
                description: '提高图像质量(应用增强滤镜)',
                default: false,
              },
              safe: {
                type: 'boolean',
                description: '启用安全过滤(过滤不适内容)',
                default: false,
              },
              private: {
                type: 'boolean',
                description: '设置为true可使图像私有',
                default: false,
              },
            },
            required: ['prompt'],
          },
        },
        {
          name: 'download_image',
          description: '下载Pollinations.ai生成的图像到本地文件',
          inputSchema: {
            type: 'object',
            properties: {
              url: {
                type: 'string',
                description: '要下载的图像URL',
              },
              output_path: {
                type: 'string',
                description: '保存图像的路径(包括文件名)',
                default: 'image.jpg',
              },
            },
            required: ['url'],
          },
        },
        {
          name: 'generate_text',
          description: '使用Pollinations.ai生成文本',
          inputSchema: {
            type: 'object',
            properties: {
              prompt: {
                type: 'string',
                description: '文本提示词',
              },
              model: {
                type: 'string',
                description: '要使用的模型(如openai、mistral等)',
                default: 'openai',
              },
              seed: {
                type: 'number',
                description: '随机种子值(用于生成一致的结果)',
              },
              system: {
                type: 'string',
                description: '系统提示词(设置AI行为)',
              },
              json: {
                type: 'boolean',
                description: '是否返回JSON格式的响应',
                default: false,
              },
              private: {
                type: 'boolean',
                description: '设置为true可使响应私有',
                default: false,
              },
            },
            required: ['prompt'],
          },
        },
      ],
    }));
    
    // 处理工具调用
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case 'generate_image':
          return this.handleGenerateImage(request.params.arguments);
        case 'download_image':
          return this.handleDownloadImage(request.params.arguments);
        case 'generate_text':
          return this.handleGenerateText(request.params.arguments);
        default:
          throw new McpError(
            ErrorCode.MethodNotFound,
            `未知工具: ${request.params.name}`
          );
      }
    });
  }

  // 处理生成图像请求
  private async handleGenerateImage(args: any) {
    try {
      if (!this.isValidGenerateImageArgs(args)) {
        throw this.handleValidationError('无效的图像生成参数');
      }

      const { 
        prompt, 
        width = 1024, 
        height = 1024, 
        seed, 
        model = 'flux', 
        nologo = true,
        enhance = false,
        safe = false,
        private: isPrivate = false
      } = args;
      
      // 检查提示词是否为英文
      const isMainlyEnglish = this.isMainlyEnglish(prompt);
      const isConcise = prompt.length <= 200;
      
      let promptFeedback = '';
      
      if (!isMainlyEnglish) {
        promptFeedback += '提示:Pollinations.ai对英文提示词的理解更好,建议使用英文编写提示词。\n';
      }
      
      if (!isConcise) {
        promptFeedback += '提示:提示词过长可能影响生成效果,建议保持简短精确(建议不超过200字符)。\n';
      }
      
      // 构建Pollinations URL(使用官方路径格式)
      let imageUrl = `${this.baseUrl}/prompt/${encodeURIComponent(prompt)}?width=${width}&height=${height}`;
      
      if (seed !== undefined) {
        imageUrl += `&seed=${seed}`;
      }
      
      if (model) {
        imageUrl += `&model=${model}`;
      }

      if (nologo) {
        imageUrl += `&nologo=true`;
      }

      // 添加新参数支持
      if (enhance) {
        imageUrl += `&enhance=true`;
      }

      if (safe) {
        imageUrl += `&safe=true`;
      }

      if (isPrivate) {
        imageUrl += `&private=true`;
      }

      // 验证URL是否有效
      try {
        // 发送HEAD请求检查URL是否可访问(不下载完整图像)
        await axios.head(imageUrl);
      } catch (error) {
        // 处理API错误
        const pollinationsError = this.handleApiError(error);
        
        // 特殊处理安全过滤错误
        if (pollinationsError.statusCode === 400 && safe) {
          throw new PollinationsError(
            '内容被安全过滤拦截,请修改提示词后重试',
            PollinationsErrorType.VALIDATION_ERROR,
            400
          );
        }
        
        throw pollinationsError;
      }

      const response = {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              url: imageUrl,
              prompt,
              width,
              height,
              seed,
              model,
              nologo,
              enhance,
              safe,
              private: isPrivate
            }, null, 2),
          },
        ],
      };
      
      // 如果有提示反馈,添加到响应中
      if (promptFeedback) {
        response.content.unshift({
          type: 'text',
          text: promptFeedback
        });
      }
      
      return response;
    } catch (error) {
      // 处理所有错误
      let pollinationsError: PollinationsError;
      
      if (error instanceof PollinationsError) {
        pollinationsError = error;
      } else {
        pollinationsError = this.handleApiError(error);
      }
      
      return {
        content: [
          {
            type: 'text',
            text: pollinationsError.toUserFriendlyMessage(),
          },
        ],
        isError: true,
      };
    }
  }

  // 检查文本是否主要为英文
  private isMainlyEnglish(text: string): boolean {
    // 英文字符(包括空格和标点)的正则表达式
    const englishRegex = /^[A-Za-z0-9\s.,;:'"!?()-]+$/;
    
    // 如果完全匹配英文字符,返回true
    if (englishRegex.test(text)) {
      return true;
    }
    
    // 否则计算非英文字符的比例
    const nonEnglishChars = text.split('').filter(char => !char.match(/[A-Za-z0-9\s.,;:'"!?()-]/)).length;
    const totalChars = text.length;
    
    // 如果非英文字符少于20%,仍然视为主要是英文
    return (nonEnglishChars / totalChars) < 0.2;
  }

  // 处理下载图像请求
  private async handleDownloadImage(args: any) {
    try {
      if (!this.isValidDownloadImageArgs(args)) {
        throw this.handleValidationError('无效的图像下载参数');
      }

      // 获取参数
      let { url, output_path = 'image.jpg' } = args;
      
      // 使用相对路径保存图像到当前工作目录
      output_path = path.resolve(process.cwd(), output_path);
      console.error(`图像将保存到: ${output_path}`);

      try {
        // 确保输出目录存在
        const dirname = path.dirname(output_path);
        if (!fs.existsSync(dirname)) {
          console.error(`创建目录: ${dirname}`);
          fs.mkdirSync(dirname, { recursive: true });
        }
      } catch (fsError) {
        throw this.handleFileSystemError(fsError, '创建目录');
      }

      // 下载图像
      console.error(`开始下载图像: ${url}`);
      let response;
      try {
        response = await axios.get(url, { responseType: 'arraybuffer' });
      } catch (downloadError) {
        throw this.handleApiError(downloadError);
      }
      
      // 写入文件
      try {
        console.error(`写入文件: ${output_path}`);
        fs.writeFileSync(output_path, Buffer.from(response.data, 'binary'));
      } catch (writeError) {
        throw this.handleFileSystemError(writeError, '写入文件');
      }
      
      // 验证文件是否写入成功
      try {
        if (fs.existsSync(output_path)) {
          const fileSize = fs.statSync(output_path).size;
          console.error(`文件成功写入: ${output_path}, 大小: ${fileSize} 字节`);
          
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: true,
                  message: `图像已下载到 ${output_path}`,
                  size: fileSize,
                  path: output_path
                }, null, 2),
              },
            ],
          };
        } else {
          throw new PollinationsError(
            `文件写入失败: ${output_path}`,
            PollinationsErrorType.FILE_SYSTEM_ERROR
          );
        }
      } catch (verifyError) {
        if (verifyError instanceof PollinationsError) {
          throw verifyError;
        } else {
          throw this.handleFileSystemError(verifyError, '验证文件');
        }
      }
    } catch (error) {
      // 处理所有错误
      let pollinationsError: PollinationsError;
      
      if (error instanceof PollinationsError) {
        pollinationsError = error;
      } else {
        pollinationsError = this.handleApiError(error);
      }
      
      return {
        content: [
          {
            type: 'text',
            text: pollinationsError.toUserFriendlyMessage(),
          },
        ],
        isError: true,
      };
    }
  }

  // 验证生成图像参数
  private isValidGenerateImageArgs(args: any): args is {
    prompt: string;
    width?: number;
    height?: number;
    seed?: number;
    model?: string;
    nologo?: boolean;
    enhance?: boolean;
    safe?: boolean;
    private?: boolean;
  } {
    return (
      typeof args === 'object' &&
      args !== null &&
      typeof args.prompt === 'string' &&
      (args.width === undefined || typeof args.width === 'number') &&
      (args.height === undefined || typeof args.height === 'number') &&
      (args.seed === undefined || typeof args.seed === 'number') &&
      (args.model === undefined || typeof args.model === 'string') &&
      (args.nologo === undefined || typeof args.nologo === 'boolean') &&
      (args.enhance === undefined || typeof args.enhance === 'boolean') &&
      (args.safe === undefined || typeof args.safe === 'boolean') &&
      (args.private === undefined || typeof args.private === 'boolean')
    );
  }

  // 验证下载图像参数
  private isValidDownloadImageArgs(args: any): args is {
    url: string;
    output_path?: string;
  } {
    return (
      typeof args === 'object' &&
      args !== null &&
      typeof args.url === 'string' &&
      (args.output_path === undefined || typeof args.output_path === 'string')
    );
  }

  // 处理生成文本请求
  private async handleGenerateText(args: any) {
    try {
      if (!this.isValidGenerateTextArgs(args)) {
        throw this.handleValidationError('无效的文本生成参数');
      }

      const { prompt, model = 'openai', seed, system, json = false, private: isPrivate = false } = args;

      const result = await this.textAPI.generateTextGet(prompt, {
        model,
        seed,
        json,
        system,
        private: isPrivate
      });

      return {
        content: [
          {
            type: 'text',
            text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
          },
        ],
      };
    } catch (error) {
      // 处理所有错误
      let pollinationsError: PollinationsError;
      
      if (error instanceof PollinationsError) {
        pollinationsError = error;
      } else {
        pollinationsError = this.handleApiError(error);
      }
      
      return {
        content: [
          {
            type: 'text',
            text: pollinationsError.toUserFriendlyMessage(),
          },
        ],
        isError: true,
      };
    }
  }

  // 验证生成文本参数
  private isValidGenerateTextArgs(args: any): args is {
    prompt: string;
    model?: string;
    seed?: number;
    system?: string;
    json?: boolean;
    private?: boolean;
  } {
    return (
      typeof args === 'object' &&
      args !== null &&
      typeof args.prompt === 'string' &&
      (args.model === undefined || typeof args.model === 'string') &&
      (args.seed === undefined || typeof args.seed === 'number') &&
      (args.system === undefined || typeof args.system === 'string') &&
      (args.json === undefined || typeof args.json === 'boolean') &&
      (args.private === undefined || typeof args.private === 'boolean')
    );
  }

  // 运行服务器
  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('Pollinations MCP服务器正在通过stdio运行');
  }
}

// 创建并运行服务器
const server = new PollinationsServer();
server.run().catch(console.error);

```