sunnyware / scripts /mcp_stdio.py
shamiur's picture
Part 20-mcp: MCP server (JSON-RPC 2.0 over HTTP + stdio), 34/34 smoke
be79c3b
Raw History Blame Contribute Delete
2.16 kB
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2026 Shamiur Rashid Sunny
# SPDX-License-Identifier: AGPL-3.0-only
"""MCP stdio runner — for Claude Desktop / local MCP clients."""
import asyncio
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
from sunnyware import mcp_server # noqa: E402
async def _readline_async() -> str:
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, sys.stdin.readline)
async def main():
log = sys.stderr
print("[sunnyware-mcp] stdio runner started", file=log)
while True:
line = await _readline_async()
if not line:
break
line = line.strip()
if not line:
continue
try:
msg = json.loads(line)
except Exception as e:
print(f"[sunnyware-mcp] parse error: {e}", file=log)
continue
if isinstance(msg, list):
responses = []
for item in msg:
if not isinstance(item, dict):
continue
r = await mcp_server.handle_rpc(
str(item.get("method", "")),
item.get("params") or {},
item.get("id"),
)
if r is not None:
responses.append(r)
if responses:
sys.stdout.write(json.dumps(responses) + "\n")
sys.stdout.flush()
continue
method = msg.get("method")
params = msg.get("params") or {}
req_id = msg.get("id")
try:
result = await mcp_server.handle_rpc(method, params, req_id)
except Exception as e:
result = {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32603, "message": f"{type(e).__name__}: {e}"},
}
if result is not None:
sys.stdout.write(json.dumps(result) + "\n")
sys.stdout.flush()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
pass