legends810 commited on
Commit
60c54e6
·
verified ·
1 Parent(s): 6b6cf0f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +263 -275
app.py CHANGED
@@ -1,16 +1,22 @@
1
  from flask import Flask, jsonify, request, Response
2
  import json
3
- import time
4
  import requests
5
- import threading
6
  import os
7
  from datetime import datetime
8
  import uuid
 
9
 
10
  app = Flask(__name__)
11
 
12
- # Global variables for MCP
13
- mcp_tools = [
 
 
 
 
 
 
 
14
  {
15
  "name": "generate_image",
16
  "description": "Generate images from text prompts using Pollinations AI",
@@ -22,18 +28,23 @@ mcp_tools = [
22
  "description": "Text description of the image to generate"
23
  },
24
  "model": {
25
- "type": "string",
26
  "description": "Model to use (flux, gptimage, etc.)",
 
27
  "default": "flux"
28
  },
29
  "width": {
30
- "type": "number",
31
- "description": "Image width",
 
 
32
  "default": 1024
33
  },
34
  "height": {
35
- "type": "number",
36
- "description": "Image height",
 
 
37
  "default": 1024
38
  }
39
  },
@@ -52,8 +63,16 @@ mcp_tools = [
52
  },
53
  "model": {
54
  "type": "string",
55
- "description": "Model to use (openai, etc.)",
 
56
  "default": "openai"
 
 
 
 
 
 
 
57
  }
58
  },
59
  "required": ["prompt"]
@@ -61,293 +80,262 @@ mcp_tools = [
61
  }
62
  ]
63
 
64
- @app.route('/')
65
- def home():
66
- return """
67
- <!DOCTYPE html>
68
- <html>
69
- <head>
70
- <title>MCPollinations MCP Server</title>
71
- <style>
72
- body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
73
- .container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
74
- .status { color: #28a745; font-weight: bold; }
75
- .endpoint { background: #f8f9fa; padding: 15px; margin: 10px 0; border-left: 4px solid #007bff; }
76
- .method { background: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; }
77
- ul { list-style-type: none; padding: 0; }
78
- li { margin: 10px 0; }
79
- a { color: #007bff; text-decoration: none; }
80
- a:hover { text-decoration: underline; }
81
- </style>
82
- </head>
83
- <body>
84
- <div class="container">
85
- <h1>🤖 MCPollinations MCP Server</h1>
86
- <p class="status">✅ Server is running on Hugging Face Spaces!</p>
87
- <p>This is a Model Context Protocol server for AI image and text generation.</p>
88
-
89
- <h2>📋 Available endpoints:</h2>
90
- <div class="endpoint">
91
- <span class="method">GET</span>
92
- <strong><a href="/status">/status</a></strong> - Check server status
93
- </div>
94
- <div class="endpoint">
95
- <span class="method">POST</span>
96
- <strong>/generate_image</strong> - Generate images via POST
97
- </div>
98
- <div class="endpoint">
99
- <span class="method">POST</span>
100
- <strong>/generate_text</strong> - Generate text via POST
101
- </div>
102
- <div class="endpoint">
103
- <span class="method">GET</span>
104
- <strong>/tools</strong> - List available MCP tools
105
- </div>
106
- <div class="endpoint">
107
- <span class="method">GET</span>
108
- <strong>/sse</strong> - MCP Server-Sent Events endpoint
109
- </div>
110
- <div class="endpoint">
111
- <span class="method">GET</span>
112
- <strong>/gradio_api/mcp/sse</strong> - Gradio-compatible MCP SSE endpoint
113
- </div>
114
-
115
- <h3>🔗 For n8n Integration:</h3>
116
- <p><strong>SSE URL:</strong> <code>https://huggingface.co/spaces/legends810/mcp-server/sse</code></p>
117
- <p><strong>HTTP API Base:</strong> <code>https://huggingface.co/spaces/legends810/mcp-server</code></p>
118
-
119
- <h3>📝 Example Usage:</h3>
120
- <pre style="background: #f1f1f1; padding: 10px; border-radius: 5px;">
121
- curl -X POST https://huggingface.co/spaces/legends810/mcp-server/generate_image \\
122
- -H "Content-Type: application/json" \\
123
- -d '{"prompt": "beautiful sunset over mountains"}'
124
- </pre>
125
- </div>
126
- </body>
127
- </html>
128
- """
129
 
130
- @app.route('/status')
131
- def status():
132
- return jsonify({
133
- "status": "running",
134
- "server": "MCPollinations MCP Server",
135
- "mode": "HTTP + SSE wrapper for HF Spaces",
136
- "endpoints": ["/status", "/generate_image", "/generate_text", "/tools", "/sse"],
137
- "timestamp": datetime.now().isoformat(),
138
- "version": "1.0.0"
139
- })
 
 
140
 
141
- @app.route('/tools')
142
- def get_tools():
143
- return jsonify({
144
- "tools": mcp_tools
145
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
- @app.route('/generate_image', methods=['POST'])
148
- def generate_image():
 
149
  try:
150
- data = request.get_json() or {}
151
- prompt = data.get('prompt', 'sunset over ocean')
152
- model = data.get('model', 'flux')
153
- width = data.get('width', 1024)
154
- height = data.get('height', 1024)
155
-
156
- # Build Pollinations image URL
157
- params = {
158
- 'width': width,
159
- 'height': height,
160
- 'model': model
161
- }
162
-
163
- # Remove empty params
164
- params = {k: v for k, v in params.items() if v}
165
-
166
- # Create image URL
167
- image_url = f"https://image.pollinations.ai/{requests.utils.quote(prompt)}"
168
- if params:
169
- param_string = "&".join([f"{k}={v}" for k, v in params.items()])
170
- image_url += f"?{param_string}"
171
 
172
- result = {
173
- "status": "success",
174
- "prompt": prompt,
175
- "model": model,
176
- "dimensions": {"width": width, "height": height},
177
- "image_url": image_url,
178
- "download_url": image_url,
179
- "message": "Image generated successfully",
180
- "timestamp": datetime.now().isoformat(),
181
- "id": str(uuid.uuid4())
182
- }
183
- return jsonify(result)
184
  except Exception as e:
185
- return jsonify({
186
- "status": "error",
187
- "error": str(e),
188
- "message": "Failed to generate image"
189
- }), 500
190
 
191
- @app.route('/generate_text', methods=['POST'])
192
- def generate_text():
 
193
  try:
194
- data = request.get_json() or {}
195
- prompt = data.get('prompt', 'Hello world')
196
- model = data.get('model', 'openai')
197
-
198
- # Build Pollinations text URL
199
- text_url = f"https://text.pollinations.ai/{requests.utils.quote(prompt)}?model={model}"
200
-
201
- # Make request to Pollinations API
202
- try:
203
- response = requests.get(text_url, timeout=30)
204
- generated_text = response.text
205
- except:
206
- generated_text = f"Generated response for: {prompt} (using {model} model)"
207
 
208
- result = {
209
- "status": "success",
210
- "prompt": prompt,
211
- "model": model,
212
- "generated_text": generated_text,
213
- "message": "Text generated successfully",
214
- "timestamp": datetime.now().isoformat(),
215
- "id": str(uuid.uuid4())
216
- }
217
- return jsonify(result)
218
  except Exception as e:
219
- return jsonify({
220
- "status": "error",
221
- "error": str(e),
222
- "message": "Failed to generate text"
223
- }), 500
224
-
225
- # MCP SSE Endpoints
226
- @app.route('/sse')
227
- @app.route('/gradio_api/mcp/sse')
228
- def mcp_sse():
229
- def generate_mcp_events():
230
- # Send initial MCP handshake
231
- yield "event: connect\n"
232
- yield f"data: " + json.dumps({
233
- "jsonrpc": "2.0",
234
- "method": "initialize",
235
- "params": {
236
- "protocolVersion": "2024-11-05",
237
- "capabilities": {
238
- "tools": {
239
- "listChanged": True
240
- },
241
- "resources": {},
242
- "prompts": {}
243
- },
244
- "serverInfo": {
245
- "name": "MCPollinations",
246
- "version": "1.0.0"
247
- }
248
- }
249
- }) + "\n\n"
250
-
251
- # Send available tools
252
- yield "event: tools\n"
253
- yield f"data: " + json.dumps({
254
- "jsonrpc": "2.0",
255
- "method": "tools/list",
256
- "result": {
257
- "tools": mcp_tools
258
- }
259
- }) + "\n\n"
260
-
261
- # Keep connection alive with heartbeat
262
- while True:
263
- try:
264
- time.sleep(30) # Send heartbeat every 30 seconds
265
- yield "event: ping\n"
266
- yield f"data: " + json.dumps({
267
- "type": "ping",
268
- "timestamp": datetime.now().isoformat()
269
- }) + "\n\n"
270
- except:
271
- break
272
-
273
- return Response(
274
- generate_mcp_events(),
275
- mimetype='text/event-stream',
276
- headers={
277
- 'Cache-Control': 'no-cache',
278
- 'Connection': 'keep-alive',
279
- 'Access-Control-Allow-Origin': '*',
280
- 'Access-Control-Allow-Methods': 'GET',
281
- 'Access-Control-Allow-Headers': 'Cache-Control'
282
- }
283
- )
284
 
285
- # MCP Tool execution endpoint
286
- @app.route('/mcp/call', methods=['POST'])
287
- def mcp_call_tool():
 
288
  try:
289
  data = request.get_json()
290
- tool_name = data.get('name')
291
- arguments = data.get('arguments', {})
 
 
292
 
293
  if tool_name == 'generate_image':
294
- # Reuse the generate_image logic
295
- prompt = arguments.get('prompt', 'sunset')
296
- model = arguments.get('model', 'flux')
297
- width = arguments.get('width', 1024)
298
- height = arguments.get('height', 1024)
299
-
300
- params = {'width': width, 'height': height, 'model': model}
301
- params = {k: v for k, v in params.items() if v}
302
-
303
- image_url = f"https://image.pollinations.ai/{requests.utils.quote(prompt)}"
304
- if params:
305
- param_string = "&".join([f"{k}={v}" for k, v in params.items()])
306
- image_url += f"?{param_string}"
307
-
308
- return jsonify({
309
- "content": [
310
- {
311
- "type": "image",
312
- "data": image_url,
313
- "mimeType": "image/png"
314
- },
315
- {
316
- "type": "text",
317
- "text": f"Generated image: {prompt}\nModel: {model}\nURL: {image_url}"
318
- }
319
- ]
320
- })
321
-
322
  elif tool_name == 'generate_text':
323
- prompt = arguments.get('prompt', 'Hello')
324
- model = arguments.get('model', 'openai')
325
-
326
- text_url = f"https://text.pollinations.ai/{requests.utils.quote(prompt)}?model={model}"
327
- try:
328
- response = requests.get(text_url, timeout=30)
329
- generated_text = response.text
330
- except:
331
- generated_text = f"Generated response for: {prompt}"
332
-
333
- return jsonify({
334
- "content": [
335
- {
336
- "type": "text",
337
- "text": generated_text
338
- }
339
- ]
340
- })
341
  else:
342
- return jsonify({"error": "Unknown tool"}), 400
 
 
 
343
 
 
 
 
 
 
344
  except Exception as e:
345
- return jsonify({"error": str(e)}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
 
347
- # Health check endpoint
348
  @app.route('/health')
349
  def health():
350
- return jsonify({"status": "healthy", "timestamp": datetime.now().isoformat()})
351
 
352
  if __name__ == '__main__':
353
  port = int(os.environ.get('PORT', 7860))
 
1
  from flask import Flask, jsonify, request, Response
2
  import json
 
3
  import requests
 
4
  import os
5
  from datetime import datetime
6
  import uuid
7
+ from urllib.parse import quote
8
 
9
  app = Flask(__name__)
10
 
11
+ # MCP Server Info (HF Style)
12
+ SERVER_INFO = {
13
+ "name": "MCPollinations",
14
+ "version": "1.0.0",
15
+ "description": "MCP Server for AI Image and Text Generation via Pollinations"
16
+ }
17
+
18
+ # MCP Tools Definition (Official Format)
19
+ MCP_TOOLS = [
20
  {
21
  "name": "generate_image",
22
  "description": "Generate images from text prompts using Pollinations AI",
 
28
  "description": "Text description of the image to generate"
29
  },
30
  "model": {
31
+ "type": "string",
32
  "description": "Model to use (flux, gptimage, etc.)",
33
+ "enum": ["flux", "gptimage", "playground", "stable-diffusion"],
34
  "default": "flux"
35
  },
36
  "width": {
37
+ "type": "integer",
38
+ "description": "Image width in pixels",
39
+ "minimum": 256,
40
+ "maximum": 2048,
41
  "default": 1024
42
  },
43
  "height": {
44
+ "type": "integer",
45
+ "description": "Image height in pixels",
46
+ "minimum": 256,
47
+ "maximum": 2048,
48
  "default": 1024
49
  }
50
  },
 
63
  },
64
  "model": {
65
  "type": "string",
66
+ "description": "Text model to use",
67
+ "enum": ["openai", "claude", "gemini"],
68
  "default": "openai"
69
+ },
70
+ "max_tokens": {
71
+ "type": "integer",
72
+ "description": "Maximum tokens to generate",
73
+ "minimum": 1,
74
+ "maximum": 4000,
75
+ "default": 1000
76
  }
77
  },
78
  "required": ["prompt"]
 
80
  }
