computer-control-mcp 0.2.6__tar.gz → 0.2.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computer-control-mcp
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Computer control capabilities using PyAutoGUI through a Model Context Protocol (MCP) server
5
5
  Project-URL: Homepage, https://github.com/AB498/computer-control-mcp
6
6
  Project-URL: Issues, https://github.com/AB498/computer-control-mcp/issues
@@ -31,6 +31,12 @@ Description-Content-Type: text/markdown
31
31
 
32
32
  MCP server that provides computer control capabilities, like mouse, keyboard, OCR, etc. using PyAutoGUI, RapidOCR, ONNXRuntime. With Zero External Dependencies.
33
33
 
34
+ * ***Only tested on Windows. Contributions are welcome.***
35
+
36
+ ## Example
37
+
38
+ ![MCP Computer Control Demo](demonstration.gif)
39
+
34
40
  ## Quick Usage (MCP Setup Using `uvx`)
35
41
 
36
42
  ```json
@@ -63,6 +69,23 @@ computer-control-mcp # instead of uvx computer-control-mcp, so you can use the l
63
69
  - Press keyboard keys
64
70
  - Drag and drop operations
65
71
 
72
+ ## Available Tools
73
+
74
+ ### Mouse Control
75
+ - `click_screen(x: int, y: int)`: Click at specified screen coordinates
76
+ - `move_mouse(x: int, y: int)`: Move mouse cursor to specified coordinates
77
+ - `drag_mouse(from_x: int, from_y: int, to_x: int, to_y: int, duration: float = 0.5)`: Drag mouse from one position to another
78
+
79
+ ### Keyboard Control
80
+ - `type_text(text: str)`: Type the specified text at current cursor position
81
+ - `press_key(key: str)`: Press a specified keyboard key
82
+
83
+ ### Screen and Window Management
84
+ - `take_screenshot(title_pattern: str = None, use_regex: bool = False, threshold: int = 60, with_ocr_text_and_coords: bool = False, scale_percent_for_ocr: int = 100, save_to_downloads: bool = False)`: Capture screen or window with optional OCR
85
+ - `get_screen_size()`: Get current screen resolution
86
+ - `list_windows()`: List all open windows
87
+ - `activate_window(title_pattern: str, use_regex: bool = False, threshold: int = 60)`: Bring specified window to foreground
88
+
66
89
  ### Running as a Module
67
90
 
68
91
  You can run the package as a module:
@@ -84,17 +107,6 @@ cd computer-control-mcp
84
107
  pip install -e .
85
108
  ```
86
109
 
87
- Or with uv:
88
-
89
- ```bash
90
- # Clone the repository
91
- git clone https://github.com/AB498/computer-control-mcp.git
92
- cd computer-control-mcp
93
-
94
- # Install in development mode
95
- uv pip install -e .
96
- ```
97
-
98
110
  ### Running Tests
99
111
 
100
112
  ```bash
@@ -2,6 +2,12 @@
2
2
 
3
3
  MCP server that provides computer control capabilities, like mouse, keyboard, OCR, etc. using PyAutoGUI, RapidOCR, ONNXRuntime. With Zero External Dependencies.
4
4
 
5
+ * ***Only tested on Windows. Contributions are welcome.***
6
+
7
+ ## Example
8
+
9
+ ![MCP Computer Control Demo](demonstration.gif)
10
+
5
11
  ## Quick Usage (MCP Setup Using `uvx`)
6
12
 
7
13
  ```json
@@ -34,6 +40,23 @@ computer-control-mcp # instead of uvx computer-control-mcp, so you can use the l
34
40
  - Press keyboard keys
35
41
  - Drag and drop operations
36
42
 
43
+ ## Available Tools
44
+
45
+ ### Mouse Control
46
+ - `click_screen(x: int, y: int)`: Click at specified screen coordinates
47
+ - `move_mouse(x: int, y: int)`: Move mouse cursor to specified coordinates
48
+ - `drag_mouse(from_x: int, from_y: int, to_x: int, to_y: int, duration: float = 0.5)`: Drag mouse from one position to another
49
+
50
+ ### Keyboard Control
51
+ - `type_text(text: str)`: Type the specified text at current cursor position
52
+ - `press_key(key: str)`: Press a specified keyboard key
53
+
54
+ ### Screen and Window Management
55
+ - `take_screenshot(title_pattern: str = None, use_regex: bool = False, threshold: int = 60, with_ocr_text_and_coords: bool = False, scale_percent_for_ocr: int = 100, save_to_downloads: bool = False)`: Capture screen or window with optional OCR
56
+ - `get_screen_size()`: Get current screen resolution
57
+ - `list_windows()`: List all open windows
58
+ - `activate_window(title_pattern: str, use_regex: bool = False, threshold: int = 60)`: Bring specified window to foreground
59
+
37
60
  ### Running as a Module
38
61
 
39
62
  You can run the package as a module:
