Add MCP server management + fix OAuth
This commit is contained in:
parent
05397ceffc
commit
70a12093e9
1 changed files with 94 additions and 0 deletions
|
|
@ -623,6 +623,100 @@ async def auth_logout():
|
||||||
return {"status": "error", "message": str(e)}
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
# ============ MCP Server Management ============
|
||||||
|
|
||||||
|
@app.get("/api/mcp/servers")
|
||||||
|
async def list_mcp_servers():
|
||||||
|
"""List configured MCP servers"""
|
||||||
|
try:
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
"claude", "mcp", "list",
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=10)
|
||||||
|
output = stdout.decode(errors="replace").strip()
|
||||||
|
|
||||||
|
# Parse the output — claude mcp list shows servers in a table or JSON
|
||||||
|
servers = []
|
||||||
|
if "No MCP servers configured" in output:
|
||||||
|
return {"servers": [], "raw": output}
|
||||||
|
|
||||||
|
# Try JSON parse first
|
||||||
|
try:
|
||||||
|
import json as _json
|
||||||
|
data = _json.loads(output)
|
||||||
|
if isinstance(data, list):
|
||||||
|
servers = data
|
||||||
|
elif isinstance(data, dict):
|
||||||
|
servers = list(data.values()) if data else []
|
||||||
|
except Exception:
|
||||||
|
# Parse text output line by line
|
||||||
|
for line in output.split("\n"):
|
||||||
|
line = line.strip()
|
||||||
|
if line and not line.startswith("-") and not line.startswith("Name"):
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 1:
|
||||||
|
servers.append({"name": parts[0], "details": " ".join(parts[1:])})
|
||||||
|
|
||||||
|
return {"servers": servers, "raw": output}
|
||||||
|
except Exception as e:
|
||||||
|
return {"servers": [], "error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
class McpServerAdd(BaseModel):
|
||||||
|
name: str
|
||||||
|
server_type: str = "sse" # sse | stdio
|
||||||
|
url: Optional[str] = None
|
||||||
|
command: Optional[str] = None
|
||||||
|
args: Optional[List[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/mcp/servers")
|
||||||
|
async def add_mcp_server(server: McpServerAdd):
|
||||||
|
"""Add an MCP server"""
|
||||||
|
try:
|
||||||
|
cmd = ["claude", "mcp", "add", server.name]
|
||||||
|
if server.server_type == "sse" and server.url:
|
||||||
|
cmd.extend(["--transport", "sse", server.url])
|
||||||
|
elif server.server_type == "stdio" and server.command:
|
||||||
|
cmd.extend(["--transport", "stdio", server.command])
|
||||||
|
if server.args:
|
||||||
|
cmd.extend(server.args)
|
||||||
|
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
*cmd,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=15)
|
||||||
|
output = stdout.decode(errors="replace") + stderr.decode(errors="replace")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "ok" if proc.returncode == 0 else "error",
|
||||||
|
"message": output.strip(),
|
||||||
|
"name": server.name
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/api/mcp/servers/{name}")
|
||||||
|
async def remove_mcp_server(name: str):
|
||||||
|
"""Remove an MCP server"""
|
||||||
|
try:
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
"claude", "mcp", "remove", name,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=10)
|
||||||
|
output = stdout.decode(errors="replace") + stderr.decode(errors="replace")
|
||||||
|
return {"status": "ok" if proc.returncode == 0 else "error", "message": output.strip()}
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
|
||||||
# Serve static frontend
|
# Serve static frontend
|
||||||
app.mount("/", StaticFiles(directory="/app/frontend/dist", html=True), name="static")
|
app.mount("/", StaticFiles(directory="/app/frontend/dist", html=True), name="static")
|
||||||
|
|
||||||
|
|
|
||||||
Reference in a new issue