81
  ]
82
 
83
+ def is_browser_request(request):
84
+ """Detect if request is from browser (HF Style)"""
85
+ user_agent = request.headers.get('User-Agent', '').lower()
86
+ accept = request.headers.get('Accept', '').lower()
87
+ return 'text/html' in accept or 'mozilla' in user_agent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ def create_mcp_response(id=None, result=None, error=None):
90
+ """Create JSON-RPC 2.0 MCP response"""
91
+ response = {
92
+ "jsonrpc": "2.0"
93
+ }
94
+ if id is not None:
95
+ response["id"] = id
96
+ if result is not None:
97
+ response["result"] = result
98
+ if error is not None:
99
+ response["error"] = error
100
+ return response
101
 
102
+ @app.route('/')
103
+ @app.route('/mcp')
104
+ def root():
105
+ """Main MCP endpoint (HF Style)"""
106
+ if is_browser_request(request):
107
+ # Return user-friendly page for browsers
108
+ return """
109
+ <!DOCTYPE html>
110
+ <html>
111
+ <head>
112
+ <title>MCPollinations MCP Server</title>
113
+ <meta charset="utf-8">
114
+ <style>
115
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
116
+ margin: 0; padding: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
117
+ color: white; min-height: 100vh; }
118
+ .container { max-width: 800px; margin: 0 auto; background: rgba(255,255,255,0.1);
119
+ padding: 40px; border-radius: 20px; backdrop-filter: blur(10px); }
120
+ h1 { font-size: 3em; margin-bottom: 20px; text-align: center; }
121
+ .badge { background: rgba(255,255,255,0.2); padding: 8px 16px; border-radius: 20px;
122
+ display: inline-block; margin: 5px; }
123
+ .endpoint { background: rgba(255,255,255,0.1); padding: 20px; margin: 15px 0;
124
+ border-radius: 10px; border-left: 4px solid #00d4aa; }
125
+ .code { background: rgba(0,0,0,0.3); padding: 15px; border-radius: 8px;
126
+ font-family: 'Monaco', 'Courier New', monospace; margin: 10px 0; }
127
+ .status { color: #00d4aa; font-weight: bold; font-size: 1.2em; }
128
+ </style>
129
+ </head>
130
+ <body>
131
+ <div class="container">
132
+ <h1>🤖 MCPollinations MCP Server</h1>
133
+ <p class="status">✅ Server Status: Running</p>
134
+ <p>Official Model Context Protocol server for AI image and text generation via Pollinations AI.</p>
135
+
136
+ <h2>🛠️ Available Tools</h2>
137
+ <div class="endpoint">
138
+ <strong>generate_image</strong> - Generate images from text prompts
139
+ <div class="badge">Models: flux, gptimage, playground</div>
140
+ </div>
141
+ <div class="endpoint">
142
+ <strong>generate_text</strong> - Generate text using AI models
143
+ <div class="badge">Models: openai, claude, gemini</div>
144
+ </div>
145
+
146
+ <h2>🔗 MCP Client Setup</h2>
147
+ <p><strong>Server URL:</strong></p>
148
+ <div class="code">https://huggingface.co/spaces/legends810/mcp-server</div>
149
+
150
+ <h2>📚 Integration Examples</h2>
151
+ <div class="endpoint">
152
+ <strong>Claude Desktop</strong>
153
+ <div class="code">{
154
+ "mcpServers": {
155
+ "mcpollinations": {
156
+ "command": "npx",
157
+ "args": ["mcp-remote", "https://huggingface.co/spaces/legends810/mcp-server"]
158
+ }
159
+ }
160
+ }</div>
161
+ </div>
162
+
163
+ <div class="endpoint">
164
+ <strong>n8n MCP Client</strong>
165
+ <div class="code">URL: https://huggingface.co/spaces/legends810/mcp-server
166
+ Transport: HTTP</div>
167
+ </div>
168
+
169
+ <h2>🚀 Quick Test</h2>
170
+ <div class="code">curl -X POST https://huggingface.co/spaces/legends810/mcp-server/call \\
171
+ -H "Content-Type: application/json" \\
172
+ -d '{
173
+ "jsonrpc": "2.0",
174
+ "method": "tools/call",
175
+ "params": {
176
+ "name": "generate_image",
177
+ "arguments": {"prompt": "beautiful sunset"}
178
+ },
179
+ "id": 1
180
+ }'</div>
181
+ </div>
182
+ </body>
183
+ </html>
184
+ """
185
+ else:
186
+ # Return MCP protocol response for clients
187
+ return jsonify(create_mcp_response(
188
+ result={
189
+ "protocolVersion": "2024-11-05",
190
+ "capabilities": {
191
+ "tools": {"listChanged": True},
192
+ "logging": {}
193
+ },
194
+ "serverInfo": SERVER_INFO
195
+ }
196
+ ))
197
 
