mirror of https://github.com/aliasrobotics/cai.git
Add MCP command
This commit is contained in:
parent
9b4b0f6165
commit
e5a00afeef
|
|
@ -40,7 +40,8 @@ from cai.repl.commands import ( # pylint: disable=import-error,unused-import,li
|
|||
workspace,
|
||||
virtualization,
|
||||
load,
|
||||
parallel # Add the new parallel command
|
||||
parallel, # Add the new parallel command
|
||||
mcp # Add the MCP command
|
||||
)
|
||||
|
||||
# Define helper functions
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -80,10 +80,52 @@ class MCPUtil:
|
|||
logger.debug(f"Invoking MCP tool {tool.name} with input {input_json}")
|
||||
|
||||
try:
|
||||
# Check if server session is still valid
|
||||
if not hasattr(server, 'session') or server.session is None:
|
||||
logger.warning(f"MCP server session not found for tool {tool.name}, attempting to reconnect...")
|
||||
# Try to reconnect
|
||||
try:
|
||||
await server.connect()
|
||||
logger.info(f"Successfully reconnected to MCP server for tool {tool.name}")
|
||||
except Exception as reconnect_error:
|
||||
logger.error(f"Failed to reconnect to MCP server: {reconnect_error}")
|
||||
raise AgentsException(
|
||||
f"MCP server connection lost for tool {tool.name}. "
|
||||
f"Please remove and re-add the MCP server. "
|
||||
f"Reconnection error: {str(reconnect_error)}"
|
||||
) from reconnect_error
|
||||
|
||||
# Now try to call the tool
|
||||
result = await server.call_tool(tool.name, json_data)
|
||||
|
||||
except AttributeError as ae:
|
||||
# This often happens when the server object is not properly initialized
|
||||
logger.error(f"MCP server not properly initialized for tool {tool.name}: {ae}")
|
||||
logger.error(f"Server type: {type(server)}, has session: {hasattr(server, 'session')}")
|
||||
raise AgentsException(
|
||||
f"MCP server not properly initialized for tool {tool.name}. "
|
||||
f"The server connection may have been lost. "
|
||||
f"AttributeError: {str(ae)}\n"
|
||||
f"Try: /mcp remove <server_name> then /mcp load ... to reconnect."
|
||||
) from ae
|
||||
except Exception as e:
|
||||
logger.error(f"Error invoking MCP tool {tool.name}: {e}")
|
||||
raise AgentsException(f"Error invoking MCP tool {tool.name}: {e}") from e
|
||||
# Log the full exception details
|
||||
logger.error(f"Error invoking MCP tool {tool.name}: {type(e).__name__}: {str(e)}")
|
||||
logger.error(f"Full exception details: {repr(e)}")
|
||||
|
||||
# Check if it's a connection issue
|
||||
error_str = str(e).lower()
|
||||
if "session" in error_str or "connection" in error_str or "closed" in error_str:
|
||||
raise AgentsException(
|
||||
f"MCP server connection error for tool {tool.name}. "
|
||||
f"Error: {type(e).__name__}: {str(e)}\n"
|
||||
f"Use '/mcp status' to check server health and '/mcp remove' + '/mcp load' to reconnect."
|
||||
) from e
|
||||
else:
|
||||
# For other errors, include the full error details
|
||||
raise AgentsException(
|
||||
f"Error invoking MCP tool {tool.name}: {type(e).__name__}: {str(e)}"
|
||||
) from e
|
||||
|
||||
if _debug.DONT_LOG_TOOL_DATA:
|
||||
logger.debug(f"MCP tool {tool.name} completed.")
|
||||
|
|
|
|||
|
|
@ -645,9 +645,65 @@ def visualize_agent_graph(start_agent):
|
|||
|
||||
# Add tools
|
||||
tools_node = node.add("[yellow]Tools[/yellow]")
|
||||
for tool in getattr(agent, "tools", []):
|
||||
|
||||
# Get regular tools and MCP tools separately
|
||||
regular_tools = getattr(agent, "tools", [])
|
||||
mcp_tools = []
|
||||
|
||||
try:
|
||||
# Try to get MCP tools specifically
|
||||
import asyncio
|
||||
|
||||
async def get_mcp_tools_async():
|
||||
return await agent.get_mcp_tools()
|
||||
|
||||
# Run async function to get MCP tools
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
# If we're in a loop, we need to use a different approach
|
||||
import concurrent.futures
|
||||
|
||||
def run_in_thread():
|
||||
new_loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(new_loop)
|
||||
try:
|
||||
return new_loop.run_until_complete(get_mcp_tools_async())
|
||||
finally:
|
||||
new_loop.close()
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(run_in_thread)
|
||||
mcp_tools = future.result(timeout=10)
|
||||
|
||||
except RuntimeError:
|
||||
# No running loop, we can use asyncio.run
|
||||
mcp_tools = asyncio.run(get_mcp_tools_async())
|
||||
|
||||
except Exception:
|
||||
# If MCP tools fetch fails, mcp_tools remains empty list
|
||||
mcp_tools = []
|
||||
|
||||
# Show regular tools first
|
||||
for tool in regular_tools:
|
||||
tool_name = getattr(tool, "name", None) or getattr(tool, "__name__", "")
|
||||
tools_node.add(f"[blue]{tool_name}[/blue]")
|
||||
|
||||
# Show MCP tools with a different color/prefix
|
||||
if mcp_tools:
|
||||
for tool in mcp_tools:
|
||||
tool_name = getattr(tool, "name", None) or getattr(tool, "__name__", "")
|
||||
tools_node.add(f"[magenta]🔌 {tool_name}[/magenta]")
|
||||
|
||||
# Add a summary line if we have both types
|
||||
if regular_tools and mcp_tools:
|
||||
summary_text = f"[dim]({len(regular_tools)} regular, {len(mcp_tools)} MCP tools)[/dim]"
|
||||
tools_node.add(summary_text)
|
||||
elif mcp_tools and not regular_tools:
|
||||
summary_text = f"[dim]({len(mcp_tools)} MCP tools)[/dim]"
|
||||
tools_node.add(summary_text)
|
||||
elif regular_tools and not mcp_tools:
|
||||
summary_text = f"[dim]({len(regular_tools)} regular tools)[/dim]"
|
||||
tools_node.add(summary_text)
|
||||
|
||||
# Add handoffs
|
||||
transfers_node = node.add("[magenta]Handoffs[/magenta]")
|
||||
|
|
|
|||
Loading…
Reference in New Issue