Nanny7 Claude Sonnet 4.5 commited on
Commit
3ce4ddf
·
1 Parent(s): fe33dd1

fix: resolve StopIteration by making entire stack synchronous

Browse files

- Removed async from all MCP tools (create_task, list_tasks, update_task, delete_task, complete_task)
- Made MCP server.call_tool() synchronous
- Made chat endpoint and execute_tool_calls synchronous
- Qwen client already synchronous from previous fix
- All async/await removed from chat flow
- Fixes 'coroutine raised StopIteration' error

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

backend/src/api/chat.py CHANGED
@@ -4,7 +4,6 @@
4
 
5
  import json
6
  import os
7
- import asyncio
8
  from typing import Optional, List, Dict, Any
9
  from uuid import UUID
10
  from fastapi import APIRouter, HTTPException, status, Depends
@@ -75,12 +74,12 @@ def extract_tool_call(ai_response: str) -> Optional[Dict[str, Any]]:
75
  return None
76
 
77
 
78
- async def execute_tool_calls(
79
  tool_calls: List[Dict[str, Any]],
80
  mcp_server: MCPServer
81
  ) -> List[Dict[str, Any]]:
82
  """
83
- Execute multiple tool calls.
84
 
85
  Args:
86
  tool_calls: List of tool call dicts with 'tool' and 'parameters'
@@ -95,7 +94,7 @@ async def execute_tool_calls(
95
  parameters = tool_call.get("parameters", {})
96
 
97
  try:
98
- result = await mcp_server.call_tool(tool_name, parameters)
99
  results.append({
100
  "tool": tool_name,
101
  "success": True,
@@ -113,7 +112,7 @@ async def execute_tool_calls(
113
 
114
 
115
  @router.post("/", response_model=ChatResponse)
116
- async def chat(
117
  request: ChatRequest,
118
  user_id: str = Depends(get_current_user_id),
119
  session: Session = Depends(get_session)
@@ -200,7 +199,7 @@ async def chat(
200
  if tool_call:
201
  # Execute the tool call
202
  logger.info(f"Executing tool call: {tool_call['tool']}")
203
- results = await execute_tool_calls([tool_call], mcp_server)
204
  tool_results = results
205
 
206
  # Format tool results for AI
@@ -243,6 +242,6 @@ async def chat(
243
 
244
 
245
  @router.get("/health")
246
- async def health_check():
247
  """Health check endpoint for chat API"""
248
  return {"status": "healthy", "service": "chat-api"}
 
4
 
5
  import json
6
  import os
 
7
  from typing import Optional, List, Dict, Any
8
  from uuid import UUID
9
  from fastapi import APIRouter, HTTPException, status, Depends
 
74
  return None
75
 
76
 
77
+ def execute_tool_calls(
78
  tool_calls: List[Dict[str, Any]],
79
  mcp_server: MCPServer
80
  ) -> List[Dict[str, Any]]:
81
  """
82
+ Execute multiple tool calls (synchronous).
83
 
84
  Args:
85
  tool_calls: List of tool call dicts with 'tool' and 'parameters'
 
94
  parameters = tool_call.get("parameters", {})
95
 
96
  try:
