Victor Dibia b2cef7f47c
Update AGS (Support Workbenches ++) (#6736)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-07-16 10:03:02 -07:00

122 lines
4.8 KiB
Python

from typing import Any, Dict, Protocol
from mcp import ClientSession
from .utils import McpOperationError, extract_real_error, serialize_for_json
class MCPEventHandler(Protocol):
"""Interface for handling MCP events"""
async def on_initialized(self, session_id: str, capabilities: Any) -> None:
"""Called when MCP session is initialized"""
...
async def on_operation_result(self, operation: str, data: Dict[str, Any]) -> None:
"""Called when an MCP operation completes successfully"""
...
async def on_operation_error(self, operation: str, error: str) -> None:
"""Called when an MCP operation fails"""
...
async def on_mcp_activity(self, activity_type: str, message: str, details: Dict[str, Any]) -> None:
"""Called for MCP protocol activity"""
...
async def on_elicitation_request(self, request_id: str, message: str, requested_schema: Any) -> None:
"""Called when MCP requests user input"""
...
class MCPClient:
"""Handles MCP protocol operations independently of transport"""
def __init__(self, session: ClientSession, session_id: str, event_handler: MCPEventHandler):
self.session = session
self.session_id = session_id
self.event_handler = event_handler
self._initialized = False
self._capabilities = None
async def initialize(self) -> None:
"""Initialize the MCP session"""
try:
initialize_result = await self.session.initialize()
if initialize_result:
self._capabilities = initialize_result.capabilities
else:
self._capabilities = None
self._initialized = True
# Notify handler
await self.event_handler.on_initialized(
self.session_id, serialize_for_json(self._capabilities.model_dump()) if self._capabilities else None
)
except Exception as e:
await self.event_handler.on_operation_error("initialize", str(e))
raise
async def handle_operation(self, operation: Dict[str, Any]) -> None:
"""Handle an MCP operation - this preserves the exact behavior of handle_mcp_operation"""
operation_type = operation.get("operation")
try:
if operation_type == "list_tools":
result = await self.session.list_tools()
tools_data = [serialize_for_json(tool.model_dump()) for tool in result.tools]
await self.event_handler.on_operation_result("list_tools", {"tools": tools_data})
elif operation_type == "call_tool":
tool_name = operation.get("tool_name")
arguments = operation.get("arguments", {})
if not tool_name:
raise McpOperationError("Tool name is required")
result = await self.session.call_tool(tool_name, arguments)
await self.event_handler.on_operation_result(
"call_tool", {"tool_name": tool_name, "result": serialize_for_json(result.model_dump())}
)
elif operation_type == "list_resources":
result = await self.session.list_resources()
await self.event_handler.on_operation_result("list_resources", serialize_for_json(result.model_dump()))
elif operation_type == "read_resource":
uri = operation.get("uri")
if not uri:
raise McpOperationError("Resource URI is required")
result = await self.session.read_resource(uri)
await self.event_handler.on_operation_result("read_resource", serialize_for_json(result.model_dump()))
elif operation_type == "list_prompts":
result = await self.session.list_prompts()
prompts_data = [serialize_for_json(prompt.model_dump()) for prompt in result.prompts]
await self.event_handler.on_operation_result("list_prompts", {"prompts": prompts_data})
elif operation_type == "get_prompt":
name = operation.get("name")
arguments = operation.get("arguments")
if not name:
raise McpOperationError("Prompt name is required")
result = await self.session.get_prompt(name, arguments)
await self.event_handler.on_operation_result("get_prompt", serialize_for_json(result.model_dump()))
else:
await self.event_handler.on_operation_error(
operation_type or "unknown", f"Unknown operation: {operation_type}"
)
except Exception as e:
real_error = extract_real_error(e)
await self.event_handler.on_operation_error(operation_type or "unknown", real_error)
@property
def capabilities(self):
return self._capabilities