mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-05 18:20:35 -08:00
Add HTTP transport mode for MCP server with --http flag
Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com>
This commit is contained in:
parent
0a72ce961c
commit
a15618a80e
6 changed files with 266 additions and 10 deletions
15
README.md
15
README.md
|
|
@ -207,7 +207,7 @@ python -m g4f.cli gui --port 8080 --debug
|
||||||
### MCP Server
|
### MCP Server
|
||||||
GPT4Free now includes a Model Context Protocol (MCP) server that allows AI assistants like Claude to access web search, scraping, and image generation capabilities.
|
GPT4Free now includes a Model Context Protocol (MCP) server that allows AI assistants like Claude to access web search, scraping, and image generation capabilities.
|
||||||
|
|
||||||
**Starting the MCP server:**
|
**Starting the MCP server (stdio mode):**
|
||||||
```bash
|
```bash
|
||||||
# Using g4f command
|
# Using g4f command
|
||||||
g4f mcp
|
g4f mcp
|
||||||
|
|
@ -216,6 +216,19 @@ g4f mcp
|
||||||
python -m g4f.mcp
|
python -m g4f.mcp
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Starting the MCP server (HTTP mode):**
|
||||||
|
```bash
|
||||||
|
# Start HTTP server on port 8765
|
||||||
|
g4f mcp --http --port 8765
|
||||||
|
|
||||||
|
# Custom host and port
|
||||||
|
g4f mcp --http --host 127.0.0.1 --port 3000
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP mode provides:
|
||||||
|
- `POST http://localhost:8765/mcp` - JSON-RPC endpoint
|
||||||
|
- `GET http://localhost:8765/health` - Health check
|
||||||
|
|
||||||
**Configuring with Claude Desktop:**
|
**Configuring with Claude Desktop:**
|
||||||
|
|
||||||
Add to your `claude_desktop_config.json`:
|
Add to your `claude_desktop_config.json`:
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ pip install -e .
|
||||||
|
|
||||||
### 2. Start the MCP Server
|
### 2. Start the MCP Server
|
||||||
|
|
||||||
|
**Stdio Mode (Default):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Using g4f command
|
# Using g4f command
|
||||||
g4f mcp
|
g4f mcp
|
||||||
|
|
@ -49,8 +51,33 @@ The server will:
|
||||||
- Write responses to stdout
|
- Write responses to stdout
|
||||||
- Write debug/error messages to stderr
|
- Write debug/error messages to stderr
|
||||||
|
|
||||||
|
**HTTP Mode:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start HTTP server on default port 8765
|
||||||
|
g4f mcp --http
|
||||||
|
|
||||||
|
# Custom port
|
||||||
|
g4f mcp --http --port 3000
|
||||||
|
|
||||||
|
# Custom host and port
|
||||||
|
g4f mcp --http --host 127.0.0.1 --port 8765
|
||||||
|
```
|
||||||
|
|
||||||
|
The HTTP server provides:
|
||||||
|
- `POST http://localhost:8765/mcp` - JSON-RPC endpoint
|
||||||
|
- `GET http://localhost:8765/health` - Health check endpoint
|
||||||
|
|
||||||
|
HTTP mode is useful for:
|
||||||
|
- Web-based integrations
|
||||||
|
- Testing with HTTP clients
|
||||||
|
- Remote access
|
||||||
|
- Debugging with tools like curl or Postman
|
||||||
|
|
||||||
### 3. Test the Server
|
### 3. Test the Server
|
||||||
|
|
||||||
|
**Stdio Mode:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Send a test request
|
# Send a test request
|
||||||
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | python -m g4f.mcp
|
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | python -m g4f.mcp
|
||||||
|
|
@ -61,6 +88,21 @@ Expected output:
|
||||||
{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2024-11-05", "serverInfo": {...}}}
|
{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2024-11-05", "serverInfo": {...}}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**HTTP Mode:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start server
|
||||||
|
g4f mcp --http --port 8765
|
||||||
|
|
||||||
|
# In another terminal, test with curl
|
||||||
|
curl -X POST http://localhost:8765/mcp \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
curl http://localhost:8765/health
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Claude Desktop
|
### Claude Desktop
|
||||||
|
|
|
||||||
61
etc/testing/test_mcp_http.py
Normal file
61
etc/testing/test_mcp_http.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Test HTTP MCP server functionality
|
||||||
|
|
||||||
|
This script tests the HTTP transport for the MCP server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
from g4f.mcp.server import MCPServer, MCPRequest
|
||||||
|
|
||||||
|
|
||||||
|
async def test_http_server():
|
||||||
|
"""Test HTTP server methods"""
|
||||||
|
server = MCPServer()
|
||||||
|
|
||||||
|
print("Testing HTTP MCP Server Functionality")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
# Test that server can be initialized
|
||||||
|
print("\n✓ Server initialized successfully")
|
||||||
|
print(f" Server: {server.server_info['name']}")
|
||||||
|
print(f" Version: {server.server_info['version']}")
|
||||||
|
|
||||||
|
# Test that run_http method exists
|
||||||
|
if hasattr(server, 'run_http'):
|
||||||
|
print("\n✓ HTTP transport method (run_http) available")
|
||||||
|
print(f" Signature: run_http(host, port)")
|
||||||
|
else:
|
||||||
|
print("\n✗ HTTP transport method not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Test request handling (same for both transports)
|
||||||
|
print("\n✓ Testing request handling...")
|
||||||
|
|
||||||
|
init_request = MCPRequest(
|
||||||
|
jsonrpc="2.0",
|
||||||
|
id=1,
|
||||||
|
method="initialize",
|
||||||
|
params={}
|
||||||
|
)
|
||||||
|
response = await server.handle_request(init_request)
|
||||||
|
|
||||||
|
if response.result and response.result.get("protocolVersion"):
|
||||||
|
print(f" Protocol Version: {response.result['protocolVersion']}")
|
||||||
|
print(" ✓ Request handling works correctly")
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("HTTP MCP Server Tests Passed!")
|
||||||
|
print("\nTo start HTTP server:")
|
||||||
|
print(" g4f mcp --http --port 8765")
|
||||||
|
print("\nHTTP endpoints:")
|
||||||
|
print(" POST http://localhost:8765/mcp - MCP JSON-RPC endpoint")
|
||||||
|
print(" GET http://localhost:8765/health - Health check")
|
||||||
|
print("\nExample HTTP request:")
|
||||||
|
print(' curl -X POST http://localhost:8765/mcp \\')
|
||||||
|
print(' -H "Content-Type: application/json" \\')
|
||||||
|
print(' -d \'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}\'')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(test_http_server())
|
||||||
|
|
@ -81,11 +81,14 @@ def run_api_args(args):
|
||||||
def get_mcp_parser():
|
def get_mcp_parser():
|
||||||
mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server")
|
mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server")
|
||||||
mcp_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
|
mcp_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
|
||||||
|
mcp_parser.add_argument("--http", action="store_true", help="Use HTTP transport instead of stdio.")
|
||||||
|
mcp_parser.add_argument("--host", default="0.0.0.0", help="Host to bind HTTP server to (default: 0.0.0.0)")
|
||||||
|
mcp_parser.add_argument("--port", type=int, default=8765, help="Port to bind HTTP server to (default: 8765)")
|
||||||
return mcp_parser
|
return mcp_parser
|
||||||
|
|
||||||
def run_mcp_args(args):
|
def run_mcp_args(args):
|
||||||
from ..mcp.server import main as mcp_main
|
from ..mcp.server import main as mcp_main
|
||||||
mcp_main()
|
mcp_main(http=args.http, host=args.host, port=args.port)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
|
parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ pip install -e .
|
||||||
|
|
||||||
### Running the MCP Server
|
### Running the MCP Server
|
||||||
|
|
||||||
|
**Stdio Mode (Default)**
|
||||||
|
|
||||||
Start the MCP server using:
|
Start the MCP server using:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -36,11 +38,31 @@ g4f mcp
|
||||||
|
|
||||||
The server communicates over stdin/stdout using JSON-RPC 2.0 protocol.
|
The server communicates over stdin/stdout using JSON-RPC 2.0 protocol.
|
||||||
|
|
||||||
|
**HTTP Mode**
|
||||||
|
|
||||||
|
Start the MCP server with HTTP transport:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
g4f mcp --http --port 8765
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts an HTTP server with the following endpoints:
|
||||||
|
- `POST http://localhost:8765/mcp` - MCP JSON-RPC endpoint
|
||||||
|
- `GET http://localhost:8765/health` - Health check endpoint
|
||||||
|
|
||||||
|
HTTP mode is useful for:
|
||||||
|
- Web-based integrations
|
||||||
|
- Testing with curl or HTTP clients
|
||||||
|
- Remote access (configure host with `--host`)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `--http`: Enable HTTP transport instead of stdio
|
||||||
|
- `--host HOST`: Host to bind to (default: 0.0.0.0)
|
||||||
|
- `--port PORT`: Port to bind to (default: 8765)
|
||||||
|
|
||||||
### Configuration for AI Assistants
|
### Configuration for AI Assistants
|
||||||
|
|
||||||
To use this MCP server with an AI assistant like Claude Desktop, add the following to your MCP configuration:
|
**For Claude Desktop (Stdio)** - `claude_desktop_config.json`:
|
||||||
|
|
||||||
**For Claude Desktop** (`claude_desktop_config.json`):
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -53,6 +75,17 @@ To use this MCP server with an AI assistant like Claude Desktop, add the followi
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**For HTTP-based clients**:
|
||||||
|
|
||||||
|
Make POST requests to `http://localhost:8765/mcp` with JSON-RPC payloads.
|
||||||
|
|
||||||
|
Example with curl:
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8765/mcp \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
||||||
|
```
|
||||||
|
|
||||||
**For VS Code with Cline**:
|
**For VS Code with Cline**:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
"""MCP Server implementation using stdio transport
|
"""MCP Server implementation with stdio and HTTP transports
|
||||||
|
|
||||||
This module implements a Model Context Protocol (MCP) server that communicates
|
This module implements a Model Context Protocol (MCP) server that communicates
|
||||||
over standard input/output using JSON-RPC 2.0. The server exposes tools for:
|
over standard input/output using JSON-RPC 2.0, or via HTTP POST endpoints.
|
||||||
|
The server exposes tools for:
|
||||||
- Web search
|
- Web search
|
||||||
- Web scraping
|
- Web scraping
|
||||||
- Image generation
|
- Image generation
|
||||||
|
|
@ -191,11 +192,114 @@ class MCPServer:
|
||||||
sys.stderr.write(f"Error: {e}\n")
|
sys.stderr.write(f"Error: {e}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
async def run_http(self, host: str = "0.0.0.0", port: int = 8765):
|
||||||
|
"""Run the MCP server with HTTP transport
|
||||||
|
|
||||||
def main():
|
Args:
|
||||||
"""Main entry point for MCP server"""
|
host: Host to bind the HTTP server to
|
||||||
|
port: Port to bind the HTTP server to
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from aiohttp import web
|
||||||
|
except ImportError:
|
||||||
|
sys.stderr.write("Error: aiohttp is required for HTTP transport\n")
|
||||||
|
sys.stderr.write("Install it with: pip install aiohttp\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
async def handle_mcp_request(request: web.Request) -> web.Response:
|
||||||
|
"""Handle MCP JSON-RPC request over HTTP POST"""
|
||||||
|
try:
|
||||||
|
# Parse JSON-RPC request from POST body
|
||||||
|
request_data = await request.json()
|
||||||
|
|
||||||
|
mcp_request = MCPRequest(
|
||||||
|
jsonrpc=request_data.get("jsonrpc", "2.0"),
|
||||||
|
id=request_data.get("id"),
|
||||||
|
method=request_data.get("method"),
|
||||||
|
params=request_data.get("params")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Handle request
|
||||||
|
response = await self.handle_request(mcp_request)
|
||||||
|
|
||||||
|
# Build response dict
|
||||||
|
response_dict = {
|
||||||
|
"jsonrpc": response.jsonrpc,
|
||||||
|
"id": response.id
|
||||||
|
}
|
||||||
|
if response.result is not None:
|
||||||
|
response_dict["result"] = response.result
|
||||||
|
if response.error is not None:
|
||||||
|
response_dict["error"] = response.error
|
||||||
|
|
||||||
|
return web.json_response(response_dict)
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return web.json_response({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": None,
|
||||||
|
"error": {
|
||||||
|
"code": -32700,
|
||||||
|
"message": f"Parse error: {str(e)}"
|
||||||
|
}
|
||||||
|
}, status=400)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": None,
|
||||||
|
"error": {
|
||||||
|
"code": -32603,
|
||||||
|
"message": f"Internal error: {str(e)}"
|
||||||
|
}
|
||||||
|
}, status=500)
|
||||||
|
|
||||||
|
async def handle_health(request: web.Request) -> web.Response:
|
||||||
|
"""Health check endpoint"""
|
||||||
|
return web.json_response({
|
||||||
|
"status": "ok",
|
||||||
|
"server": self.server_info
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create aiohttp application
|
||||||
|
app = web.Application()
|
||||||
|
app.router.add_post('/mcp', handle_mcp_request)
|
||||||
|
app.router.add_get('/health', handle_health)
|
||||||
|
|
||||||
|
# Start server
|
||||||
|
sys.stderr.write(f"Starting {self.server_info['name']} v{self.server_info['version']} (HTTP mode)\n")
|
||||||
|
sys.stderr.write(f"Listening on http://{host}:{port}\n")
|
||||||
|
sys.stderr.write(f"MCP endpoint: http://{host}:{port}/mcp\n")
|
||||||
|
sys.stderr.write(f"Health check: http://{host}:{port}/health\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
runner = web.AppRunner(app)
|
||||||
|
await runner.setup()
|
||||||
|
site = web.TCPSite(runner, host, port)
|
||||||
|
await site.start()
|
||||||
|
|
||||||
|
# Keep server running
|
||||||
|
try:
|
||||||
|
await asyncio.Event().wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.stderr.write("\nShutting down HTTP server...\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
finally:
|
||||||
|
await runner.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
def main(http: bool = False, host: str = "0.0.0.0", port: int = 8765):
|
||||||
|
"""Main entry point for MCP server
|
||||||
|
|
||||||
|
Args:
|
||||||
|
http: If True, use HTTP transport instead of stdio
|
||||||
|
host: Host to bind HTTP server to (only used when http=True)
|
||||||
|
port: Port to bind HTTP server to (only used when http=True)
|
||||||
|
"""
|
||||||
server = MCPServer()
|
server = MCPServer()
|
||||||
asyncio.run(server.run())
|
if http:
|
||||||
|
asyncio.run(server.run_http(host, port))
|
||||||
|
else:
|
||||||
|
asyncio.run(server.run())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue