#!/usr/bin/env python3 """ AGIAgent MCP Client - Model Context Protocol Client Integrates with 100+ MCP tools for external service integration """ import json import subprocess import sys from pathlib import Path from typing import Any, Dict, List, Optional import os class MCPClient: """Main MCP Client for Claude Code integration""" def __init__(self, config_path: str = None): self.config_path = config_path or os.path.expanduser("~/.claude/mcp-config.json") self.servers = self.load_config() self.available_tools = [] def load_config(self) -> Dict[str, Any]: """Load MCP server configuration""" default_config = { "mcpServers": { "zai-mcp-server": { "command": "npx", "args": ["-y", "@zai/mcp-server"] }, "web-search-prime": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-web-search"] }, "web-reader": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-web-reader"] }, "zread": { "command": "npx", "args": ["-y", "zread-mcp-server"] } } } if Path(self.config_path).exists(): try: with open(self.config_path) as f: return json.load(f) except: pass return default_config def save_config(self, config: Dict[str, Any]): """Save MCP server configuration""" Path(self.config_path).parent.mkdir(parents=True, exist_ok=True) with open(self.config_path, 'w') as f: json.dump(config, f, indent=2) def discover_tools(self) -> List[Dict[str, Any]]: """Discover all available tools from connected MCP servers""" tools = [] for server_name, server_config in self.config.get("mcpServers", {}).items(): try: server_tools = self._get_server_tools(server_name, server_config) tools.extend(server_tools) except Exception as e: print(f"Warning: Could not connect to {server_name}: {e}", file=sys.stderr) self.available_tools = tools return tools def _get_server_tools(self, name: str, config: Dict[str, Any]) -> List[Dict[str, Any]]: """Get tools from a specific MCP server""" # This is a simplified version - real implementation would use stdio/jsonrpc command = config.get("command", "") args = config.get("args", []) try: # Try to run the server's list-tools command result = subprocess.run( [command] + args + ["--list-tools"], capture_output=True, text=True, timeout=5 ) if result.returncode == 0: return json.loads(result.stdout) except: pass # Return mock tools if server doesn't respond return self._get_mock_tools(name) def _get_mock_tools(self, server_name: str) -> List[Dict[str, Any]]: """Get mock tool definitions for known servers""" mock_tools = { "zai-mcp-server": [ {"name": "analyze_image", "description": "Analyze images with AI vision"}, {"name": "generate_image", "description": "Generate images from text"}, {"name": "extract_text", "description": "Extract text from images"} ], "web-search-prime": [ {"name": "web_search", "description": "Search the web for information"} ], "web-reader": [ {"name": "fetch_webpage", "description": "Fetch and read webpage content"} ], "zread": [ {"name": "search_repo", "description": "Search GitHub repositories"} ] } return mock_tools.get(server_name, []) def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any: """Call an MCP tool""" # Find which server has this tool for server_name, server_config in self.config.get("mcpServers", {}).items(): server_tools = self._get_server_tools(server_name, server_config) if any(t["name"] == tool_name for t in server_tools): return self._call_server_tool(server_name, server_config, tool_name, arguments) raise ValueError(f"Tool {tool_name} not found in any connected server") def _call_server_tool(self, server_name: str, config: Dict[str, Any], tool_name: str, arguments: Dict[str, Any]) -> Any: """Call a tool on a specific server""" command = config.get("command", "") args = config.get("args", []) # Build the tool call tool_call = { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": tool_name, "arguments": arguments } } try: result = subprocess.run( [command] + args, input=json.dumps(tool_call), capture_output=True, text=True, timeout=30 ) if result.returncode == 0: response = json.loads(result.stdout) return response.get("result", {}) else: return {"error": result.stderr} except subprocess.TimeoutExpired: return {"error": "Tool call timed out"} except Exception as e: return {"error": str(e)} def list_tools(self) -> str: """List all available tools in a readable format""" tools = self.discover_tools() output = [] output.append("🔧 Available MCP Tools") output.append("=" * 50) for tool in tools: output.append(f"• {tool['name']}: {tool.get('description', 'No description')}") return "\n".join(output) def main(): """Main entry point""" client = MCPClient() if len(sys.argv) < 2: print("MCP Client - Model Context Protocol Integration") print("") print("Usage:") print(" mcp-client.py list - List all available tools") print(" mcp-client.py call - Call a specific tool") print(" mcp-client.py discover - Discover and cache tools") print("") print(client.list_tools()) return command = sys.argv[1] if command == "list": print(client.list_tools()) elif command == "discover": tools = client.discover_tools() print(f"Discovered {len(tools)} tools") elif command == "call": if len(sys.argv) < 3: print("Error: Tool name required") return tool_name = sys.argv[2] args = json.loads(sys.argv[3]) if len(sys.argv) > 3 else {} result = client.call_tool(tool_name, args) print(json.dumps(result, indent=2)) else: print(f"Unknown command: {command}") if __name__ == "__main__": main()