Skip to main content
Glama

MCP Server

by DPoitrast
client.py12.8 kB
#!/usr/bin/env python3 """ Bovisync MCP Client A client for interacting with the Bovisync MCP Server. """ import asyncio import json import logging from typing import Dict, List, Optional, Any import httpx import argparse # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class BovisyncMCPClient: """Client for Bovisync MCP Server.""" def __init__( self, mcp_server_url: str = "http://localhost:8002", auth_token: Optional[str] = None ): self.mcp_server_url = mcp_server_url.rstrip("/") self.auth_token = auth_token self.client = httpx.AsyncClient(timeout=30.0) # Default headers self.headers = { "Content-Type": "application/json", "Accept": "application/json" } if self.auth_token: self.headers["Authorization"] = f"Bearer {self.auth_token}" async def get_server_info(self) -> Dict[str, Any]: """Get MCP server information.""" try: response = await self.client.get(f"{self.mcp_server_url}/") response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"Failed to connect to MCP server: {e}") return {"error": str(e)} async def list_operations(self) -> List[Dict[str, Any]]: """List all available operations.""" try: response = await self.client.get( f"{self.mcp_server_url}/operations", headers=self.headers ) response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"Failed to list operations: {e}") return [] async def execute_operation( self, operation: str, parameters: Dict[str, Any] = None ) -> Dict[str, Any]: """Execute an MCP operation.""" if parameters is None: parameters = {} try: payload = { "operation": operation, "parameters": parameters } response = await self.client.post( f"{self.mcp_server_url}/execute", headers=self.headers, json=payload ) response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"Failed to execute operation: {e}") return {"success": False, "error": str(e)} async def health_check(self) -> Dict[str, Any]: """Check MCP server health.""" try: response = await self.client.get(f"{self.mcp_server_url}/health") response.raise_for_status() return response.json() except httpx.RequestError as e: logger.error(f"Health check failed: {e}") return {"status": "unhealthy", "error": str(e)} async def close(self): """Close the HTTP client.""" await self.client.aclose() class BovisyncCLI: """Command-line interface for the Bovisync MCP Client.""" def __init__(self, client: BovisyncMCPClient): self.client = client async def show_server_info(self): """Display server information.""" print("🐄 Bovisync MCP Server Information") print("=" * 40) info = await self.client.get_server_info() if "error" in info: print(f"❌ Error: {info['error']}") return print(f"Name: {info.get('name', 'Unknown')}") print(f"Version: {info.get('version', 'Unknown')}") print(f"Description: {info.get('description', 'N/A')}") print(f"Operations: {info.get('operations_count', 0)}") print(f"API URL: {info.get('base_url', 'Unknown')}") async def list_operations(self): """List all available operations.""" print("\n📋 Available Operations") print("=" * 30) operations = await self.client.list_operations() if not operations: print("No operations available or connection failed.") return # Group operations by category categories = { "Authentication": [], "Session": [], "User": [], "Animal": [], "Event": [], "Milk": [], "Report": [] } for op in operations: # Categorize operations based on their name name = op["name"] if "token" in name: categories["Authentication"].append(op) elif "herd" in name or "session" in name: categories["Session"].append(op) elif "user" in name: categories["User"].append(op) elif "animal" in name: categories["Animal"].append(op) elif "event" in name: categories["Event"].append(op) elif "milk" in name or "parlor" in name: categories["Milk"].append(op) elif "report" in name: categories["Report"].append(op) for category, ops in categories.items(): if ops: print(f"\n📂 {category}") print("-" * 20) for op in ops: print(f" • {op['name']}") print(f" {op['method']} {op['path']}") print(f" {op['description']}") if op['parameters']: print(f" Parameters: {', '.join(op['parameters'])}") if op.get('scope'): print(f" Scope: {op['scope']}") print() async def execute_operation(self, operation: str, parameters: Dict[str, Any]): """Execute a specific operation.""" print(f"\n🔄 Executing: {operation}") print("-" * 30) result = await self.client.execute_operation(operation, parameters) if result.get("success"): print("✅ Operation successful!") if result.get("result"): print("\nResult:") print(json.dumps(result["result"], indent=2)) else: print("❌ Operation failed!") if result.get("error"): print(f"Error: {result['error']}") async def health_check(self): """Check server health.""" print("\n🏥 Health Check") print("-" * 15) health = await self.client.health_check() status = health.get("status", "unknown") if status == "healthy": print("✅ Server is healthy") else: print("❌ Server is unhealthy") print(f"Status: {status}") if "bovisync_api_connected" in health: api_status = "✅" if health["bovisync_api_connected"] else "❌" print(f"Bovisync API: {api_status}") if "active_herd" in health and health["active_herd"]: print(f"Active Herd: {health['active_herd']}") if health.get("timestamp"): print(f"Timestamp: {health['timestamp']}") if health.get("error"): print(f"Error: {health['error']}") async def interactive_mode(self): """Run interactive mode.""" print("\n🎯 Interactive Mode - Bovisync MCP") print("Type 'help' for commands, 'quit' to exit") print("-" * 40) while True: try: command = input("\nBovisync MCP> ").strip() if command.lower() in ['quit', 'exit', 'q']: break elif command.lower() in ['help', 'h']: await self.show_help() elif command.lower() in ['info', 'i']: await self.show_server_info() elif command.lower() in ['list', 'l']: await self.list_operations() elif command.lower() in ['health', 'status']: await self.health_check() elif command.startswith('exec '): await self.handle_exec_command(command[5:]) else: print("Unknown command. Type 'help' for available commands.") except KeyboardInterrupt: print("\nUse 'quit' to exit.") except EOFError: break async def show_help(self): """Show help information.""" help_text = """ 🆘 Available Commands: Basic Commands: help, h - Show this help info, i - Show server information list, l - List available operations health, status - Check server health quit, exit, q - Exit interactive mode Operation Execution: exec <operation> [params] - Execute an operation Examples: exec get_user_herds exec set_active_herd herd_id=123 exec list_animals limit=50 active_only=true exec get_animal_data animal_id=456 report_items="milk,reproduction" exec list_events event_type=breeding date_from=2024-01-01 exec get_milk_test_data animal_id=789 limit=100 Parameter Format: key=value key2="string value" key3=123 key4=true """ print(help_text) async def handle_exec_command(self, command_args: str): """Handle exec command with parameters.""" parts = command_args.split() if not parts: print("Usage: exec <operation> [key=value ...]") return operation = parts[0] parameters = {} # Parse parameters for param in parts[1:]: if "=" in param: key, value = param.split("=", 1) # Try to parse as number or boolean try: if value.lower() in ['true', 'false']: value = value.lower() == 'true' elif "." in value: value = float(value) else: value = int(value) except ValueError: # Remove quotes if present if value.startswith('"') and value.endswith('"'): value = value[1:-1] elif value.startswith("'") and value.endswith("'"): value = value[1:-1] parameters[key] = value else: print(f"Invalid parameter format: {param}") return await self.execute_operation(operation, parameters) async def main(): """Main CLI function.""" parser = argparse.ArgumentParser(description="Bovisync MCP Client") parser.add_argument("--server", default="http://localhost:8002", help="MCP server URL") parser.add_argument("--token", help="Authentication token") parser.add_argument("--operation", help="Operation to execute") parser.add_argument("--parameters", help="Operation parameters (JSON)") parser.add_argument("--interactive", "-i", action="store_true", help="Run in interactive mode") parser.add_argument("--info", action="store_true", help="Show server info") parser.add_argument("--list", action="store_true", help="List operations") parser.add_argument("--health", action="store_true", help="Check health") args = parser.parse_args() # Create client client = BovisyncMCPClient( mcp_server_url=args.server, auth_token=args.token ) cli = BovisyncCLI(client) try: if args.interactive: await cli.interactive_mode() elif args.info: await cli.show_server_info() elif args.list: await cli.list_operations() elif args.health: await cli.health_check() elif args.operation: parameters = {} if args.parameters: try: parameters = json.loads(args.parameters) except json.JSONDecodeError as e: print(f"Invalid JSON parameters: {e}") return await cli.execute_operation(args.operation, parameters) else: # Default: show server info await cli.show_server_info() print("\nUse --help for more options or --interactive for interactive mode") finally: await client.close() if __name__ == "__main__": asyncio.run(main())

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DPoitrast/MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server