|
|
@@ -134,6 +134,11 @@ async def chat(request: Request):
|
|
|
else:
|
|
|
parsed_tokens = mcp_tokens
|
|
|
|
|
|
+ # DEBUG: 打印收到的 token
|
|
|
+ print(f"[DEBUG /api/chat] Received mcp_tokens: {list(parsed_tokens.keys()) if parsed_tokens else 'None'}")
|
|
|
+ for k, v in parsed_tokens.items():
|
|
|
+ print(f"[DEBUG /api/chat] {k}: {v[:30] if v else 'None'}...")
|
|
|
+
|
|
|
# 创建对话管理器(带 token)
|
|
|
conv_manager = ConversationManager(
|
|
|
api_key=ANTHROPIC_API_KEY,
|
|
|
@@ -194,11 +199,16 @@ async def generate_chat_stream(
|
|
|
if isinstance(mcp_tokens, str):
|
|
|
try:
|
|
|
parsed_tokens = json_module.loads(mcp_tokens)
|
|
|
- except:
|
|
|
+ except Exception as e:
|
|
|
parsed_tokens = {}
|
|
|
else:
|
|
|
parsed_tokens = mcp_tokens
|
|
|
|
|
|
+ # DEBUG: 打印解析后的 token
|
|
|
+ print(f"[DEBUG generate_chat_stream] Parsed mcp_tokens keys: {list(parsed_tokens.keys())}")
|
|
|
+ for k, v in parsed_tokens.items():
|
|
|
+ print(f"[DEBUG generate_chat_stream] {k}: {v[:30] if v else 'None'}...")
|
|
|
+
|
|
|
# 创建对话管理器(带 token)
|
|
|
conv_manager = ConversationManager(
|
|
|
api_key=ANTHROPIC_API_KEY,
|
|
|
@@ -327,7 +337,8 @@ async def generate_chat_stream(
|
|
|
yield f"event: tool_call\ndata: {json_module.dumps({'tool': tool_block['name'], 'args': tool_block['input'], 'tool_id': tool_block['id']})}\n\n"
|
|
|
|
|
|
tool_results = await conv_manager.tool_handler.process_tool_use_blocks(
|
|
|
- tool_use_blocks
|
|
|
+ tool_use_blocks,
|
|
|
+ conv_manager._tool_to_server_map
|
|
|
)
|
|
|
|
|
|
for tr in tool_results:
|
|
|
@@ -389,6 +400,10 @@ async def chat_stream(request: Request):
|
|
|
session_id = request.headers.get('X-Session-ID')
|
|
|
mcp_tokens = request.headers.get('X-MCP-Tokens') # MCP tokens (JSON string)
|
|
|
|
|
|
+ # DEBUG: 打印收到的 token
|
|
|
+ print(f"[DEBUG /api/chat/stream] mcp_tokens type: {type(mcp_tokens)}")
|
|
|
+ print(f"[DEBUG /api/chat/stream] mcp_tokens value: {mcp_tokens[:150] if mcp_tokens else 'None'}...")
|
|
|
+
|
|
|
if not message:
|
|
|
raise HTTPException(status_code=400, detail="Message is required")
|
|
|
|
|
|
@@ -507,11 +522,15 @@ async def login(request: Request):
|
|
|
result = response.json()
|
|
|
session_id = str(uuid.uuid4())
|
|
|
|
|
|
+ # Novel Platform API 返回 access_token 和 user 对象
|
|
|
+ access_token = result.get("access_token")
|
|
|
+ user_info = result.get("user", {})
|
|
|
+
|
|
|
# 存储会话信息
|
|
|
auth_sessions[session_id] = {
|
|
|
- "username": result.get("username", email),
|
|
|
+ "username": user_info.get("username") or user_info.get("email", email),
|
|
|
"email": email,
|
|
|
- "token": result.get("token"),
|
|
|
+ "token": access_token,
|
|
|
"refresh_token": result.get("refresh_token"),
|
|
|
"server": target_server.get("name")
|
|
|
}
|
|
|
@@ -519,9 +538,9 @@ async def login(request: Request):
|
|
|
return {
|
|
|
"success": True,
|
|
|
"session_id": session_id,
|
|
|
- "username": result.get("username", email),
|
|
|
+ "username": user_info.get("username") or user_info.get("email", email),
|
|
|
"server": target_server.get("name"),
|
|
|
- "token": result.get("token")
|
|
|
+ "token": access_token
|
|
|
}
|
|
|
else:
|
|
|
raise HTTPException(
|
|
|
@@ -571,10 +590,14 @@ async def admin_login(request: Request):
|
|
|
result = response.json()
|
|
|
session_id = str(uuid.uuid4())
|
|
|
|
|
|
+ # Novel Platform API 返回 access_token 和 user 对象
|
|
|
+ access_token = result.get("access_token")
|
|
|
+ user_info = result.get("user", {})
|
|
|
+
|
|
|
auth_sessions[session_id] = {
|
|
|
- "username": result.get("username", email),
|
|
|
+ "username": user_info.get("username") or user_info.get("email", email),
|
|
|
"email": email,
|
|
|
- "token": result.get("token"),
|
|
|
+ "token": access_token,
|
|
|
"refresh_token": result.get("refresh_token"),
|
|
|
"server": target_server.get("name"),
|
|
|
"role": "admin"
|
|
|
@@ -583,10 +606,10 @@ async def admin_login(request: Request):
|
|
|
return {
|
|
|
"success": True,
|
|
|
"session_id": session_id,
|
|
|
- "username": result.get("username", email),
|
|
|
+ "username": user_info.get("username") or user_info.get("email", email),
|
|
|
"server": target_server.get("name"),
|
|
|
"role": "admin",
|
|
|
- "token": result.get("token")
|
|
|
+ "token": access_token
|
|
|
}
|
|
|
else:
|
|
|
raise HTTPException(
|
|
|
@@ -630,6 +653,157 @@ async def auth_status(x_session_id: Optional[str] = Header(None, alias='X-Sessio
|
|
|
return {"authenticated": False}
|
|
|
|
|
|
|
|
|
+# ========== 测试 API ==========
|
|
|
+
|
|
|
+@app.get("/api/test-mcp")
|
|
|
+async def test_mcp_get(
|
|
|
+ tool_name: str = "get_system_stats",
|
|
|
+ server_id: str = "novel-platform-admin",
|
|
|
+ auth_token: Optional[str] = None
|
|
|
+):
|
|
|
+ """
|
|
|
+ GET 方式测试 MCP 工具调用(用于 curl 测试)
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ - tool_name: 工具名称 (默认: get_system_stats)
|
|
|
+ - server_id: 服务器 ID (默认: novel-platform-admin)
|
|
|
+ - auth_token: JWT token (通过 query 参数或 header 传递)
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 如果 query 参数没有 token,尝试从 header 获取
|
|
|
+ if not auth_token:
|
|
|
+ # 这个处理会在实际请求时通过 FastAPI 的 Header 参数处理
|
|
|
+ pass
|
|
|
+
|
|
|
+ print("\n" + "="*60)
|
|
|
+ print("[TEST-MCP GET] MCP 工具调用测试")
|
|
|
+ print("="*60)
|
|
|
+ print(f"[TEST-MCP GET] server_id: {server_id}")
|
|
|
+ print(f"[TEST-MCP GET] tool_name: {tool_name}")
|
|
|
+ print(f"[TEST-MCP GET] auth_token present: {bool(auth_token)}")
|
|
|
+ if auth_token:
|
|
|
+ print(f"[TEST-MCP GET] auth_token (前50字符): {auth_token[:50]}...")
|
|
|
+ print("="*60 + "\n")
|
|
|
+
|
|
|
+ # 创建 MCP 客户端
|
|
|
+ client = MCPClient(
|
|
|
+ server_id=server_id,
|
|
|
+ session_id="test-session",
|
|
|
+ auth_token=auth_token
|
|
|
+ )
|
|
|
+
|
|
|
+ # 调用工具(无参数)
|
|
|
+ print(f"[TEST-MCP GET] 开始调用工具...")
|
|
|
+ result = await client.call_tool(tool_name, {})
|
|
|
+
|
|
|
+ print(f"[TEST-MCP GET] 调用完成")
|
|
|
+ print(f"[TEST-MCP GET] success: {result.get('success', False)}")
|
|
|
+ print("="*60 + "\n")
|
|
|
+
|
|
|
+ return {
|
|
|
+ "success": True,
|
|
|
+ "server_id": server_id,
|
|
|
+ "tool_name": tool_name,
|
|
|
+ "result": result
|
|
|
+ }
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ import traceback
|
|
|
+ print(f"[TEST-MCP GET] 异常: {e}")
|
|
|
+ traceback.print_exc()
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=500,
|
|
|
+ content={
|
|
|
+ "success": False,
|
|
|
+ "error": str(e),
|
|
|
+ "traceback": traceback.format_exc()
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@app.post("/api/test-mcp")
|
|
|
+async def test_mcp_call(request: Request):
|
|
|
+ """
|
|
|
+ 直接测试 MCP 工具调用(绕过 CORS,用于调试)
|
|
|
+
|
|
|
+ 请求体:
|
|
|
+ {
|
|
|
+ "server_id": "novel-platform-admin", // 可选,默认使用 admin
|
|
|
+ "tool_name": "get_system_stats", // 工具名称
|
|
|
+ "arguments": {}, // 工具参数
|
|
|
+ "auth_token": "jwt-token" // JWT 认证 token
|
|
|
+ }
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ data = await request.json()
|
|
|
+ server_id = data.get('server_id', 'novel-platform-admin')
|
|
|
+ tool_name = data.get('tool_name', '')
|
|
|
+ arguments = data.get('arguments', {})
|
|
|
+ auth_token = data.get('auth_token')
|
|
|
+
|
|
|
+ print("\n" + "="*60)
|
|
|
+ print("[TEST-MCP] MCP 工具调用测试")
|
|
|
+ print("="*60)
|
|
|
+ print(f"[TEST-MCP] server_id: {server_id}")
|
|
|
+ print(f"[TEST-MCP] tool_name: {tool_name}")
|
|
|
+ print(f"[TEST-MCP] arguments: {arguments}")
|
|
|
+ print(f"[TEST-MCP] auth_token present: {bool(auth_token)}")
|
|
|
+ if auth_token:
|
|
|
+ print(f"[TEST-MCP] auth_token (前50字符): {auth_token[:50]}...")
|
|
|
+ print("="*60 + "\n")
|
|
|
+
|
|
|
+ if not tool_name:
|
|
|
+ raise HTTPException(status_code=400, detail="tool_name is required")
|
|
|
+
|
|
|
+ # 创建 MCP 客户端
|
|
|
+ client = MCPClient(
|
|
|
+ server_id=server_id,
|
|
|
+ session_id="test-session",
|
|
|
+ auth_token=auth_token
|
|
|
+ )
|
|
|
+
|
|
|
+ # 调用工具
|
|
|
+ print(f"[TEST-MCP] 开始调用工具...")
|
|
|
+ result = await client.call_tool(tool_name, arguments)
|
|
|
+
|
|
|
+ print(f"[TEST-MCP] 调用结果:")
|
|
|
+ print(f"[TEST-MCP] success: {result.get('success', False)}")
|
|
|
+ print(f"[TEST-MCP] has_error: {'error' in result}")
|
|
|
+ if 'error' in result:
|
|
|
+ print(f"[TEST-MCP] error: {result['error']}")
|
|
|
+ else:
|
|
|
+ result_preview = result.get('result', '')[:100]
|
|
|
+ print(f"[TEST-MCP] result (预览): {result_preview}...")
|
|
|
+ print("="*60 + "\n")
|
|
|
+
|
|
|
+ return {
|
|
|
+ "success": True,
|
|
|
+ "server_id": server_id,
|
|
|
+ "tool_name": tool_name,
|
|
|
+ "arguments": arguments,
|
|
|
+ "result": result,
|
|
|
+ "debug": {
|
|
|
+ "auth_token_present": bool(auth_token),
|
|
|
+ "auth_token_length": len(auth_token) if auth_token else 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ except HTTPException:
|
|
|
+ raise
|
|
|
+ except Exception as e:
|
|
|
+ import traceback
|
|
|
+ print(f"[TEST-MCP] 异常: {e}")
|
|
|
+ traceback.print_exc()
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=500,
|
|
|
+ content={
|
|
|
+ "success": False,
|
|
|
+ "error": str(e),
|
|
|
+ "traceback": traceback.format_exc()
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
# ========== 主程序入口 ==========
|
|
|
|
|
|
if __name__ == '__main__':
|