返回提交历史
Modified
g4f/cli/__init__.py
+2
-1
Modified
g4f/gui/server/backend_api.py
+6
-4
Modified
g4f/mcp/server.py
+10
-5
Modified
g4f/mcp/tools.py
+11
-2
XFEstudio/gpt4free
Add CORS support to MCP server and tools; enhance image URL handling in tools
a4923529
代码差异
4 个文件
+29
-12
@@ -84,11 +84,12 @@ def get_mcp_parser():
84
84
mcp_parser.add_argument("--http", action="store_true", help="Use HTTP transport instead of stdio.")
85
85
mcp_parser.add_argument("--host", default="0.0.0.0", help="Host to bind HTTP server to (default: 0.0.0.0)")
86
86
mcp_parser.add_argument("--port", type=int, default=8765, help="Port to bind HTTP server to (default: 8765)")
87
mcp_parser.add_argument("--origin", type=str, default=None, help="Origin URL for CORS (default: None)")
87
88
return mcp_parser
88
89
89
90
def run_mcp_args(args):
90
91
from ..mcp.server import main as mcp_main
91
mcp_main(http=args.http, host=args.host, port=args.port)
92
mcp_main(http=args.http, host=args.host, port=args.port, origin=args.origin)
92
93
93
94
def main():
94
95
parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
@@ -356,11 +356,13 @@ class Backend_Api(Api):
356
356
if response.startswith("/media/"):
357
357
media_dir = get_media_dir()
358
358
filename = os.path.basename(response.split("?")[0])
359
try:
360
return send_from_directory(os.path.abspath(media_dir), filename)
361
finally:
362
if not cache_id:
359
if not cache_id:
360
try:
361
return send_from_directory(os.path.abspath(media_dir), filename)
362
finally:
363
363
os.remove(os.path.join(media_dir, filename))
364
else:
365
return redirect(response)
364
366
elif response.startswith("https://") or response.startswith("http://"):
365
367
return redirect(response)
366
368
if do_filter:
@@ -31,6 +31,7 @@ class MCPRequest:
31
31
id: Optional[Union[int, str]] = None
32
32
method: Optional[str] = None
33
33
params: Optional[Dict[str, Any]] = None
34
origin: Optional[str] = None
34
35
35
36
36
37
@dataclass
@@ -101,6 +102,7 @@ class MCPServer:
101
102
elif method == "tools/call":
102
103
tool_name = params.get("name")
103
104
tool_arguments = params.get("arguments", {})
105
tool_arguments.setdefault("origin", request.origin)
104
106
105
107
if tool_name not in self.tools:
106
108
return MCPResponse(
@@ -173,7 +175,7 @@ class MCPServer:
173
175
jsonrpc=request_data.get("jsonrpc", "2.0"),
174
176
id=request_data.get("id"),
175
177
method=request_data.get("method"),
176
params=request_data.get("params")
178
params=request_data.get("params"),
177
179
)
178
180
179
181
# Handle request
@@ -199,7 +201,7 @@ class MCPServer:
199
201
sys.stderr.write(f"Error: {e}\n")
200
202
sys.stderr.flush()
201
203
202
async def run_http(self, host: str = "0.0.0.0", port: int = 8765):
204
async def run_http(self, host: str = "0.0.0.0", port: int = 8765, origin: Optional[str] = None):
203
205
"""Run the MCP server with HTTP transport
204
206
205
207
Args:
@@ -218,12 +220,15 @@ class MCPServer:
218
220
try:
219
221
# Parse JSON-RPC request from POST body
220
222
request_data = await request.json()
223
if origin is None:
224
request_origin = request.headers.get("origin")
221
225
222
226
mcp_request = MCPRequest(
223
227
jsonrpc=request_data.get("jsonrpc", "2.0"),
224
228
id=request_data.get("id"),
225
229
method=request_data.get("method"),
226
params=request_data.get("params")
230
params=request_data.get("params"),
231
origin=request_origin
227
232
)
228
233
229
234
# Handle request
@@ -295,7 +300,7 @@ class MCPServer:
295
300
await runner.cleanup()
296
301
297
302
298
def main(http: bool = False, host: str = "0.0.0.0", port: int = 8765):
303
def main(http: bool = False, host: str = "0.0.0.0", port: int = 8765, origin: Optional[str] = None):
299
304
"""Main entry point for MCP server
300
305
301
306
Args:
@@ -305,7 +310,7 @@ def main(http: bool = False, host: str = "0.0.0.0", port: int = 8765):
305
310
"""
306
311
server = MCPServer()
307
312
if http:
308
asyncio.run(server.run_http(host, port))
313
asyncio.run(server.run_http(host, port, origin))
309
314
else:
310
315
asyncio.run(server.run())
311
316
@@ -11,6 +11,8 @@ from __future__ import annotations
11
11
from typing import Any, Dict
12
12
from abc import ABC, abstractmethod
13
13
14
from aiohttp import ClientSession
15
14
16
15
17
class MCPTool(ABC):
16
18
"""Base class for MCP tools"""
@@ -278,6 +280,8 @@ class ImageGenerationTool(MCPTool):
278
280
"image": image_url
279
281
}
280
282
else:
283
if arguments.get("origin") and image_url.startswith("/media/"):
284
image_url = f"{arguments.get('origin')}{image_url}"
281
285
return {
282
286
"prompt": prompt,
283
287
"model": model,
@@ -437,8 +441,13 @@ class TextToAudioTool(MCPTool):
437
441
encoded_prompt = prompt.replace(" ", "%20") # Basic space encoding
438
442
439
443
# Construct the Pollinations AI text-to-speech URL
440
base_url = "https://text.pollinations.ai"
441
audio_url = f"{base_url}/{encoded_prompt}?voice={voice}"
444
audio_url = f"/backend-api/v2/create?provider=Gemini&model=gemini-audio&cache=true&prompt={encoded_prompt}"
445
446
if arguments.get("origin"):
447
audio_url = f"{arguments.get('origin')}{audio_url}"
448
async with ClientSession() as session:
449
async with session.get(audio_url, max_redirects=0) as resp:
450
audio_url = str(resp.url)
442
451
443
452
return {
444
453
"prompt": prompt,