@@ -55,17 +78,6 @@ cd computer-control-mcp
55
78
  pip install -e .
56
79
  ```
57
80
 
58
- Or with uv:
59
-
60
- ```bash
61
- # Clone the repository
62
- git clone https://github.com/AB498/computer-control-mcp.git
63
- cd computer-control-mcp
64
-
65
- # Install in development mode
66
- uv pip install -e .
67
- ```
68
-
69
81
  ### Running Tests
70
82
 
71
83
  ```bash
@@ -149,7 +149,7 @@ def _find_matching_window(
149
149
  @mcp.tool()
150
150
  def tool_version() -> str:
151
151
  """Get the version of the tool."""
152
- return "0.2.6"
152
+ return "0.2.7"
153
153
 
154
154
 
155
155
  @mcp.tool()
@@ -191,101 +191,24 @@ def take_screenshot(
191
191
  title_pattern: str = None,
192
192
  use_regex: bool = False,
193
193
  threshold: int = 60,
194
+ with_ocr_text_and_coords: bool = False,
195
+ scale_percent_for_ocr: int = 100,
194
196
  save_to_downloads: bool = False,
195
- ) -> Image:
197
+ ) -> Image | List[Tuple[List[List[int]], str, float]]:
196
198
  """
197
- Take screenshots based on the specified title pattern and save them to the downloads directory with absolute paths returned.
198
- If no title pattern is provided, take screenshot of entire screen.
199
+ Get screenshot and OCR text with absolute coordinates (returned after adding the window offset from true (0, 0) of screen to the OCR coordinates, so clicking is on-point. Recommended to click in the middle of OCR Box) and confidence from window with the specified title pattern.
200
+ If no title pattern is provided, get screenshot of entire screen and all text on the screen.
199
201
 
200
202
  Args:
201
203
  title_pattern: Pattern to match window title, if None, take screenshot of entire screen
202
204
  use_regex: If True, treat the pattern as a regex, otherwise best match with fuzzy matching
203
- save_to_downloads: If True, save the screenshot to the downloads directory and return the absolute path
204
205
  threshold: Minimum score (0-100) required for a fuzzy match
206
+ with_ocr_text_and_coords: If True, get OCR text with absolute coordinates from the screenshot
207
+ scale_percent_for_ocr: Percentage to scale the image down before processing, you wont need this most of the time unless your pc is extremely old or slow
208
+ save_to_downloads: If True, save the screenshot to the downloads directory and return the absolute path
205
209
 
206
210
  Returns:
207
- Always returns a single screenshot as MCP Image object, content type image not supported means preview isnt supported but Image object is there.
208
- """
209
- try:
210
- all_windows = gw.getAllWindows()
211
-
212
- # Convert to list of dictionaries for _find_matching_window
213
- windows = []
214
- for window in all_windows:
215
- if window.title: # Only include windows with titles
216
- windows.append(
217
- {
218
- "title": window.title,
219
- "window_obj": window, # Store the actual window object
220
- }
221
- )
222
-
223
- log(f"Found {len(windows)} windows")
224
- window = _find_matching_window(windows, title_pattern, use_regex, threshold)
225
- window = window["window_obj"] if window else None
226
-
227
- # Store the currently active window
228
- current_active_window = gw.getActiveWindow()
229
-
230
- # Take the screenshot
231
- if not window:
232
- log("No matching window found, taking screenshot of entire screen")
233
- screenshot = pyautogui.screenshot()
234
- else:
235
- log(f"Taking screenshot of window: {window.title}")
236
- # Activate the window and wait for it to be fully in focus
237
- window.activate()
238
- pyautogui.sleep(0.5) # Wait for 0.5 seconds to ensure window is active
239
- screenshot = pyautogui.screenshot(
240
- region=(window.left, window.top, window.width, window.height)
241
- )
242
- # Restore the previously active window
243
- if current_active_window:
244
- current_active_window.activate()
245
- pyautogui.sleep(0.2) # Wait a bit to ensure previous window is restored
246
-
247
- # Create temp directory
248
- temp_dir = Path(tempfile.mkdtemp())
249
-
250
- # Save screenshot and get filepath
251
- filepath, _ = save_image_to_downloads(
252
- screenshot, prefix="screenshot", directory=temp_dir
253
- )
254
-
255
- # Create Image object from filepath
256
- image = Image(filepath)
257
-
258
- # Copy from temp to downloads
259
- if save_to_downloads:
260
- log("Copying screenshot from temp to downloads")
261
- shutil.copy(filepath, get_downloads_dir())
262
-
263
- return image # MCP Image object
264
-
265
- except Exception as e:
266
- log(f"Error taking screenshot: {str(e)}")
267
- return f"Error taking screenshot: {str(e)}"
268
-
269
-
270
- @mcp.tool()
271
- def get_ocr_from_screenshot(
272
- title_pattern: str = None,
273
- use_regex: bool = False,
274
- threshold: int = 60,
275
- scale_percent: int = 100,
276
- ) -> List[Tuple[List[List[int]], str, float]]:
277
- """
278
- Get OCR text with absolute coordinates (returned after adding the window offset from true (0, 0) of screen to the OCR coordinates, so clicking is on-point. Recommended to click in the middle of OCR Box) and confidence from window with the specified title pattern.
279
- If no title pattern is provided, get all text on the screen.
280
-
281
- Args:
282
- title_pattern: Pattern to match window title, if None, get all UI elements on the screen
283
- use_regex: If True, treat the pattern as a regex, otherwise best match with fuzzy matching
284
- threshold: Minimum score (0-100) required for a fuzzy match
285
- scale_percent: Percentage to scale the image down before processing, you wont need this most of the time unless your pc is extremely old or slow
286
-
287
- Returns:
288
- List of UI elements as [[4 corners of box], text, confidence]
211
+ Returns a single screenshot as MCP Image object, if with_ocr_text_and_coords is True, returns a MCP Image object followed by list of UI elements as [[4 corners of box], text, confidence], "content type image not supported" means preview isnt supported but Image object is there.
289
212
  """
