Add MCP command

This commit is contained in:
luijait 2025-05-29 23:58:35 +02:00
parent 9b4b0f6165
commit e5a00afeef
4 changed files with 1169 additions and 4 deletions

View File

@ -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

1066
src/cai/repl/commands/mcp.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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.")

View File

@ -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]")