| import os |
| import asyncio |
| import sys |
| import json |
| from fastapi import FastAPI, Request |
| from sse_starlette.sse import EventSourceResponse |
| from fastapi.responses import JSONResponse |
|
|
| app = FastAPI() |
|
|
| |
| |
| ENV_VARS = os.environ.copy() |
|
|
| |
| |
| MCP_COMMAND = ["python", "-m", "paddleocr_mcp"] |
|
|
| |
| |
| active_processes = {} |
|
|
| async def run_mcp_session(request: Request): |
| """ |
| 为每个连接启动一个独立的 paddleocr-mcp 子进程 |
| 并将其输出流式传输回客户端 (SSE) |
| """ |
| process = await asyncio.create_subprocess_exec( |
| *MCP_COMMAND, |
| stdin=asyncio.subprocess.PIPE, |
| stdout=asyncio.subprocess.PIPE, |
| stderr=sys.stderr, |
| env=ENV_VARS |
| ) |
| |
| |
| |
| |
| |
| |
| global active_processes |
| active_processes["default"] = process |
|
|
| async def event_generator(): |
| try: |
| while True: |
| if await request.is_disconnected(): |
| break |
| |
| |
| line = await process.stdout.readline() |
| if not line: |
| break |
| |
| |
| data = line.decode().strip() |
| if data: |
| yield { |
| "event": "message", |
| "data": data |
| } |
| except Exception as e: |
| print(f"Error in event stream: {e}") |
| finally: |
| |
| if process.returncode is None: |
| process.terminate() |
| await process.wait() |
|
|
| return EventSourceResponse(event_generator()) |
|
|
| @app.get("/sse") |
| async def handle_sse(request: Request): |
| return await run_mcp_session(request) |
|
|
| @app.post("/messages") |
| async def handle_messages(request: Request): |
| """ |
| 接收 Claude 发来的指令 (JSON-RPC),写入子进程的 stdin |
| """ |
| if "default" not in active_processes or active_processes["default"].returncode is not None: |
| return JSONResponse(status_code=400, content={"error": "No active MCP session"}) |
|
|
| process = active_processes["default"] |
| |
| try: |
| body = await request.json() |
| |
| message_str = json.dumps(body) + "\n" |
| process.stdin.write(message_str.encode()) |
| await process.stdin.drain() |
| return JSONResponse(content={"status": "accepted"}) |
| except Exception as e: |
| return JSONResponse(status_code=500, content={"error": str(e)}) |
|
|
| @app.get("/") |
| def health_check(): |
| return {"status": "paddleocr-mcp-server is running"} |