├── .gitignore ├── ComputerUse ├── README.md ├── claude_automation.py ├── claude_live_test.py ├── current_action.py ├── execute_commands.py ├── full_mcp_integration.py ├── mcp_command.py ├── mcp_computer_server.py ├── mcp_integration.py ├── mcp_keyboard_actions.py ├── test_client.py ├── test_computer_control.py └── test_computer_copy.py ├── Curl_Server ├── curl_server.py ├── start_mcp_server.sh └── start_server.sh ├── README.md ├── claude_desktop_config.json ├── config └── claude_desktop_config.json ├── debug.log ├── fastapi ├── duckdb │ ├── README.md │ ├── assets │ │ ├── duckdb_query1.png │ │ └── duckdb_query2.png │ └── main.py ├── how to use └── main.py ├── install_dependencies.sh ├── mcp_screen_server copy.py ├── mcp_screen_server.py ├── requirements.txt ├── test_capture.py ├── test_screen_server.py └── test_venv_capture.py /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /ComputerUse/README.md: -------------------------------------------------------------------------------- 1 | # Computer Use Module 2 | 3 | This directory contains the computer control and automation components of the MCP server. -------------------------------------------------------------------------------- /ComputerUse/claude_automation.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | class ClaudeAutomation: 6 | def __init__(self, uri="ws://localhost:8767"): 7 | self.uri = uri 8 | 9 | async def execute_commands(self, commands): 10 | async with websockets.connect(self.uri) as websocket: 11 | results = [] 12 | for cmd in commands: 13 | try: 14 | await websocket.send(json.dumps(cmd)) 15 | response = await websocket.recv() 16 | results.append(json.loads(response)) 17 | await asyncio.sleep(3) # Delay between commands 18 | except Exception as e: 19 | print(f"Error executing command {cmd}: {str(e)}") 20 | results.append({"status": "error", "message": str(e)}) 21 | return results 22 | 23 | async def select_all_and_copy(self): 24 | commands = [ 25 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 26 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]} 27 | ] 28 | return await self.execute_commands(commands) 29 | 30 | async def get_mouse_position(self): 31 | commands = [ 32 | {"type": "system", "action": "get_mouse_position"} 33 | ] 34 | return await self.execute_commands(commands) 35 | 36 | async def move_and_click(self, x, y): 37 | commands = [ 38 | {"type": "mouse", "action": "move", "x": x, "y": y}, 39 | {"type": "mouse", "action": "click"} 40 | ] 41 | return await self.execute_commands(commands) 42 | 43 | async def paste(self): 44 | commands = [ 45 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "v"]} 46 | ] 47 | return await self.execute_commands(commands) 48 | 49 | async def main(): 50 | automation = ClaudeAutomation() 51 | 52 | results = await automation.select_all_and_copy() 53 | print("Select and copy results:", json.dumps(results, indent=2)) 54 | 55 | position = await automation.get_mouse_position() 56 | print("Mouse position:", json.dumps(position, indent=2)) 57 | 58 | click_results = await automation.move_and_click(500, 500) 59 | print("Move and click results:", json.dumps(click_results, indent=2)) 60 | 61 | paste_results = await automation.paste() 62 | print("Paste results:", json.dumps(paste_results, indent=2)) 63 | 64 | if __name__ == "__main__": 65 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/claude_live_test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | async def run_claude_test(): 6 | uri = "ws://localhost:8767" 7 | 8 | # Test sequence 9 | commands = [ 10 | # Get initial mouse position 11 | {"type": "system", "action": "get_mouse_position"}, 12 | 13 | # Move mouse to center and click 14 | {"type": "mouse", "action": "move", "x": 500, "y": 500}, 15 | {"type": "mouse", "action": "click"}, 16 | 17 | # Type a test message 18 | {"type": "keyboard", "action": "type", "text": "Hello from Claude! Testing MCP connection."}, 19 | 20 | # Select all and copy 21 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 22 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]} 23 | ] 24 | 25 | try: 26 | async with websockets.connect(uri) as websocket: 27 | print("Connected to MCP server") 28 | 29 | for cmd in commands: 30 | print(f"\nSending command: {json.dumps(cmd, indent=2)}") 31 | await websocket.send(json.dumps(cmd)) 32 | response = await websocket.recv() 33 | print(f"Server response: {response}") 34 | await asyncio.sleep(2) # Wait between commands 35 | 36 | print("\nTest sequence completed successfully") 37 | 38 | except Exception as e: 39 | print(f"Error during test: {e}") 40 | 41 | if __name__ == "__main__": 42 | asyncio.run(run_claude_test()) -------------------------------------------------------------------------------- /ComputerUse/current_action.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | async def test_connection(): 6 | uri = "ws://localhost:8767" 7 | # First let's check system info 8 | command = {"type": "system", "action": "get_mouse_position"} 9 | 10 | try: 11 | async with websockets.connect(uri) as websocket: 12 | print("Connected to MCP server") 13 | await websocket.send(json.dumps(command)) 14 | response = await websocket.recv() 15 | print(f"Current mouse position: {response}") 16 | 17 | # Now move mouse to center 18 | move_command = { 19 | "type": "mouse", 20 | "action": "move", 21 | "x": 500, 22 | "y": 500 23 | } 24 | await websocket.send(json.dumps(move_command)) 25 | move_response = await websocket.recv() 26 | print(f"Move result: {move_response}") 27 | 28 | except Exception as e: 29 | print(f"Error: {e}") 30 | 31 | if __name__ == "__main__": 32 | asyncio.run(test_connection()) -------------------------------------------------------------------------------- /ComputerUse/execute_commands.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | async def execute_mcp_commands(): 6 | uri = "ws://localhost:8767" 7 | commands = [ 8 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 9 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]}, 10 | {"type": "system", "action": "get_mouse_position"}, 11 | {"type": "mouse", "action": "move", "x": 500, "y": 500}, 12 | {"type": "mouse", "action": "click"}, 13 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "v"]} 14 | ] 15 | 16 | try: 17 | async with websockets.connect(uri) as websocket: 18 | print("Connected to MCP server") 19 | for cmd in commands: 20 | print(f"\nExecuting command: {json.dumps(cmd, indent=2)}") 21 | await websocket.send(json.dumps(cmd)) 22 | response = await websocket.recv() 23 | print(f"Response: {response}") 24 | await asyncio.sleep(3) # Wait 3 seconds between commands 25 | 26 | print("\nAll commands executed successfully") 27 | 28 | except Exception as e: 29 | print(f"Error: {e}") 30 | 31 | if __name__ == "__main__": 32 | asyncio.run(execute_mcp_commands()) -------------------------------------------------------------------------------- /ComputerUse/full_mcp_integration.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | import sys 5 | 6 | class MCPIntegrationServer: 7 | def __init__(self): 8 | self.host = 'localhost' 9 | self.port = 8767 10 | 11 | async def handle_connection(self, websocket): 12 | print("New client connected to integration server") 13 | try: 14 | async for message in websocket: 15 | try: 16 | # Parse incoming message 17 | command = json.loads(message) 18 | 19 | # Forward command to computer control server 20 | async with websockets.connect(f'ws://{self.host}:{self.port}') as computer_ws: 21 | await computer_ws.send(json.dumps(command)) 22 | response = await computer_ws.recv() 23 | # Forward response back to client 24 | await websocket.send(response) 25 | 26 | except json.JSONDecodeError as e: 27 | error_response = { 28 | 'status': 'error', 29 | 'message': f'Invalid JSON format: {str(e)}' 30 | } 31 | await websocket.send(json.dumps(error_response)) 32 | except Exception as e: 33 | error_response = { 34 | 'status': 'error', 35 | 'message': str(e) 36 | } 37 | await websocket.send(json.dumps(error_response)) 38 | except Exception as e: 39 | print(f"Connection error: {e}") 40 | finally: 41 | print("Client disconnected from integration server") 42 | 43 | async def start_server(self): 44 | integration_port = 8768 # Different port from computer server 45 | server = await websockets.serve( 46 | self.handle_connection, 47 | self.host, 48 | integration_port 49 | ) 50 | 51 | print(f"MCP Integration Server running on ws://{self.host}:{integration_port}") 52 | await server.wait_closed() 53 | 54 | async def main(): 55 | try: 56 | integration_server = MCPIntegrationServer() 57 | await integration_server.start_server() 58 | except KeyboardInterrupt: 59 | print("\nServer stopped by user") 60 | except Exception as e: 61 | print(f"Server error: {e}") 62 | sys.exit(1) 63 | 64 | if __name__ == "__main__": 65 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/mcp_command.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | class ClaudeComputerClient: 6 | def __init__(self, uri="ws://localhost:8767"): 7 | self.uri = uri 8 | 9 | async def send_command(self, command_data): 10 | async with websockets.connect(self.uri) as websocket: 11 | await websocket.send(json.dumps(command_data)) 12 | response = await websocket.recv() 13 | return json.loads(response) 14 | 15 | async def execute_sequence(self, commands): 16 | results = [] 17 | for cmd in commands: 18 | result = await self.send_command(cmd) 19 | results.append(result) 20 | await asyncio.sleep(3) 21 | return results 22 | 23 | async def main(): 24 | client = ClaudeComputerClient() 25 | commands = [ 26 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 27 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]}, 28 | {"type": "system", "action": "get_mouse_position"}, 29 | {"type": "mouse", "action": "move", "x": 500, "y": 500}, 30 | {"type": "mouse", "action": "click"}, 31 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "v"]} 32 | ] 33 | 34 | try: 35 | results = await client.execute_sequence(commands) 36 | print(json.dumps(results, indent=2)) 37 | except Exception as e: 38 | print(f"Error: {str(e)}") 39 | 40 | if __name__ == "__main__": 41 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/mcp_computer_server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | import pyautogui 5 | import base64 6 | import io 7 | from PIL import Image 8 | from mss import mss 9 | import time 10 | import sys 11 | import os 12 | import pyperclip 13 | 14 | class ComputerControlServer: 15 | def __init__(self, host='localhost', port=8767): 16 | self.host = host 17 | self.port = port 18 | self.sct = mss() 19 | pyautogui.FAILSAFE = False 20 | self.screen_width, self.screen_height = pyautogui.size() 21 | 22 | async def execute_action(self, action_data): 23 | """Execute various computer control actions""" 24 | action_type = action_data.get('type', '') 25 | try: 26 | if action_type == 'keyboard': 27 | return await self.handle_keyboard_action(action_data) 28 | elif action_type == 'mouse': 29 | return await self.handle_mouse_action(action_data) 30 | elif action_type == 'system': 31 | return await self.handle_system_action(action_data) 32 | elif action_type == 'text': 33 | return await self.handle_text_action(action_data) 34 | else: 35 | return {'status': 'error', 'message': 'Unknown action type'} 36 | except Exception as e: 37 | return {'status': 'error', 'message': str(e)} 38 | 39 | async def handle_keyboard_action(self, data): 40 | """Handle keyboard-related actions""" 41 | action = data.get('action', '') 42 | 43 | if action == 'type': 44 | text = data.get('text', '') 45 | pyautogui.write(text) 46 | return {'status': 'success', 'action': 'type', 'text': text} 47 | 48 | elif action == 'hotkey': 49 | keys = data.get('keys', []) 50 | pyautogui.hotkey(*keys) 51 | return {'status': 'success', 'action': 'hotkey', 'keys': keys} 52 | 53 | elif action == 'press': 54 | key = data.get('key', '') 55 | pyautogui.press(key) 56 | return {'status': 'success', 'action': 'press', 'key': key} 57 | 58 | async def handle_mouse_action(self, data): 59 | """Handle mouse-related actions""" 60 | action = data.get('action', '') 61 | 62 | if action == 'move': 63 | x = data.get('x', 0) 64 | y = data.get('y', 0) 65 | pyautogui.moveTo(x, y) 66 | return {'status': 'success', 'action': 'move', 'position': {'x': x, 'y': y}} 67 | 68 | elif action == 'click': 69 | button = data.get('button', 'left') 70 | clicks = data.get('clicks', 1) 71 | pyautogui.click(button=button, clicks=clicks) 72 | return {'status': 'success', 'action': 'click', 'button': button, 'clicks': clicks} 73 | 74 | elif action == 'drag': 75 | start_x = data.get('start_x', 0) 76 | start_y = data.get('start_y', 0) 77 | end_x = data.get('end_x', 0) 78 | end_y = data.get('end_y', 0) 79 | duration = data.get('duration', 0.5) 80 | pyautogui.dragTo(end_x, end_y, duration=duration) 81 | return {'status': 'success', 'action': 'drag'} 82 | 83 | async def handle_system_action(self, data): 84 | """Handle system-related actions""" 85 | action = data.get('action', '') 86 | 87 | if action == 'get_screen_size': 88 | return { 89 | 'status': 'success', 90 | 'action': 'get_screen_size', 91 | 'width': self.screen_width, 92 | 'height': self.screen_height 93 | } 94 | 95 | elif action == 'get_mouse_position': 96 | x, y = pyautogui.position() 97 | return { 98 | 'status': 'success', 99 | 'action': 'get_mouse_position', 100 | 'x': x, 101 | 'y': y 102 | } 103 | 104 | async def handle_text_action(self, data): 105 | """Handle text-related actions""" 106 | action = data.get('action', '') 107 | 108 | if action == 'copy': 109 | pyautogui.hotkey('command', 'a') 110 | await asyncio.sleep(0.1) 111 | pyautogui.hotkey('command', 'c') 112 | await asyncio.sleep(0.1) 113 | text = pyperclip.paste() 114 | return { 115 | 'status': 'success', 116 | 'action': 'copy', 117 | 'text': text 118 | } 119 | 120 | elif action == 'paste': 121 | text = data.get('text', '') 122 | pyperclip.copy(text) 123 | pyautogui.hotkey('command', 'v') 124 | return {'status': 'success', 'action': 'paste'} 125 | 126 | async def handle_connection(self, websocket): 127 | print("New client connected") 128 | try: 129 | async for message in websocket: 130 | try: 131 | data = json.loads(message) 132 | response = await self.execute_action(data) 133 | await websocket.send(json.dumps(response)) 134 | except json.JSONDecodeError: 135 | await websocket.send(json.dumps({ 136 | 'status': 'error', 137 | 'message': 'Invalid JSON format' 138 | })) 139 | except Exception as e: 140 | await websocket.send(json.dumps({ 141 | 'status': 'error', 142 | 'message': str(e) 143 | })) 144 | except Exception as e: 145 | print(f"Connection error: {e}") 146 | finally: 147 | print("Client disconnected") 148 | 149 | async def start_server(self): 150 | if sys.platform == 'darwin': 151 | try: 152 | import Quartz 153 | except ImportError: 154 | print("Please install pyobjc-framework-Quartz for macOS support") 155 | sys.exit(1) 156 | 157 | server = websockets.serve( 158 | lambda ws: self.handle_connection(ws), 159 | self.host, 160 | self.port 161 | ) 162 | 163 | print(f"Computer Control Server starting on ws://{self.host}:{self.port}") 164 | 165 | async with server: 166 | await asyncio.Future() 167 | 168 | async def main(): 169 | try: 170 | server = ComputerControlServer() 171 | await server.start_server() 172 | except KeyboardInterrupt: 173 | print("\nServer stopped by user") 174 | except Exception as e: 175 | print(f"Server error: {e}") 176 | sys.exit(1) 177 | 178 | if __name__ == "__main__": 179 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/mcp_integration.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | import sys 5 | import os 6 | 7 | class MCPIntegration: 8 | def __init__(self): 9 | self.computer_uri = "ws://localhost:8767" # Computer control server 10 | self.commands = [ 11 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 12 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]}, 13 | {"type": "system", "action": "get_mouse_position"}, 14 | {"type": "mouse", "action": "move", "x": 500, "y": 500}, 15 | {"type": "mouse", "action": "click"}, 16 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "v"]} 17 | ] 18 | 19 | async def execute_computer_commands(self): 20 | try: 21 | async with websockets.connect(self.computer_uri) as websocket: 22 | print("Connected to Computer Control Server") 23 | for cmd in commands: 24 | print(f"\nExecuting: {json.dumps(cmd, indent=2)}") 25 | await websocket.send(json.dumps(cmd)) 26 | response = await websocket.recv() 27 | print(f"Response: {response}") 28 | await asyncio.sleep(3) 29 | except Exception as e: 30 | print(f"Error with computer control: {e}") 31 | 32 | async def main(): 33 | mcp = MCPIntegration() 34 | await mcp.execute_computer_commands() 35 | 36 | if __name__ == "__main__": 37 | # Use the python path from your config 38 | python_path = "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python" 39 | if not os.path.exists(python_path): 40 | print(f"Warning: Python path {python_path} not found") 41 | 42 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/mcp_keyboard_actions.py: -------------------------------------------------------------------------------- 1 | import pyautogui 2 | import json 3 | 4 | class KeyboardActions: 5 | @staticmethod 6 | async def perform_action(websocket, action_type, data): 7 | try: 8 | if action_type == 'hotkey': 9 | # Handle keyboard shortcuts 10 | keys = data.get('keys', []) 11 | if keys: 12 | pyautogui.hotkey(*keys) 13 | await websocket.send(json.dumps({ 14 | 'status': 'success', 15 | 'action': 'hotkey', 16 | 'keys': keys 17 | })) 18 | 19 | elif action_type == 'select_all': 20 | # Select all text (Command+A on macOS) 21 | pyautogui.hotkey('command', 'a') 22 | await websocket.send(json.dumps({ 23 | 'status': 'success', 24 | 'action': 'select_all' 25 | })) 26 | 27 | elif action_type == 'copy': 28 | # Copy (Command+C on macOS) 29 | pyautogui.hotkey('command', 'c') 30 | await websocket.send(json.dumps({ 31 | 'status': 'success', 32 | 'action': 'copy' 33 | })) 34 | 35 | elif action_type == 'paste': 36 | # Paste (Command+V on macOS) 37 | pyautogui.hotkey('command', 'v') 38 | await websocket.send(json.dumps({ 39 | 'status': 'success', 40 | 'action': 'paste' 41 | })) 42 | 43 | elif action_type == 'shift_tab': 44 | # Navigate backwards 45 | pyautogui.hotkey('shift', 'tab') 46 | await websocket.send(json.dumps({ 47 | 'status': 'success', 48 | 'action': 'shift_tab' 49 | })) 50 | 51 | except Exception as e: 52 | await websocket.send(json.dumps({ 53 | 'status': 'error', 54 | 'action': action_type, 55 | 'message': str(e) 56 | })) -------------------------------------------------------------------------------- /ComputerUse/test_client.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | 5 | async def run_test(): 6 | uri = "ws://localhost:8767" 7 | sequence = [ 8 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "a"]}, 9 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "c"]}, 10 | {"type": "system", "action": "get_mouse_position"}, 11 | {"type": "mouse", "action": "move", "x": 500, "y": 500}, 12 | {"type": "mouse", "action": "click"}, 13 | {"type": "keyboard", "action": "hotkey", "keys": ["command", "v"]} 14 | ] 15 | 16 | async with websockets.connect(uri) as websocket: 17 | for cmd in sequence: 18 | await websocket.send(json.dumps(cmd)) 19 | response = await websocket.recv() 20 | print(f"Command: {cmd}") 21 | print(f"Response: {response}\n") 22 | await asyncio.sleep(3) # 3 second delay between commands 23 | 24 | if __name__ == "__main__": 25 | asyncio.run(run_test()) -------------------------------------------------------------------------------- /ComputerUse/test_computer_control.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | import time 5 | 6 | async def test_computer_control(): 7 | uri = "ws://localhost:8767" 8 | print("Connecting to", uri) 9 | 10 | try: 11 | async with websockets.connect(uri) as websocket: 12 | print("Connected to Computer Control Server") 13 | 14 | # Example 1: Get screen size 15 | request = { 16 | 'command': 'get_screen_size' 17 | } 18 | print(f"Sending request: {request}") 19 | await websocket.send(json.dumps(request)) 20 | 21 | response = await websocket.recv() 22 | print(f"Received response: {response}") 23 | 24 | # Example 2: Move mouse in a square pattern 25 | points = [ 26 | (100, 100), 27 | (300, 100), 28 | (300, 300), 29 | (100, 300), 30 | (100, 100) 31 | ] 32 | 33 | print("\nMoving mouse in a square pattern...") 34 | for x, y in points: 35 | request = { 36 | 'command': 'move', 37 | 'x': x, 38 | 'y': y 39 | } 40 | print(f"Moving to: ({x}, {y})") 41 | await websocket.send(json.dumps(request)) 42 | await asyncio.sleep(1) 43 | 44 | # Example 3: Perform click 45 | print("\nPerforming click...") 46 | request = { 47 | 'command': 'click', 48 | 'button': 'left', 49 | 'clicks': 1 50 | } 51 | await websocket.send(json.dumps(request)) 52 | await asyncio.sleep(1) 53 | 54 | # Example 4: Scroll test 55 | print("\nTesting scroll...") 56 | request = { 57 | 'command': 'scroll', 58 | 'amount': 100 59 | } 60 | await websocket.send(json.dumps(request)) 61 | await asyncio.sleep(1) 62 | 63 | print("Test completed successfully") 64 | 65 | except websockets.exceptions.ConnectionRefusedError: 66 | print("Could not connect to the server. Make sure the server is running.") 67 | except Exception as e: 68 | print(f"Test error: {str(e)}") 69 | 70 | async def main(): 71 | print("Starting Computer Control Test...") 72 | try: 73 | await test_computer_control() 74 | except KeyboardInterrupt: 75 | print("\nTest stopped by user") 76 | except Exception as e: 77 | print(f"Main error: {str(e)}") 78 | 79 | if __name__ == "__main__": 80 | asyncio.run(main()) -------------------------------------------------------------------------------- /ComputerUse/test_computer_copy.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import websockets 3 | import json 4 | import time 5 | import pyautogui 6 | 7 | async def copy_and_navigate(): 8 | uri = "ws://localhost:8767" 9 | print("Connecting to", uri) 10 | 11 | try: 12 | async with websockets.connect(uri) as websocket: 13 | print("Connected to Computer Control Server") 14 | 15 | # 1. Get screen size for reference 16 | await websocket.send(json.dumps({ 17 | 'command': 'get_screen_size' 18 | })) 19 | response = await websocket.recv() 20 | screen_info = json.loads(response) 21 | print(f"Screen size: {screen_info}") 22 | 23 | # 2. Simulate Shift+Tab (navigate backwards) 24 | print("\nPerforming Shift+Tab...") 25 | pyautogui.hotkey('shift', 'tab') 26 | await asyncio.sleep(1) 27 | 28 | # 3. Select text (click and drag) 29 | print("\nSelecting text...") 30 | await websocket.send(json.dumps({ 31 | 'command': 'drag', 32 | 'start_x': 200, 33 | 'start_y': 200, 34 | 'end_x': 400, 35 | 'end_y': 200 36 | })) 37 | await asyncio.sleep(1) 38 | 39 | # 4. Copy selected text (Command+C on macOS) 40 | print("\nCopying text...") 41 | pyautogui.hotkey('command', 'c') 42 | await asyncio.sleep(1) 43 | 44 | print("Operations completed successfully") 45 | 46 | except websockets.exceptions.ConnectionRefusedError: 47 | print("Could not connect to the server. Make sure the server is running.") 48 | except Exception as e: 49 | print(f"Test error: {str(e)}") 50 | 51 | async def main(): 52 | print("Starting Copy and Navigation Test...") 53 | try: 54 | await copy_and_navigate() 55 | except KeyboardInterrupt: 56 | print("\nTest stopped by user") 57 | except Exception as e: 58 | print(f"Main error: {str(e)}") 59 | 60 | if __name__ == "__main__": 61 | asyncio.run(main()) -------------------------------------------------------------------------------- /Curl_Server/curl_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import subprocess 3 | import json 4 | from http.server import HTTPServer, BaseHTTPRequestHandler 5 | import logging 6 | 7 | # Configure logging 8 | logging.basicConfig(level=logging.INFO, 9 | format='%(asctime)s [%(levelname)s] %(message)s') 10 | logger = logging.getLogger(__name__) 11 | 12 | class CurlServer(BaseHTTPRequestHandler): 13 | def do_POST(self): 14 | try: 15 | # Default curl command 16 | curl_command = [ 17 | 'curl', 18 | '-X', 'POST', 19 | 'http://localhost:8000/process', 20 | '-H', 'accept: application/json', 21 | '-H', 'Content-Type: application/json', 22 | '-d', json.dumps({ 23 | "date_column": "Start Time", 24 | "model_column": "Device Brand", 25 | "csv_file_paths": "S5_S8_2024_11_20_15_27_33.csv,S5_S8_2024_11_25_10_10_12.csv" 26 | }) 27 | ] 28 | 29 | # Execute curl command 30 | logger.info(f"Executing curl command: {' '.join(curl_command)}") 31 | process = subprocess.Popen( 32 | curl_command, 33 | stdout=subprocess.PIPE, 34 | stderr=subprocess.PIPE 35 | ) 36 | stdout, stderr = process.communicate() 37 | 38 | # Check if there was an error 39 | if process.returncode != 0: 40 | logger.error(f"Curl command failed with error: {stderr.decode()}") 41 | self.send_response(500) 42 | self.send_header('Content-Type', 'application/json') 43 | self.end_headers() 44 | self.wfile.write(json.dumps({ 45 | "error": "Curl command failed", 46 | "details": stderr.decode() 47 | }).encode()) 48 | return 49 | 50 | # Send successful response 51 | self.send_response(200) 52 | self.send_header('Content-Type', 'application/json') 53 | self.end_headers() 54 | 55 | # Try to parse the response as JSON 56 | try: 57 | response_data = json.loads(stdout.decode()) 58 | self.wfile.write(json.dumps(response_data).encode()) 59 | except json.JSONDecodeError: 60 | self.wfile.write(stdout) 61 | 62 | logger.info("Successfully processed request") 63 | 64 | except Exception as e: 65 | logger.error(f"Error processing request: {str(e)}") 66 | self.send_response(500) 67 | self.send_header('Content-Type', 'application/json') 68 | self.end_headers() 69 | self.wfile.write(json.dumps({ 70 | "error": "Internal server error", 71 | "details": str(e) 72 | }).encode()) 73 | 74 | def do_GET(self): 75 | # Simple health check endpoint 76 | self.send_response(200) 77 | self.send_header('Content-Type', 'application/json') 78 | self.end_headers() 79 | self.wfile.write(json.dumps({ 80 | "status": "healthy", 81 | "message": "Curl server is running" 82 | }).encode()) 83 | 84 | def run_server(port=9000): 85 | server_address = ('', port) 86 | httpd = HTTPServer(server_address, CurlServer) 87 | logger.info(f"Starting curl server on port {port}") 88 | httpd.serve_forever() 89 | 90 | if __name__ == '__main__': 91 | run_server() -------------------------------------------------------------------------------- /Curl_Server/start_mcp_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Activate virtual environment if it exists 4 | if [ -d "../.env" ]; then 5 | source ../.env/bin/activate 6 | fi 7 | 8 | # Make the Python script executable 9 | chmod +x curl_mcp_server.py 10 | 11 | # Run the server 12 | python3 curl_mcp_server.py -------------------------------------------------------------------------------- /Curl_Server/start_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Activate virtual environment if it exists 4 | if [ -d "../.env" ]; then 5 | source ../.env/bin/activate 6 | fi 7 | 8 | # Make the Python script executable 9 | chmod +x curl_server.py 10 | 11 | # Run the server 12 | python3 curl_server.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Claude MCP Server Collection 2 | 3 | This repository contains a collection of Model Context Protocol (MCP) servers designed to enhance Claude's desktop application capabilities. Each server provides specific functionality that allows Claude to interact with your computer in different ways. 4 | 5 | ## Latest Addition: DuckDB Integration! 🚀 6 | 7 | New DuckDB FastAPI server for efficient large-scale data analysis: 8 | 9 | ### Example Queries and Results 10 | 11 | #### Query 1: Analysis 12 | ![DuckDB Query Example 1](./fastapi/duckdb/assets/duckdb_query1.png) 13 | 14 | #### Query 2: Aggregated Results 15 | ![DuckDB Query Example 2](./fastapi/duckdb/assets/duckdb_query2.png) 16 | 17 | ### DuckDB Server Features 18 | - Analyze multiple CSV files (>1GB) directly in Claude Desktop 19 | - Lightning-fast SQL queries with DuckDB 20 | - Memory-efficient data processing 21 | - Connection pooling and caching 22 | - Automatic cleanup of unused connections 23 | 24 | ## Overview 25 | 26 | The project consists of several MCP servers: 27 | 1. Screen Capture Server - Captures and processes screenshots 28 | 2. Computer Control Server - Enables keyboard and mouse automation 29 | 3. FastAPI Integration Server - Handles data processing and API endpoints 30 | 4. Curl Server - Provides HTTP request capabilities 31 | 5. DuckDB Server - Enables large-scale data analysis 32 | 33 | ## Prerequisites 34 | 35 | - Python 3.8 or higher 36 | - Node.js and npm (for filesystem server) 37 | - Claude Desktop Application 38 | - Git 39 | 40 | ## Installation 41 | 42 | 1. Clone the repository: 43 | ```bash 44 | git clone https://github.com/syedazharmbnr1/ClaudeMCPServer.git 45 | cd ClaudeMCPServer 46 | ``` 47 | 48 | 2. Create and activate a virtual environment: 49 | ```bash 50 | python3 -m venv .env 51 | source .env/bin/activate # On Windows: .env\Scripts\activate 52 | ``` 53 | 54 | 3. Install dependencies: 55 | ```bash 56 | pip install -r requirements.txt 57 | ``` 58 | 59 | ## Server Components 60 | 61 | ### 1. DuckDB Server 62 | 63 | Enables Claude to analyze large CSV files efficiently. 64 | 65 | #### Setup and Usage: 66 | ```bash 67 | cd fastapi/duckdb 68 | python main.py 69 | ``` 70 | 71 | #### Features: 72 | - Process multiple CSV files simultaneously 73 | - Handle files larger than 1GB 74 | - Fast SQL querying with DuckDB 75 | - Memory-efficient processing 76 | - Connection pooling 77 | 78 | ### 2. Screen Capture Server 79 | 80 | Enables Claude to capture and process screenshots of your screen. 81 | 82 | #### Setup and Usage: 83 | ```bash 84 | python mcp_screen_server.py 85 | ``` 86 | 87 | #### Features: 88 | - Real-time screen capture 89 | - Dynamic image compression 90 | - WebP format support for optimal file size 91 | - Customizable save locations 92 | 93 | ### 3. Computer Control Server 94 | 95 | Allows Claude to control mouse and keyboard actions. 96 | 97 | #### Setup and Usage: 98 | ```bash 99 | python ComputerUse/mcp_computer_server.py 100 | ``` 101 | 102 | #### Features: 103 | - Mouse movement and clicks 104 | - Keyboard shortcuts and text input 105 | - Screen position tracking 106 | - Clipboard operations 107 | 108 | ### 4. FastAPI Integration Server 109 | 110 | The FastAPI server provides a robust API interface for data processing and integration. 111 | 112 | #### Setup and Configuration: 113 | 114 | 1. Navigate to the FastAPI directory: 115 | ```bash 116 | cd fastapi 117 | ``` 118 | 119 | 2. Configure environment variables: 120 | ```bash 121 | export PYTHONPATH=/path/to/mcp-server-py 122 | export PORT=8000 123 | ``` 124 | 125 | 3. Start the server: 126 | ```bash 127 | python main.py 128 | ``` 129 | 130 | ### 5. Curl Server 131 | 132 | Provides HTTP request capabilities to Claude. 133 | 134 | #### Setup: 135 | ```bash 136 | cd Curl_Server 137 | ./start_server.sh # For the basic server 138 | ./start_mcp_server.sh # For the MCP integration 139 | ``` 140 | 141 | ## Claude Desktop Integration 142 | 143 | ### Configuration 144 | 145 | 1. Copy the `claude_desktop_config.json` to your Claude Desktop app configuration directory 146 | 147 | 2. Update the paths in the configuration to match your system: 148 | ```json 149 | { 150 | "mcpServers": { 151 | "filesystem": { 152 | "command": "npx", 153 | "args": [ 154 | "-y", 155 | "@modelcontextprotocol/server-filesystem", 156 | "" 157 | ] 158 | }, 159 | "duckdb": { 160 | "command": "/path/to/python", 161 | "args": [ 162 | "/path/to/fastapi/duckdb/main.py" 163 | ], 164 | "cwd": "/path/to/fastapi/duckdb", 165 | "env": { 166 | "PYTHONPATH": "/path/to/mcp-server-py", 167 | "PORT": "8010" 168 | } 169 | } 170 | } 171 | } 172 | ``` 173 | 174 | ## Testing 175 | 176 | Each component has its own test suite: 177 | 178 | ```bash 179 | # Test screen capture 180 | python test_screen_server.py 181 | 182 | # Test computer control 183 | python ComputerUse/test_computer_control.py 184 | 185 | # Test integration 186 | python ComputerUse/test_client.py 187 | ``` 188 | 189 | ## Troubleshooting 190 | 191 | ### Common Issues 192 | 193 | 1. Python Path Issues: 194 | - Ensure PYTHONPATH is set correctly 195 | - Verify virtual environment is activated 196 | 197 | 2. Permission Errors: 198 | - Make sure script files are executable: 199 | ```bash 200 | chmod +x *.py 201 | chmod +x Curl_Server/*.sh 202 | ``` 203 | 204 | 3. Port Conflicts: 205 | - Screen Server: Default port 8767 206 | - FastAPI Server: Default port 8000 207 | - DuckDB Server: Default port 8010 208 | - Integration Server: Default port 8768 209 | 210 | ### Logging 211 | 212 | - Check `debug.log` for detailed error messages 213 | - Each server component writes to its own log file 214 | 215 | ## Security Notes 216 | 217 | 1. GitHub Integration: 218 | - Store your GitHub token securely 219 | - Never commit tokens to the repository 220 | 221 | 2. File System Access: 222 | - Configure filesystem paths carefully 223 | - Limit access to necessary directories only 224 | 225 | ## Contributing 226 | 227 | 1. Fork the repository 228 | 2. Create a feature branch 229 | 3. Submit a pull request 230 | 231 | ## License 232 | 233 | This project is licensed under the MIT License - see the LICENSE file for details. 234 | -------------------------------------------------------------------------------- /claude_desktop_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "filesystem": { 4 | "command": "npx", 5 | "args": [ 6 | "-y", 7 | "@modelcontextprotocol/server-filesystem", 8 | "/Users/azhar/Desktop", 9 | "/Users/azhar/Desktop/others/claudeMCP", 10 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/ComputerUse", 11 | "/Users/azhar/Desktop/others/slidesapp/slides-react-app/public/data/S5_S8", 12 | "/Users/azhar/Desktop/others/slidesapp/slides-react-app" 13 | ] 14 | }, 15 | "github": { 16 | "command": "npx", 17 | "args": [ 18 | "-y", 19 | "@modelcontextprotocol/server-github" 20 | ], 21 | "env": { 22 | "GITHUB_PERSONAL_ACCESS_TOKEN": "your github GITHUB_PERSONAL_ACCESS_TOKEN", 23 | "GITHUB_USERNAME": "syedazharmbnr1" 24 | } 25 | }, 26 | "screen": { 27 | "command": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python", 28 | "args": [ 29 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/mcp_screen_server.py" 30 | ] 31 | }, 32 | "curlserver": { 33 | "command": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python", 34 | "args": [ 35 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/Curl_Server/curl_mcp_server.py" 36 | ], 37 | "cwd": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/Curl_Server" 38 | }, 39 | "integration": { 40 | "command": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python", 41 | "args": [ 42 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/ComputerUse/full_mcp_integration.py" 43 | ] 44 | }, 45 | "duckdb": { 46 | "command": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python", 47 | "args": [ 48 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/fastapi/duckdb/main.py" 49 | ], 50 | "cwd": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/fastapi/duckdb", 51 | "env": { 52 | "PYTHONPATH": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py", 53 | "PORT": "8010" 54 | } 55 | }, 56 | "fastapiserver": { 57 | "command": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python", 58 | "args": [ 59 | "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/fastapi/main.py" 60 | ], 61 | "cwd": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/fastapi", 62 | "env": { 63 | "PYTHONPATH": "/Users/azhar/Desktop/others/claudeMCP/mcp-server-py", 64 | "PORT": "8001" 65 | } 66 | } 67 | }, 68 | "globalShortcut": "Shift+Cmd+Space" 69 | } -------------------------------------------------------------------------------- /config/claude_desktop_config.json: -------------------------------------------------------------------------------- 1 | /Users/azhar/Desktop/others/claudeMCP/mcp-server-py/claude_desktop_config.json -------------------------------------------------------------------------------- /debug.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedazharmbnr1/ClaudeMCPServer/36d34c1318153a09b050acd9c72117d18b48708f/debug.log -------------------------------------------------------------------------------- /fastapi/duckdb/README.md: -------------------------------------------------------------------------------- 1 | # DuckDB FastAPI MCP Server 2 | 3 | A FastAPI-based Model Context Protocol (MCP) server that provides DuckDB integration for efficient CSV file querying and analysis, capable of handling large datasets (>1GB) directly through Claude Desktop. 4 | 5 | ## Example Queries and Results 6 | 7 | ### Query 1: Network Coverage Analysis 8 | ![DuckDB Query Example 1](./assets/duckdb_query1.png) 9 | 10 | ### Query 2: Aggregated Results 11 | ![DuckDB Query Example 2](./assets/duckdb_query2.png) 12 | 13 | ## Features 14 | 15 | - FastAPI integration with MCP server 16 | - DuckDB in-memory database for fast CSV querying of large files (>1GB) 17 | - Efficient analysis of multiple CSV files simultaneously 18 | - Connection pooling and caching 19 | - Automatic cleanup of unused connections 20 | - CORS support 21 | - Health check endpoint 22 | - Error handling and logging 23 | 24 | ## Query Examples 25 | 26 | ```sql 27 | -- Example of analyzing multiple large CSV files 28 | WITH coverage_summary AS ( 29 | SELECT 30 | ROUND(Latitude, 2) as lat_round, 31 | ROUND(Longitude, 2) as lon_round, 32 | AVG("RSRP(ALL MRs) (dBm)") as avg_rsrp, 33 | SUM("UMR Count") as total_mr_count 34 | FROM data 35 | GROUP BY lat_round, lon_round 36 | ) 37 | SELECT * 38 | FROM coverage_summary 39 | WHERE avg_rsrp < -90 40 | ORDER BY total_mr_count DESC 41 | LIMIT 10; 42 | ``` 43 | 44 | ## Endpoints 45 | 46 | - `POST /execute_query`: Execute DuckDB SQL queries on CSV files 47 | - `GET /health`: Health check endpoint 48 | 49 | ## Large File Handling 50 | 51 | DuckDB efficiently handles large CSV files by: 52 | - Using memory-mapped files for data access 53 | - Implementing parallel processing 54 | - Providing optimized SQL execution 55 | - Supporting streaming for large result sets 56 | 57 | ## Configuration 58 | 59 | The server runs on port 8010 by default and accepts the following environment variables: 60 | - `PYTHONPATH`: Path to the MCP server root 61 | - `PORT`: Server port (default: 8010) 62 | 63 | ## Usage 64 | 65 | 1. Start the server: 66 | ```bash 67 | python main.py 68 | ``` 69 | 70 | 2. Execute queries via the API: 71 | ```bash 72 | curl -X POST http://localhost:8010/execute_query \ 73 | -H "Content-Type: application/json" \ 74 | -d '{ 75 | "csv_file_path": "/path/to/your/large_file.csv", 76 | "query": "SELECT * FROM data LIMIT 5;" 77 | }' 78 | ``` 79 | 80 | ## Features 81 | 82 | - CSV file validation and path safety checks 83 | - Connection caching for improved performance 84 | - Automatic connection cleanup after 10 minutes of inactivity 85 | - Error handling and detailed logging 86 | - MCP tool integration for Claude AI 87 | - Support for large file analysis (>1GB) 88 | - Multi-file query capabilities 89 | 90 | ## Integration with Claude Desktop 91 | 92 | Add the following configuration to your `claude_desktop_config.json`: 93 | 94 | ```json 95 | "duckdb": { 96 | "command": "/path/to/python", 97 | "args": [ 98 | "/path/to/fastapi/duckdb/main.py" 99 | ], 100 | "cwd": "/path/to/fastapi/duckdb", 101 | "env": { 102 | "PYTHONPATH": "/path/to/mcp-server-py", 103 | "PORT": "8010" 104 | } 105 | } 106 | ``` -------------------------------------------------------------------------------- /fastapi/duckdb/assets/duckdb_query1.png: -------------------------------------------------------------------------------- 1 | data:image/png;base64,... -------------------------------------------------------------------------------- /fastapi/duckdb/assets/duckdb_query2.png: -------------------------------------------------------------------------------- 1 | data:image/png;base64,... -------------------------------------------------------------------------------- /fastapi/duckdb/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import json 4 | import logging 5 | import duckdb 6 | import threading 7 | import time 8 | import asyncio 9 | from datetime import datetime 10 | from collections.abc import Sequence 11 | from typing import Any, Optional, List 12 | from fastapi import FastAPI, HTTPException 13 | import uvicorn 14 | from pydantic import BaseModel 15 | from fastapi.middleware.cors import CORSMiddleware 16 | 17 | from mcp.server import Server 18 | from mcp.types import ( 19 | Resource, 20 | Tool, 21 | TextContent, 22 | ImageContent, 23 | EmbeddedResource, 24 | LoggingLevel 25 | ) 26 | 27 | # DuckDB cache management 28 | duckdb_cache = {} 29 | cache_access_times = {} 30 | 31 | class QueryRequest(BaseModel): 32 | csv_file_path: str 33 | query: str 34 | 35 | class MCPFastAPIServer: 36 | def __init__(self): 37 | self.app = Server("fastapi-mcp-server") 38 | self.fastapi_app = FastAPI() 39 | 40 | # Add CORS middleware 41 | self.fastapi_app.add_middleware( 42 | CORSMiddleware, 43 | allow_origins=["*"], 44 | allow_credentials=True, 45 | allow_methods=["*"], 46 | allow_headers=["*"], 47 | ) 48 | 49 | # Configure logging 50 | logging.basicConfig(level=logging.INFO) 51 | self.logger = logging.getLogger("fastapi-mcp-server") 52 | 53 | # Set up handlers 54 | self.setup_handlers() 55 | self.setup_fastapi_routes() 56 | 57 | # Start cleanup thread 58 | self.cleanup_thread = threading.Thread(target=self.cleanup_duckdb_connections, daemon=True) 59 | self.cleanup_thread.start() 60 | 61 | def is_valid_csv_path(self, csv_file_path: str) -> bool: 62 | """Validate if the CSV path is safe to use""" 63 | csv_file_path = os.path.abspath(csv_file_path) 64 | return csv_file_path.endswith('.csv') and os.path.exists(csv_file_path) 65 | 66 | def get_cache_key(self, csv_paths: List[str]) -> str: 67 | """Generate cache key based on file paths and modification times""" 68 | mod_times = [os.path.getmtime(path) for path in csv_paths] 69 | return f"{','.join(csv_paths)}:{','.join(map(str, mod_times))}" 70 | 71 | def load_csv_into_duckdb(self, csv_file_paths: List[str]) -> duckdb.DuckDBPyConnection: 72 | """Load multiple CSVs into DuckDB with caching""" 73 | # Validate all paths 74 | for path in csv_file_paths: 75 | if not self.is_valid_csv_path(path): 76 | raise ValueError(f"Invalid or non-existent CSV file path: {path}") 77 | 78 | cache_key = self.get_cache_key(csv_file_paths) 79 | 80 | if cache_key in duckdb_cache: 81 | cache_access_times[cache_key] = time.time() 82 | return duckdb_cache[cache_key] 83 | else: 84 | conn = duckdb.connect(database=':memory:') 85 | 86 | # Configure DuckDB for large files 87 | conn.execute("SET memory_limit='80%'") 88 | conn.execute("SET threads TO 4") 89 | 90 | # Load each CSV file into a separate table 91 | for i, file_path in enumerate(csv_file_paths): 92 | table_name = f"data_{i}" 93 | conn.execute(f"CREATE TABLE {table_name} AS SELECT * FROM read_csv_auto('{file_path}', sample_size=-1);") 94 | 95 | duckdb_cache[cache_key] = conn 96 | cache_access_times[cache_key] = time.time() 97 | return conn 98 | 99 | def cleanup_duckdb_connections(self): 100 | """Cleanup unused DuckDB connections periodically""" 101 | while True: 102 | time.sleep(3000) # Check every 5 minutes 103 | current_time = time.time() 104 | to_delete = [] 105 | for cache_key, last_access in cache_access_times.items(): 106 | if current_time - last_access > 600: # 10 minutes timeout 107 | conn = duckdb_cache.get(cache_key) 108 | if conn: 109 | conn.close() 110 | to_delete.append(cache_key) 111 | for cache_key in to_delete: 112 | del duckdb_cache[cache_key] 113 | del cache_access_times[cache_key] 114 | 115 | def setup_fastapi_routes(self): 116 | @self.fastapi_app.post("/execute_query") 117 | async def execute_query(request: QueryRequest): 118 | try: 119 | # Handle multiple CSV files 120 | csv_paths = [path.strip() for path in request.csv_file_path.split(',')] 121 | result = await self.execute_query_internal( 122 | csv_file_paths=csv_paths, 123 | query=request.query 124 | ) 125 | return result 126 | except Exception as e: 127 | self.logger.error(f"Error processing request: {str(e)}", exc_info=True) 128 | raise HTTPException(status_code=500, detail=str(e)) 129 | 130 | @self.fastapi_app.get("/health") 131 | async def health_check(): 132 | return {"status": "healthy", "server": "fastapi-mcp-server"} 133 | 134 | async def execute_query_internal(self, csv_file_paths: List[str], query: str): 135 | """Execute DuckDB query on CSV data""" 136 | try: 137 | # Load CSVs into DuckDB 138 | conn = self.load_csv_into_duckdb(csv_file_paths) 139 | 140 | # Execute the query with pagination 141 | result = conn.execute(query).fetchdf() 142 | 143 | # Convert DataFrame to dict for JSON serialization 144 | processed_data = result.to_dict('records') 145 | columns = list(result.columns) 146 | 147 | return { 148 | "success": True, 149 | "data": { 150 | "columns": columns, 151 | "rows": processed_data, 152 | "rowCount": len(processed_data) 153 | } 154 | } 155 | 156 | except Exception as e: 157 | self.logger.error(f"Error executing query: {str(e)}", exc_info=True) 158 | return { 159 | "success": False, 160 | "error": str(e) 161 | } 162 | 163 | def setup_handlers(self): 164 | @self.app.list_tools() 165 | async def list_tools() -> list[Tool]: 166 | return [ 167 | Tool( 168 | name="execute_query", 169 | description="Execute DuckDB query on one or more CSV files", 170 | inputSchema={ 171 | "type": "object", 172 | "properties": { 173 | "csv_file_path": { 174 | "type": "string", 175 | "description": "Comma-separated paths to CSV files" 176 | }, 177 | "query": { 178 | "type": "string", 179 | "description": "DuckDB SQL query to execute" 180 | } 181 | }, 182 | "required": ["csv_file_path", "query"] 183 | } 184 | ) 185 | ] 186 | 187 | @self.app.call_tool() 188 | async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]: 189 | if name != "execute_query": 190 | raise ValueError(f"Unknown tool: {name}") 191 | 192 | try: 193 | csv_paths = [path.strip() for path in arguments.get("csv_file_path").split(',')] 194 | result = await self.execute_query_internal( 195 | csv_file_paths=csv_paths, 196 | query=arguments.get("query") 197 | ) 198 | 199 | return [ 200 | TextContent( 201 | type="text", 202 | text=json.dumps(result, indent=2) 203 | ) 204 | ] 205 | 206 | except Exception as e: 207 | self.logger.error(f"Error processing request: {str(e)}", exc_info=True) 208 | return [ 209 | TextContent( 210 | type="text", 211 | text=json.dumps({ 212 | "success": False, 213 | "error": str(e) 214 | }, indent=2) 215 | ) 216 | ] 217 | 218 | async def run_fastapi(self): 219 | """Run the FastAPI server""" 220 | config = uvicorn.Config( 221 | self.fastapi_app, 222 | host="0.0.0.0", 223 | port=8010, 224 | log_level="info", 225 | reload=False 226 | ) 227 | server = uvicorn.Server(config) 228 | await server.serve() 229 | 230 | async def run_mcp(self): 231 | """Run the MCP server""" 232 | from mcp.server.stdio import stdio_server 233 | self.logger.info("Starting MCP Server") 234 | async with stdio_server() as (read_stream, write_stream): 235 | await self.app.run( 236 | read_stream, 237 | write_stream, 238 | self.app.create_initialization_options() 239 | ) 240 | 241 | async def run(self): 242 | """Main entry point for the server""" 243 | self.logger.info("Starting FastAPI MCP Server") 244 | try: 245 | await asyncio.gather( 246 | self.run_fastapi(), 247 | self.run_mcp() 248 | ) 249 | except Exception as e: 250 | self.logger.error(f"Server error: {str(e)}", exc_info=True) 251 | raise 252 | 253 | async def main(): 254 | try: 255 | server = MCPFastAPIServer() 256 | await server.run() 257 | except Exception as e: 258 | logging.error(f"Failed to start server: {str(e)}", exc_info=True) 259 | raise 260 | 261 | if __name__ == "__main__": 262 | try: 263 | asyncio.run(main()) 264 | except KeyboardInterrupt: 265 | logging.info("Server shutting down...") 266 | except Exception as e: 267 | logging.error(f"Unexpected error: {str(e)}", exc_info=True) 268 | -------------------------------------------------------------------------------- /fastapi/how to use: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedazharmbnr1/ClaudeMCPServer/36d34c1318153a09b050acd9c72117d18b48708f/fastapi/how to use -------------------------------------------------------------------------------- /fastapi/main.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syedazharmbnr1/ClaudeMCPServer/36d34c1318153a09b050acd9c72117d18b48708f/fastapi/main.py -------------------------------------------------------------------------------- /install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pip3 install pyautogui pillow 3 | -------------------------------------------------------------------------------- /mcp_screen_server copy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import json 4 | import logging 5 | from datetime import datetime 6 | from collections.abc import Sequence 7 | from typing import Any, Optional 8 | 9 | import pyautogui 10 | from PIL import Image 11 | from mcp.server import Server 12 | from mcp.types import ( 13 | Resource, 14 | Tool, 15 | TextContent, 16 | ImageContent, 17 | EmbeddedResource, 18 | LoggingLevel 19 | ) 20 | from pydantic import AnyUrl 21 | 22 | class MCPScreenServer: 23 | def __init__(self): 24 | self.buffer = "" 25 | self.save_dir = "/Users/azhar/Desktop/others/claudeMCP/dump" 26 | os.makedirs(self.save_dir, exist_ok=True) 27 | self.app = Server("screen-server") 28 | 29 | # Configure logging 30 | logging.basicConfig(level=logging.INFO) 31 | self.logger = logging.getLogger("screen-server") 32 | 33 | # Set up server handlers 34 | self.setup_handlers() 35 | 36 | def setup_handlers(self): 37 | @self.app.list_resources() 38 | async def list_resources() -> list[Resource]: 39 | uri = AnyUrl("screen://capture/current") 40 | return [ 41 | Resource( 42 | uri=uri, 43 | name="Current screen capture", 44 | mimeType="image/png", 45 | description="Real-time screen capture" 46 | ) 47 | ] 48 | 49 | @self.app.read_resource() 50 | async def read_resource(uri: AnyUrl) -> str: 51 | try: 52 | if not str(uri).startswith("screen://") or not str(uri).endswith("/current"): 53 | raise ValueError(f"Unknown resource: {uri}") 54 | 55 | screenshot = pyautogui.screenshot() 56 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 57 | filepath = os.path.join(self.save_dir, f"screen_capture_{timestamp}.png") 58 | screenshot.save(filepath) 59 | 60 | return json.dumps({ 61 | "timestamp": timestamp, 62 | "path": filepath, 63 | "size": { 64 | "width": screenshot.width, 65 | "height": screenshot.height 66 | } 67 | }, indent=2) 68 | except Exception as e: 69 | self.logger.error(f"Error reading resource: {str(e)}") 70 | raise 71 | 72 | @self.app.list_tools() 73 | async def list_tools() -> list[Tool]: 74 | return [ 75 | Tool( 76 | name="capture_screen", 77 | description="Capture current screen content", 78 | inputSchema={ 79 | "type": "object", 80 | "properties": { 81 | "save_path": { 82 | "type": "string", 83 | "description": "Optional custom save path" 84 | } 85 | } 86 | } 87 | ) 88 | ] 89 | 90 | @self.app.call_tool() 91 | async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]: 92 | if name != "capture_screen": 93 | raise ValueError(f"Unknown tool: {name}") 94 | 95 | try: 96 | screenshot = pyautogui.screenshot() 97 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 98 | save_path = arguments.get("save_path") if isinstance(arguments, dict) else None 99 | 100 | if not save_path: 101 | save_path = os.path.join(self.save_dir, f"screen_capture_{timestamp}.png") 102 | 103 | os.makedirs(os.path.dirname(save_path), exist_ok=True) 104 | screenshot.save(save_path) 105 | 106 | return [ 107 | TextContent( 108 | type="text", 109 | text=json.dumps({ 110 | "success": True, 111 | "timestamp": timestamp, 112 | "path": save_path, 113 | "size": { 114 | "width": screenshot.width, 115 | "height": screenshot.height 116 | } 117 | }, indent=2) 118 | ) 119 | ] 120 | except Exception as e: 121 | self.logger.error(f"Screen capture error: {str(e)}") 122 | return [ 123 | TextContent( 124 | type="text", 125 | text=json.dumps({ 126 | "success": False, 127 | "error": str(e) 128 | }, indent=2) 129 | ) 130 | ] 131 | 132 | async def run(self): 133 | """Main entry point for the server.""" 134 | from mcp.server.stdio import stdio_server 135 | self.logger.info("Starting MCP Screen Server") 136 | async with stdio_server() as (read_stream, write_stream): 137 | await self.app.run( 138 | read_stream, 139 | write_stream, 140 | self.app.create_initialization_options() 141 | ) 142 | 143 | async def main(): 144 | server = MCPScreenServer() 145 | await server.run() 146 | 147 | if __name__ == "__main__": 148 | import asyncio 149 | asyncio.run(main()) -------------------------------------------------------------------------------- /mcp_screen_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import json 4 | import logging 5 | from datetime import datetime 6 | from collections.abc import Sequence 7 | from typing import Any, Optional 8 | 9 | import pyautogui 10 | from PIL import Image 11 | from mcp.server import Server 12 | from mcp.types import ( 13 | Resource, 14 | Tool, 15 | TextContent, 16 | ImageContent, 17 | EmbeddedResource, 18 | LoggingLevel 19 | ) 20 | from pydantic import AnyUrl 21 | 22 | class MCPScreenServer: 23 | def __init__(self): 24 | self.buffer = "" 25 | self.save_dir = os.getenv('SCREEN_SAVE_DIR', "/Users/azhar/Desktop/others/claudeMCP/dump") 26 | os.makedirs(self.save_dir, exist_ok=True) 27 | self.app = Server("screen-server") 28 | 29 | # Configure logging 30 | logging.basicConfig(level=logging.INFO) 31 | self.logger = logging.getLogger("screen-server") 32 | 33 | # Set up server handlers 34 | self.setup_handlers() 35 | 36 | def setup_handlers(self): 37 | @self.app.list_resources() 38 | async def list_resources() -> list[Resource]: 39 | uri = AnyUrl("screen://capture/current") 40 | return [ 41 | Resource( 42 | uri=uri, 43 | name="Current screen capture", 44 | mimeType="image/webp", 45 | description="Real-time screen capture in WebP format" 46 | ) 47 | ] 48 | 49 | @self.app.read_resource() 50 | async def read_resource(uri: AnyUrl) -> str: 51 | try: 52 | if not str(uri).startswith("screen://") or not str(uri).endswith("/current"): 53 | raise ValueError(f"Unknown resource: {uri}") 54 | 55 | screenshot = pyautogui.screenshot() 56 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 57 | filepath = os.path.join(self.save_dir, f"screen_capture_{timestamp}.webp") 58 | 59 | # Save the screenshot with dynamic compression to ensure it's under 500KB 60 | self.save_compressed_image(screenshot, filepath, target_size_kb=500) 61 | 62 | return json.dumps({ 63 | "timestamp": timestamp, 64 | "path": filepath, 65 | "size": { 66 | "width": screenshot.width, 67 | "height": screenshot.height 68 | } 69 | }, indent=2) 70 | except Exception as e: 71 | self.logger.error(f"Error reading resource: {str(e)}", exc_info=True) 72 | raise ValueError("Failed to read resource.") 73 | 74 | @self.app.list_tools() 75 | async def list_tools() -> list[Tool]: 76 | return [ 77 | Tool( 78 | name="capture_screen", 79 | description="Capture current screen content", 80 | inputSchema={ 81 | "type": "object", 82 | "properties": { 83 | "save_path": { 84 | "type": "string", 85 | "description": "Optional custom save path" 86 | } 87 | } 88 | } 89 | ) 90 | ] 91 | 92 | @self.app.call_tool() 93 | async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]: 94 | if name != "capture_screen": 95 | raise ValueError(f"Unknown tool: {name}") 96 | 97 | try: 98 | screenshot = pyautogui.screenshot() 99 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 100 | save_path = arguments.get("save_path") if isinstance(arguments, dict) else None 101 | 102 | if not save_path: 103 | save_path = os.path.join(self.save_dir, f"screen_capture_{timestamp}.webp") 104 | else: 105 | # Ensure the file has a .webp extension 106 | if not save_path.lower().endswith('.webp'): 107 | save_path += '.webp' 108 | 109 | os.makedirs(os.path.dirname(save_path), exist_ok=True) 110 | 111 | # Save the screenshot with dynamic compression 112 | self.save_compressed_image(screenshot, save_path, target_size_kb=500) 113 | 114 | return [ 115 | TextContent( 116 | type="text", 117 | text=json.dumps({ 118 | "success": True, 119 | "timestamp": timestamp, 120 | "path": save_path, 121 | "size": { 122 | "width": screenshot.width, 123 | "height": screenshot.height 124 | } 125 | }, indent=2) 126 | ) 127 | ] 128 | except Exception as e: 129 | self.logger.error(f"Screen capture error: {str(e)}", exc_info=True) 130 | return [ 131 | TextContent( 132 | type="text", 133 | text=json.dumps({ 134 | "success": False, 135 | "error": str(e) 136 | }, indent=2) 137 | ) 138 | ] 139 | 140 | def save_compressed_image(self, image: Image.Image, filepath: str, target_size_kb: int = 500): 141 | """Save an image with dynamic compression to ensure it's under the target size.""" 142 | # Define initial parameters for binary search 143 | min_quality = 20 144 | max_quality = 95 145 | target_size = target_size_kb * 1024 # Convert KB to Bytes 146 | quality = max_quality 147 | iteration = 0 148 | max_iterations = 7 # Limit iterations to prevent infinite loops 149 | 150 | while iteration < max_iterations: 151 | # Save image with current quality 152 | image.save(filepath, format='WEBP', quality=quality, method=6, optimize=True) 153 | size = os.path.getsize(filepath) 154 | 155 | self.logger.debug(f"Iteration {iteration}: quality={quality}, size={size / 1024:.2f}KB") 156 | 157 | if size <= target_size: 158 | self.logger.info(f"Image saved at quality={quality}, size={size / 1024:.2f}KB") 159 | return 160 | else: 161 | # Decrease quality for next iteration 162 | max_quality = quality - 1 163 | quality = (min_quality + quality) // 2 # Binary search approach 164 | 165 | iteration += 1 166 | 167 | # If target size not achieved, start resizing 168 | self.logger.warning(f"Could not achieve target size with quality >= {min_quality}. Resizing image.") 169 | new_width = int(image.width * 0.9) 170 | new_height = int(image.height * 0.9) 171 | resized_image = image.resize((new_width, new_height), Image.ANTIALIAS) 172 | self.save_compressed_image(resized_image, filepath, target_size_kb) 173 | 174 | async def run(self): 175 | """Main entry point for the server.""" 176 | from mcp.server.stdio import stdio_server 177 | self.logger.info("Starting MCP Screen Server") 178 | async with stdio_server() as (read_stream, write_stream): 179 | await self.app.run( 180 | read_stream, 181 | write_stream, 182 | self.app.create_initialization_options() 183 | ) 184 | 185 | async def main(): 186 | server = MCPScreenServer() 187 | await server.run() 188 | 189 | if __name__ == "__main__": 190 | import asyncio 191 | asyncio.run(main()) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyautogui>=0.9.53 2 | pillow>=9.5.0 3 | pydantic>=2.0.0 4 | pytest>=7.0.0 5 | pytest-asyncio>=0.21.0 6 | python-dotenv>=1.0.0 7 | -------------------------------------------------------------------------------- /test_capture.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import pyautogui 3 | import os 4 | from datetime import datetime 5 | 6 | # Set up save directory 7 | save_dir = "/Users/azhar/Desktop/others/claudeMCP/dump" 8 | os.makedirs(save_dir, exist_ok=True) 9 | 10 | # Take screenshot 11 | screenshot = pyautogui.screenshot() 12 | 13 | # Generate filename with timestamp 14 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 15 | filename = f"screen_capture_{timestamp}.png" 16 | filepath = os.path.join(save_dir, filename) 17 | 18 | # Save screenshot 19 | screenshot.save(filepath) 20 | print(f"Screenshot saved to: {filepath}") -------------------------------------------------------------------------------- /test_screen_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import json 4 | import asyncio 5 | import pytest 6 | from datetime import datetime 7 | from unittest.mock import Mock, patch 8 | from mcp.types import ( 9 | Resource, 10 | Tool, 11 | TextContent, 12 | ImageContent, 13 | EmbeddedResource, 14 | LoggingLevel 15 | ) 16 | from pydantic import AnyUrl 17 | 18 | # Import our server 19 | from mcp_screen_server import MCPScreenServer 20 | 21 | async def test_mcp_server(): 22 | """Test MCP server initialization""" 23 | print("\nTesting MCP server initialization...") 24 | try: 25 | server = MCPScreenServer() 26 | assert server.save_dir == "/Users/azhar/Desktop/others/claudeMCP/dump" 27 | assert os.path.exists(server.save_dir) 28 | print("✓ MCP server initialization test passed") 29 | except Exception as e: 30 | print(f"✗ MCP server initialization test failed: {str(e)}") 31 | raise 32 | 33 | async def test_screen_capture(): 34 | """Test screen capture functionality""" 35 | print("\nTesting screen capture...") 36 | try: 37 | import pyautogui 38 | 39 | # Take screenshot 40 | screenshot = pyautogui.screenshot() 41 | 42 | # Save to test file 43 | test_file = "/Users/azhar/Desktop/others/claudeMCP/dump/test_capture.png" 44 | os.makedirs(os.path.dirname(test_file), exist_ok=True) 45 | screenshot.save(test_file) 46 | 47 | # Verify file exists and has content 48 | assert os.path.exists(test_file) 49 | assert os.path.getsize(test_file) > 0 50 | print(f"✓ Screen capture test passed - saved to {test_file}") 51 | 52 | # Test file info 53 | file_info = os.stat(test_file) 54 | print(f" - File size: {file_info.st_size} bytes") 55 | print(f" - Created at: {datetime.fromtimestamp(file_info.st_ctime)}") 56 | 57 | except Exception as e: 58 | print(f"✗ Screen capture test failed: {str(e)}") 59 | raise 60 | 61 | async def test_server_handlers(): 62 | """Test server handlers setup""" 63 | print("\nTesting server handlers...") 64 | try: 65 | server = MCPScreenServer() 66 | 67 | # Test handler registration by checking if methods exist 68 | test_uri = AnyUrl("screen://capture/current") 69 | test_args = {"save_path": "/test/path.png"} 70 | 71 | # Verify that the server has the basic MCP functionality 72 | assert hasattr(server.app, "run"), "Server missing run method" 73 | assert hasattr(server.app, "create_initialization_options"), "Server missing initialization options" 74 | 75 | print("✓ Server handlers test passed") 76 | print(" - Server has required MCP methods") 77 | print(" - Handler registration successful") 78 | 79 | except Exception as e: 80 | print(f"✗ Server handlers test failed: {str(e)}") 81 | raise 82 | 83 | async def test_save_directory(): 84 | """Test save directory setup and permissions""" 85 | print("\nTesting save directory...") 86 | try: 87 | save_dir = "/Users/azhar/Desktop/others/claudeMCP/dump" 88 | test_file = os.path.join(save_dir, "test_write_permission.txt") 89 | 90 | # Test directory exists 91 | assert os.path.exists(save_dir), "Save directory doesn't exist" 92 | 93 | # Test write permission 94 | try: 95 | with open(test_file, 'w') as f: 96 | f.write("test") 97 | os.remove(test_file) 98 | print("✓ Save directory test passed") 99 | print(f" - Directory exists: {save_dir}") 100 | print(" - Write permission verified") 101 | except Exception as e: 102 | raise Exception(f"Write permission test failed: {str(e)}") 103 | 104 | except Exception as e: 105 | print(f"✗ Save directory test failed: {str(e)}") 106 | raise 107 | 108 | async def main(): 109 | """Run all tests""" 110 | print("\nRunning MCP Screen Server Tests...") 111 | try: 112 | import pyautogui 113 | from PIL import Image 114 | except ImportError: 115 | print("Installing required packages...") 116 | os.system("pip install pyautogui pillow") 117 | import pyautogui 118 | from PIL import Image 119 | 120 | test_functions = [ 121 | test_mcp_server, 122 | test_screen_capture, 123 | test_server_handlers, 124 | test_save_directory 125 | ] 126 | 127 | failed_tests = 0 128 | for test in test_functions: 129 | try: 130 | await test() 131 | except Exception as e: 132 | print(f"\n❌ Test failed: {test.__name__}") 133 | print(f"Error: {str(e)}") 134 | failed_tests += 1 135 | continue 136 | 137 | if failed_tests == 0: 138 | print("\n✨ All tests completed successfully!") 139 | else: 140 | print(f"\n⚠️ {failed_tests} test(s) failed") 141 | 142 | if __name__ == "__main__": 143 | asyncio.run(main()) -------------------------------------------------------------------------------- /test_venv_capture.py: -------------------------------------------------------------------------------- 1 | #!/Users/azhar/Desktop/others/claudeMCP/mcp-server-py/.env/bin/python 2 | import sys 3 | import os 4 | import pyautogui 5 | from datetime import datetime 6 | 7 | def main(): 8 | print(f"Python executable: {sys.executable}") 9 | print(f"Python version: {sys.version}") 10 | 11 | # Set up save directory 12 | save_dir = "/Users/azhar/Desktop/others/claudeMCP/dump" 13 | os.makedirs(save_dir, exist_ok=True) 14 | 15 | # Take screenshot 16 | print("Taking screenshot...") 17 | screenshot = pyautogui.screenshot() 18 | 19 | # Generate filename with timestamp 20 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 21 | filename = f"screen_capture_{timestamp}.png" 22 | filepath = os.path.join(save_dir, filename) 23 | 24 | # Save screenshot 25 | screenshot.save(filepath) 26 | print(f"Screenshot saved to: {filepath}") 27 | 28 | if __name__ == "__main__": 29 | main() --------------------------------------------------------------------------------