198
+ @app.route('/initialize', methods=['POST'])
199
+ def initialize():
200
+ """MCP Initialize endpoint"""
201
  try:
202
+ data = request.get_json()
203
+ request_id = data.get('id')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
+ return jsonify(create_mcp_response(
206
+ id=request_id,
207
+ result={
208
+ "protocolVersion": "2024-11-05",
209
+ "capabilities": {
210
+ "tools": {"listChanged": True},
211
+ "logging": {}
212
+ },
213
+ "serverInfo": SERVER_INFO
214
+ }
215
+ ))
 
216
  except Exception as e:
217
+ return jsonify(create_mcp_response(
218
+ error={"code": -32603, "message": f"Internal error: {str(e)}"}
219
+ )), 500
 
 
220
 
221
+ @app.route('/tools/list', methods=['POST'])
222
+ def tools_list():
223
+ """MCP Tools List endpoint"""
224
  try:
225
+ data = request.get_json()
226
+ request_id = data.get('id')
 
 
 
 
 
 
 
 
 
 
 
227
 
228
+ return jsonify(create_mcp_response(
229
+ id=request_id,
230
+ result={"tools": MCP_TOOLS}
231
+ ))
 
 
 
 
 
 
232
  except Exception as e:
233
+ return jsonify(create_mcp_response(
234
+ error={"code": -32603, "message": f"Internal error: {str(e)}"}
235
+ )), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ @app.route('/tools/call', methods=['POST'])
238
+ @app.route('/call', methods=['POST'])
239
+ def tools_call():
240
+ """MCP Tools Call endpoint (Main functionality)"""
241
  try:
242
  data = request.get_json()
243
+ request_id = data.get('id')
244
+ params = data.get('params', {})
245
+ tool_name = params.get('name')
246
+ arguments = params.get('arguments', {})
247
 
248
  if tool_name == 'generate_image':
249
+ result = handle_image_generation(arguments)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  elif tool_name == 'generate_text':
251
+ result = handle_text_generation(arguments)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  else:
253
+ return jsonify(create_mcp_response(
254
+ id=request_id,
255
+ error={"code": -32601, "message": f"Unknown tool: {tool_name}"}
256
+ )), 400
257
 
258
+ return jsonify(create_mcp_response(
259
+ id=request_id,
260
+ result=result
261
+ ))
262
+
263
  except Exception as e:
264
+ return jsonify(create_mcp_response(
265
+ error={"code": -32603, "message": f"Tool execution failed: {str(e)}"}
266
+ )), 500
267
+
268
+ def handle_image_generation(arguments):
269
+ """Handle image generation with Pollinations API"""
270
+ prompt = arguments.get('prompt', 'beautiful landscape')
271
+ model = arguments.get('model', 'flux')
272
+ width = arguments.get('width', 1024)
273
+ height = arguments.get('height', 1024)
274
+
275
+ # Build Pollinations URL
276
+ params = []
277
+ if width != 1024:
278
+ params.append(f"width={width}")
279
+ if height != 1024:
280
+ params.append(f"height={height}")
281
+ if model != 'flux':
282
+ params.append(f"model={model}")
283
+
284
+ image_url = f"https://image.pollinations.ai/{quote(prompt)}"
285
+ if params:
286
+ image_url += "?" + "&".join(params)
287
+
288
+ return {
289
+ "content": [
290
+ {
291
+ "type": "image",
292
+ "data": image_url,
293
+ "mimeType": "image/png"
294
+ },
295
+ {
296
+ "type": "text",
297
+ "text": f"Generated image: {prompt}\nModel: {model}\nDimensions: {width}x{height}\nURL: {image_url}"
298
+ }
299
+ ]
300
+ }
301
+
302
+ def handle_text_generation(arguments):
303
+ """Handle text generation with Pollinations API"""
304
+ prompt = arguments.get('prompt', 'Hello world')
305
+ model = arguments.get('model', 'openai')
306
+ max_tokens = arguments.get('max_tokens', 1000)
307
+
308
+ # Call Pollinations text API
309
+ try:
310
+ text_url = f"https://text.pollinations.ai/{quote(prompt)}?model={model}"
311
+ response = requests.get(text_url, timeout=30)
312
+ generated_text = response.text[:max_tokens] # Limit tokens
313
+ except:
314
+ generated_text = f"Generated response for: {prompt} (using {model} model)"
315
+
316
+ return {
317
+ "content": [
318
+ {
319
+ "type": "text",
320
+ "text": generated_text
321
+ }
322
+ ]
323
+ }
324
+
325
+ # Legacy endpoints for backward compatibility
326
+ @app.route('/status')
327
+ def status():
328
+ return jsonify({
329
+ "status": "running",
330
+ "server": SERVER_INFO["name"],
331
+ "version": SERVER_INFO["version"],
332
+ "mcp_protocol": "2024-11-05",
333
+ "timestamp": datetime.now().isoformat()
334
+ })
335
 
 
336
  @app.route('/health')
337
  def health():
338
+ return jsonify({"status": "healthy"})
339
 
340
  if __name__ == '__main__':
341
  port = int(os.environ.get('PORT', 7860))