97
+ result = mcp_server.call_tool(tool_name, parameters)
98
  results.append({
99
  "tool": tool_name,
100
  "success": True,
 
112
 
113
 
114
  @router.post("/", response_model=ChatResponse)
115
+ def chat(
116
  request: ChatRequest,
117
  user_id: str = Depends(get_current_user_id),
118
  session: Session = Depends(get_session)
 
199
  if tool_call:
200
  # Execute the tool call
201
  logger.info(f"Executing tool call: {tool_call['tool']}")
202
+ results = execute_tool_calls([tool_call], mcp_server)
203
  tool_results = results
204
 
205
  # Format tool results for AI
 
242
 
243
 
244
  @router.get("/health")
245
+ def health_check():
246
  """Health check endpoint for chat API"""
247
  return {"status": "healthy", "service": "chat-api"}
backend/src/mcp/server.py CHANGED
@@ -38,15 +38,15 @@ class MCPServer:
38
 
39
  Args:
40
  name: Tool name (e.g., "add_task", "list_tasks")
41
- func: Async function implementing the tool
42
  description: Human-readable tool description
43
  """
44
  self.tools[name] = func
45
  logger.info(f"Registered MCP tool: {name} - {description or 'No description'}")
46
 
47
- async def call_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
48
  """
49
- Execute an MCP tool by name.
50
 
51
  Args:
52
  tool_name: Name of the tool to execute
@@ -67,7 +67,7 @@ class MCPServer:
67
  logger.info(f"Executing MCP tool: {tool_name} with parameters: {list(parameters.keys())}")
68
 
69
  try:
70
- result = await tool_func(**parameters)
71
  logger.info(f"Tool '{tool_name}' executed successfully")
72
  return result
73
  except Exception as e:
 
38
 
39
  Args:
40
  name: Tool name (e.g., "add_task", "list_tasks")
41
+ func: Synchronous function implementing the tool
42
  description: Human-readable tool description
43
  """
44
  self.tools[name] = func
45
  logger.info(f"Registered MCP tool: {name} - {description or 'No description'}")
46
 
47
+ def call_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
48
  """
49
+ Execute an MCP tool by name (synchronous).
50
 
51
  Args:
52
  tool_name: Name of the tool to execute
 
67
  logger.info(f"Executing MCP tool: {tool_name} with parameters: {list(parameters.keys())}")
68
 
69
  try:
70
+ result = tool_func(**parameters)
71
  logger.info(f"Tool '{tool_name}' executed successfully")
72
  return result
73
  except Exception as e:
backend/src/mcp/tools.py CHANGED
@@ -18,7 +18,7 @@ class MCPTools:
18
  self.repo = todo_repository
19
  self.user_id = user_id
20
 
21
- async def create_task(
22
  self,
23
  title: str,
24
  description: Optional[str] = None,
@@ -78,7 +78,7 @@ class MCPTools:
78
  "error": f"Failed to create task: {str(e)}"
79
  }
80
 
81
- async def list_tasks(
82
  self,
83
  status: Optional[str] = None,
84
  priority: Optional[str] = None,
@@ -129,7 +129,7 @@ class MCPTools:
129
  "error": f"Failed to list tasks: {str(e)}"
130
  }
131
 
132
- async def update_task(
133
  self,
134
  task_id: str,
135
  title: Optional[str] = None,
@@ -206,7 +206,7 @@ class MCPTools:
206
  "error": f"Failed to update task: {str(e)}"
207
  }
208
 
209
- async def delete_task(self, task_id: str) -> dict:
210
  """
211
  Delete a task.
212
 
@@ -244,7 +244,7 @@ class MCPTools:
244
  "error": f"Failed to delete task: {str(e)}"
245
  }
246
 
247
- async def complete_task(self, task_id: str) -> dict:
248
  """
249
  Mark a task as completed.
250
 
 
18
  self.repo = todo_repository
19
  self.user_id = user_id
20
 
21
+ def create_task(
22
  self,
23
  title: str,
24
  description: Optional[str] = None,
 
78
  "error": f"Failed to create task: {str(e)}"
79
  }
80
 
81
+ def list_tasks(
82
  self,
83
  status: Optional[str] = None,
84
  priority: Optional[str] = None,
 
129
  "error": f"Failed to list tasks: {str(e)}"
130
  }
131
 
132
+ def update_task(
133
  self,
134
  task_id: str,
135
  title: Optional[str] = None,
 
206
  "error": f"Failed to update task: {str(e)}"
207
  }
208
 
209
+ def delete_task(self, task_id: str) -> dict:
210
  """
211
  Delete a task.
212
 
 
244
  "error": f"Failed to delete task: {str(e)}"
245
  }
246
 
247
+ def complete_task(self, task_id: str) -> dict:
248
  """
249
  Mark a task as completed.
250