93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
"""Simple MySQL MCP server for Hermes - with async stdin handling."""
|
|
import json, sys, os, select
|
|
import mysql.connector
|
|
|
|
sys.stdin.reconfigure(encoding='utf-8')
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
|
|
def get_conn():
|
|
return mysql.connector.connect(
|
|
host=os.environ.get("MYSQL_HOST", "127.0.0.1"),
|
|
port=int(os.environ.get("MYSQL_PORT", "33060")),
|
|
user=os.environ.get("MYSQL_USER", "root"),
|
|
password=os.environ.get("MYSQL_PASS", ""),
|
|
database=os.environ.get("MYSQL_DB", ""),
|
|
charset="utf8mb4",
|
|
collation="utf8mb4_general_ci",
|
|
ssl_disabled=True
|
|
)
|
|
|
|
def handle_request(req):
|
|
method = req.get("method", "")
|
|
req_id = req.get("id", 1)
|
|
|
|
if method == "initialize":
|
|
return {"jsonrpc": "2.0", "result": {
|
|
"protocolVersion": "2024-11-05",
|
|
"serverInfo": {"name": "mysql-mcp", "version": "1.0.0"},
|
|
"capabilities": {"tools": {}}
|
|
}, "id": req_id}
|
|
|
|
if method == "notifications/initialized":
|
|
return None
|
|
|
|
if method == "ping":
|
|
return {"jsonrpc": "2.0", "result": {}, "id": req_id}
|
|
|
|
if method == "tools/list":
|
|
return {"jsonrpc": "2.0", "result": {"tools": [
|
|
{
|
|
"name": "mysql_query",
|
|
"description": "Execute a SQL query on the MySQL database",
|
|
"inputSchema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {"type": "string", "description": "SQL query to execute"}
|
|
},
|
|
"required": ["query"]
|
|
}
|
|
}
|
|
]}, "id": req_id}
|
|
|
|
if method == "tools/call":
|
|
tool = req.get("params", {}).get("name", "")
|
|
args = req.get("params", {}).get("arguments", {})
|
|
|
|
if tool == "mysql_query":
|
|
try:
|
|
conn = get_conn()
|
|
cursor = conn.cursor(dictionary=True)
|
|
cursor.execute(args["query"])
|
|
result = cursor.fetchall()
|
|
conn.commit()
|
|
cursor.close()
|
|
conn.close()
|
|
return {"jsonrpc": "2.0", "result": {
|
|
"content": [{"type": "text", "text": json.dumps(result, default=str, indent=2)}]
|
|
}, "id": req_id}
|
|
except Exception as e:
|
|
return {"jsonrpc": "2.0", "result": {
|
|
"content": [{"type": "text", "text": f"Error: {e}"}],
|
|
"isError": True
|
|
}, "id": req_id}
|
|
return None
|
|
|
|
# Use sys.stdin.buffer for line reading
|
|
with sys.stdin as f:
|
|
buf = ""
|
|
while True:
|
|
line = f.readline()
|
|
if not line:
|
|
break
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
req = json.loads(line)
|
|
resp = handle_request(req)
|
|
if resp:
|
|
sys.stdout.write(json.dumps(resp) + "\n")
|
|
sys.stdout.flush()
|
|
except json.JSONDecodeError:
|
|
pass
|