from flask import Flask, jsonify, request, Response
import json
import requests
import os
from datetime import datetime
import uuid
from urllib.parse import quote
app = Flask(__name__)
# MCP Server Info (HF Style)
SERVER_INFO = {
"name": "MCPollinations",
"version": "1.0.0",
"description": "MCP Server for AI Image and Text Generation via Pollinations"
}
# MCP Tools Definition (Official Format)
MCP_TOOLS = [
{
"name": "generate_image",
"description": "Generate images from text prompts using Pollinations AI",
"inputSchema": {
"type": "object",
"properties": {
"prompt": {
"type": "string",
"description": "Text description of the image to generate"
},
"model": {
"type": "string",
"description": "Model to use (flux, gptimage, etc.)",
"enum": ["flux", "gptimage", "playground", "stable-diffusion"],
"default": "flux"
},
"width": {
"type": "integer",
"description": "Image width in pixels",
"minimum": 256,
"maximum": 2048,
"default": 1024
},
"height": {
"type": "integer",
"description": "Image height in pixels",
"minimum": 256,
"maximum": 2048,
"default": 1024
}
},
"required": ["prompt"]
}
},
{
"name": "generate_text",
"description": "Generate text responses using Pollinations AI",
"inputSchema": {
"type": "object",
"properties": {
"prompt": {
"type": "string",
"description": "Text prompt for generation"
},
"model": {
"type": "string",
"description": "Text model to use",
"enum": ["openai", "claude", "gemini"],
"default": "openai"
},
"max_tokens": {
"type": "integer",
"description": "Maximum tokens to generate",
"minimum": 1,
"maximum": 4000,
"default": 1000
}
},
"required": ["prompt"]
}
}
]
def is_browser_request(request):
"""Detect if request is from browser (HF Style)"""
user_agent = request.headers.get('User-Agent', '').lower()
accept = request.headers.get('Accept', '').lower()
return 'text/html' in accept or 'mozilla' in user_agent
def create_mcp_response(id=None, result=None, error=None):
"""Create JSON-RPC 2.0 MCP response"""
response = {
"jsonrpc": "2.0"
}
if id is not None:
response["id"] = id
if result is not None:
response["result"] = result
if error is not None:
response["error"] = error
return response
@app.route('/')
@app.route('/mcp')
def root():
"""Main MCP endpoint (HF Style)"""
if is_browser_request(request):
# Return user-friendly page for browsers
return """
MCPollinations MCP Server
🤖 MCPollinations MCP Server
✅ Server Status: Running
Official Model Context Protocol server for AI image and text generation via Pollinations AI.
🛠️ Available Tools
generate_image - Generate images from text prompts
Models: flux, gptimage, playground
generate_text - Generate text using AI models
Models: openai, claude, gemini
🔗 MCP Client Setup
Server URL:
https://huggingface.co/spaces/legends810/mcp-server
📚 Integration Examples
Claude Desktop
{
"mcpServers": {
"mcpollinations": {
"command": "npx",
"args": ["mcp-remote", "https://huggingface.co/spaces/legends810/mcp-server"]
}
}
}
n8n MCP Client
URL: https://huggingface.co/spaces/legends810/mcp-server
Transport: HTTP
🚀 Quick Test
curl -X POST https://huggingface.co/spaces/legends810/mcp-server/call \\
-H "Content-Type: application/json" \\
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "generate_image",
"arguments": {"prompt": "beautiful sunset"}
},
"id": 1
}'
"""
else:
# Return MCP protocol response for clients
return jsonify(create_mcp_response(
result={
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {"listChanged": True},
"logging": {}
},
"serverInfo": SERVER_INFO
}
))
@app.route('/initialize', methods=['POST'])
def initialize():
"""MCP Initialize endpoint"""
try:
data = request.get_json()
request_id = data.get('id')
return jsonify(create_mcp_response(
id=request_id,
result={
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {"listChanged": True},
"logging": {}
},
"serverInfo": SERVER_INFO
}
))
except Exception as e:
return jsonify(create_mcp_response(
error={"code": -32603, "message": f"Internal error: {str(e)}"}
)), 500
@app.route('/tools/list', methods=['POST'])
def tools_list():
"""MCP Tools List endpoint"""
try:
data = request.get_json()
request_id = data.get('id')
return jsonify(create_mcp_response(
id=request_id,
result={"tools": MCP_TOOLS}
))
except Exception as e:
return jsonify(create_mcp_response(
error={"code": -32603, "message": f"Internal error: {str(e)}"}
)), 500
@app.route('/tools/call', methods=['POST'])
@app.route('/call', methods=['POST'])
def tools_call():
"""MCP Tools Call endpoint (Main functionality)"""
try:
data = request.get_json()
request_id = data.get('id')
params = data.get('params', {})
tool_name = params.get('name')
arguments = params.get('arguments', {})
if tool_name == 'generate_image':
result = handle_image_generation(arguments)
elif tool_name == 'generate_text':
result = handle_text_generation(arguments)
else:
return jsonify(create_mcp_response(
id=request_id,
error={"code": -32601, "message": f"Unknown tool: {tool_name}"}
)), 400
return jsonify(create_mcp_response(
id=request_id,
result=result
))
except Exception as e:
return jsonify(create_mcp_response(
error={"code": -32603, "message": f"Tool execution failed: {str(e)}"}
)), 500
def handle_image_generation(arguments):
"""Handle image generation with Pollinations API"""
prompt = arguments.get('prompt', 'beautiful landscape')
model = arguments.get('model', 'flux')
width = arguments.get('width', 1024)
height = arguments.get('height', 1024)
# Build Pollinations URL
params = []
if width != 1024:
params.append(f"width={width}")
if height != 1024:
params.append(f"height={height}")
if model != 'flux':
params.append(f"model={model}")
image_url = f"https://image.pollinations.ai/{quote(prompt)}"
if params:
image_url += "?" + "&".join(params)
return {
"content": [
{
"type": "image",
"data": image_url,
"mimeType": "image/png"
},
{
"type": "text",
"text": f"Generated image: {prompt}\nModel: {model}\nDimensions: {width}x{height}\nURL: {image_url}"
}
]
}
def handle_text_generation(arguments):
"""Handle text generation with Pollinations API"""
prompt = arguments.get('prompt', 'Hello world')
model = arguments.get('model', 'openai')
max_tokens = arguments.get('max_tokens', 1000)
# Call Pollinations text API
try:
text_url = f"https://text.pollinations.ai/{quote(prompt)}?model={model}"
response = requests.get(text_url, timeout=30)
generated_text = response.text[:max_tokens] # Limit tokens
except:
generated_text = f"Generated response for: {prompt} (using {model} model)"
return {
"content": [
{
"type": "text",
"text": generated_text
}
]
}
# Legacy endpoints for backward compatibility
@app.route('/status')
def status():
return jsonify({
"status": "running",
"server": SERVER_INFO["name"],
"version": SERVER_INFO["version"],
"mcp_protocol": "2024-11-05",
"timestamp": datetime.now().isoformat()
})
@app.route('/health')
def health():
return jsonify({"status": "healthy"})
if __name__ == '__main__':
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port, debug=False, threaded=True)