290
213
  try:
291
214
 
@@ -307,13 +230,13 @@ def get_ocr_from_screenshot(
307
230
  window = window["window_obj"] if window else None
308
231
 
309
232
  # Store the currently active window
310
- current_active_window = gw.getActiveWindow()
311
233
 
312
234
  # Take the screenshot
313
235
  if not window:
314
236
  log("No matching window found, taking screenshot of entire screen")
315
237
  screenshot = pyautogui.screenshot()
316
238
  else:
239
+ current_active_window = gw.getActiveWindow()
317
240
  log(f"Taking screenshot of window: {window.title}")
318
241
  # Activate the window and wait for it to be fully in focus
319
242
  window.activate()
@@ -323,8 +246,11 @@ def get_ocr_from_screenshot(
323
246
  )
324
247
  # Restore the previously active window
325
248
  if current_active_window:
326
- current_active_window.activate()
327
- pyautogui.sleep(0.2) # Wait a bit to ensure previous window is restored
249
+ try:
250
+ current_active_window.activate()
251
+ pyautogui.sleep(0.2) # Wait a bit to ensure previous window is restored
252
+ except Exception as e:
253
+ log(f"Error restoring previous window: {str(e)}")
328
254
 
329
255
  # Create temp directory
330
256
  temp_dir = Path(tempfile.mkdtemp())
@@ -337,8 +263,11 @@ def get_ocr_from_screenshot(
337
263
  # Create Image object from filepath
338
264
  image = Image(filepath)
339
265
 
266
+ if not with_ocr_text_and_coords:
267
+ return image # MCP Image object
268
+
340
269
  # Copy from temp to downloads
341
- if False:
270
+ if save_to_downloads:
342
271
  log("Copying screenshot from temp to downloads")
343
272
  shutil.copy(filepath, get_downloads_dir())
344
273
 
@@ -346,8 +275,8 @@ def get_ocr_from_screenshot(
346
275
  img = cv2.imread(image_path)
347
276
 
348
277
  # Lower down resolution before processing
349
- width = int(img.shape[1] * scale_percent / 100)
350
- height = int(img.shape[0] * scale_percent / 100)
278
+ width = int(img.shape[1] * scale_percent_for_ocr / 100)
279
+ height = int(img.shape[0] * scale_percent_for_ocr / 100)
351
280
  dim = (width, height)
352
281
  resized_img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
353
282
  # save resized image to pwd
@@ -357,18 +286,21 @@ def get_ocr_from_screenshot(
357
286
 
358
287
  result, elapse_list = engine(resized_img)
359
288
  boxes, txts, scores = list(zip(*result))
360
- boxes = [[[x + window.left, y + window.top] for x, y in box] for box in boxes]
289
+ boxes = [
290
+ [[x + window.left, y + window.top] if window else [x, y] for x, y in box]
291
+ for box in boxes
292
+ ]
361
293
  zipped_results = list(zip(boxes, txts, scores))
362
294
 
363
- return zipped_results
295
+ return [image, *zipped_results]
364
296
 
365
297
  except Exception as e:
366
- log(f"Error getting UI elements: {str(e)}")
298
+ log(f"Error in screenshot or getting UI elements: {str(e)}")
367
299
  import traceback
368
300
 
369
301
  stack_trace = traceback.format_exc()
370
302
  log(f"Stack trace:\n{stack_trace}")
371
- return f"Error getting UI elements: {str(e)}\nStack trace:\n{stack_trace}"
303
+ return f"Error in screenshot or getting UI elements: {str(e)}\nStack trace:\n{stack_trace}"
372
304
 
373
305
 
374
306
  @mcp.tool()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "computer-control-mcp"
7
- version = "0.2.6"
7
+ version = "0.2.7"
8
8
  description = "Computer control capabilities using PyAutoGUI through a Model Context Protocol (MCP) server"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"