windows-mcp 0.5.6__py3-none-any.whl → 0.5.7__py3-none-any.whl
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.
- windows_mcp/__main__.py +12 -12
- windows_mcp/analytics.py +23 -2
- windows_mcp/desktop/service.py +1 -1
- {windows_mcp-0.5.6.dist-info → windows_mcp-0.5.7.dist-info}/METADATA +3 -3
- {windows_mcp-0.5.6.dist-info → windows_mcp-0.5.7.dist-info}/RECORD +8 -8
- {windows_mcp-0.5.6.dist-info → windows_mcp-0.5.7.dist-info}/WHEEL +0 -0
- {windows_mcp-0.5.6.dist-info → windows_mcp-0.5.7.dist-info}/entry_points.txt +0 -0
- {windows_mcp-0.5.6.dist-info → windows_mcp-0.5.7.dist-info}/licenses/LICENSE.md +0 -0
windows_mcp/__main__.py
CHANGED
|
@@ -6,9 +6,9 @@ from fastmcp.utilities.types import Image
|
|
|
6
6
|
from mcp.types import ToolAnnotations
|
|
7
7
|
from typing import Literal, Optional
|
|
8
8
|
from humancursor import SystemCursor
|
|
9
|
+
from fastmcp import FastMCP, Context
|
|
9
10
|
from dotenv import load_dotenv
|
|
10
11
|
from textwrap import dedent
|
|
11
|
-
from fastmcp import FastMCP
|
|
12
12
|
import pyautogui as pg
|
|
13
13
|
import asyncio
|
|
14
14
|
import click
|
|
@@ -63,7 +63,7 @@ mcp=FastMCP(name='windows-mcp',instructions=instructions,lifespan=lifespan)
|
|
|
63
63
|
)
|
|
64
64
|
)
|
|
65
65
|
@with_analytics(analytics, "App-Tool")
|
|
66
|
-
def app_tool(mode:Literal['launch','resize','switch'],name:str|None=None,window_loc:list[int]|None=None,window_size:list[int]|None=None):
|
|
66
|
+
def app_tool(mode:Literal['launch','resize','switch'],name:str|None=None,window_loc:list[int]|None=None,window_size:list[int]|None=None, ctx: Context = None):
|
|
67
67
|
return desktop.app(mode,name,window_loc,window_size)
|
|
68
68
|
|
|
69
69
|
@mcp.tool(
|
|
@@ -78,7 +78,7 @@ def app_tool(mode:Literal['launch','resize','switch'],name:str|None=None,window_
|
|
|
78
78
|
)
|
|
79
79
|
)
|
|
80
80
|
@with_analytics(analytics, "Powershell-Tool")
|
|
81
|
-
def powershell_tool(command: str) -> str:
|
|
81
|
+
def powershell_tool(command: str, ctx: Context = None) -> str:
|
|
82
82
|
response,status_code=desktop.execute_command(command)
|
|
83
83
|
return f'Response: {response}\nStatus Code: {status_code}'
|
|
84
84
|
|
|
@@ -94,7 +94,7 @@ def powershell_tool(command: str) -> str:
|
|
|
94
94
|
)
|
|
95
95
|
)
|
|
96
96
|
@with_analytics(analytics, "State-Tool")
|
|
97
|
-
def state_tool(use_vision:bool=False,use_dom:bool=False):
|
|
97
|
+
def state_tool(use_vision:bool=False,use_dom:bool=False, ctx: Context = None):
|
|
98
98
|
# Calculate scale factor to cap resolution at 1080p (1920x1080)
|
|
99
99
|
max_width, max_height = 1920, 1080
|
|
100
100
|
scale_width = max_width / screen_width if screen_width > max_width else 1.0
|
|
@@ -135,7 +135,7 @@ def state_tool(use_vision:bool=False,use_dom:bool=False):
|
|
|
135
135
|
)
|
|
136
136
|
)
|
|
137
137
|
@with_analytics(analytics, "Click-Tool")
|
|
138
|
-
def click_tool(loc:list[int],button:Literal['left','right','middle']='left',clicks:int=1)->str:
|
|
138
|
+
def click_tool(loc:list[int],button:Literal['left','right','middle']='left',clicks:int=1, ctx: Context = None)->str:
|
|
139
139
|
if len(loc) != 2:
|
|
140
140
|
raise ValueError("Location must be a list of exactly 2 integers [x, y]")
|
|
141
141
|
x,y=loc[0],loc[1]
|
|
@@ -155,7 +155,7 @@ def click_tool(loc:list[int],button:Literal['left','right','middle']='left',clic
|
|
|
155
155
|
)
|
|
156
156
|
)
|
|
157
157
|
@with_analytics(analytics, "Type-Tool")
|
|
158
|
-
def type_tool(loc:list[int],text:str,clear:bool=False,press_enter:bool=False)->str:
|
|
158
|
+
def type_tool(loc:list[int],text:str,clear:bool=False,press_enter:bool=False, ctx: Context = None)->str:
|
|
159
159
|
if len(loc) != 2:
|
|
160
160
|
raise ValueError("Location must be a list of exactly 2 integers [x, y]")
|
|
161
161
|
x,y=loc[0],loc[1]
|
|
@@ -174,7 +174,7 @@ def type_tool(loc:list[int],text:str,clear:bool=False,press_enter:bool=False)->s
|
|
|
174
174
|
)
|
|
175
175
|
)
|
|
176
176
|
@with_analytics(analytics, "Scroll-Tool")
|
|
177
|
-
def scroll_tool(loc:list[int]=None,type:Literal['horizontal','vertical']='vertical',direction:Literal['up','down','left','right']='down',wheel_times:int=1)->str:
|
|
177
|
+
def scroll_tool(loc:list[int]=None,type:Literal['horizontal','vertical']='vertical',direction:Literal['up','down','left','right']='down',wheel_times:int=1, ctx: Context = None)->str:
|
|
178
178
|
if loc and len(loc) != 2:
|
|
179
179
|
raise ValueError("Location must be a list of exactly 2 integers [x, y]")
|
|
180
180
|
response=desktop.scroll(loc,type,direction,wheel_times)
|
|
@@ -194,7 +194,7 @@ def scroll_tool(loc:list[int]=None,type:Literal['horizontal','vertical']='vertic
|
|
|
194
194
|
)
|
|
195
195
|
)
|
|
196
196
|
@with_analytics(analytics, "Drag-Tool")
|
|
197
|
-
def drag_tool(to_loc:list[int])->str:
|
|
197
|
+
def drag_tool(to_loc:list[int], ctx: Context = None)->str:
|
|
198
198
|
if len(to_loc) != 2:
|
|
199
199
|
raise ValueError("to_loc must be a list of exactly 2 integers [x, y]")
|
|
200
200
|
desktop.drag(to_loc)
|
|
@@ -213,7 +213,7 @@ def drag_tool(to_loc:list[int])->str:
|
|
|
213
213
|
)
|
|
214
214
|
)
|
|
215
215
|
@with_analytics(analytics, "Move-Tool")
|
|
216
|
-
def move_tool(to_loc:list[int])->str:
|
|
216
|
+
def move_tool(to_loc:list[int], ctx: Context = None)->str:
|
|
217
217
|
if len(to_loc) != 2:
|
|
218
218
|
raise ValueError("to_loc must be a list of exactly 2 integers [x, y]")
|
|
219
219
|
x,y=to_loc[0],to_loc[1]
|
|
@@ -232,7 +232,7 @@ def move_tool(to_loc:list[int])->str:
|
|
|
232
232
|
)
|
|
233
233
|
)
|
|
234
234
|
@with_analytics(analytics, "Shortcut-Tool")
|
|
235
|
-
def shortcut_tool(shortcut:str):
|
|
235
|
+
def shortcut_tool(shortcut:str, ctx: Context = None):
|
|
236
236
|
desktop.shortcut(shortcut)
|
|
237
237
|
return f"Pressed {shortcut}."
|
|
238
238
|
|
|
@@ -248,7 +248,7 @@ def shortcut_tool(shortcut:str):
|
|
|
248
248
|
)
|
|
249
249
|
)
|
|
250
250
|
@with_analytics(analytics, "Wait-Tool")
|
|
251
|
-
def wait_tool(duration:int)->str:
|
|
251
|
+
def wait_tool(duration:int, ctx: Context = None)->str:
|
|
252
252
|
pg.sleep(duration)
|
|
253
253
|
return f'Waited for {duration} seconds.'
|
|
254
254
|
|
|
@@ -264,7 +264,7 @@ def wait_tool(duration:int)->str:
|
|
|
264
264
|
)
|
|
265
265
|
)
|
|
266
266
|
@with_analytics(analytics, "Scrape-Tool")
|
|
267
|
-
def scrape_tool(url:str,use_dom:bool=False)->str:
|
|
267
|
+
def scrape_tool(url:str,use_dom:bool=False, ctx: Context = None)->str:
|
|
268
268
|
if not use_dom:
|
|
269
269
|
content=desktop.scrape(url)
|
|
270
270
|
return f'URL:{url}\nContent:\n{content}'
|
windows_mcp/analytics.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Optional, Dict, Any, TypeVar, Callable, Protocol, Awaitable
|
|
2
2
|
from tempfile import TemporaryDirectory
|
|
3
3
|
from uuid_extensions import uuid7str
|
|
4
|
+
from fastmcp import Context
|
|
4
5
|
from functools import wraps
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
import posthog
|
|
@@ -125,6 +126,21 @@ def with_analytics(analytics_instance: Optional[Analytics], tool_name: str):
|
|
|
125
126
|
@wraps(func)
|
|
126
127
|
async def wrapper(*args, **kwargs) -> T:
|
|
127
128
|
start = time.time()
|
|
129
|
+
|
|
130
|
+
# Capture client info from Context passed as argument
|
|
131
|
+
client_data = {}
|
|
132
|
+
try:
|
|
133
|
+
ctx = next((arg for arg in args if isinstance(arg, Context)), None)
|
|
134
|
+
if not ctx:
|
|
135
|
+
ctx = next((val for val in kwargs.values() if isinstance(val, Context)), None)
|
|
136
|
+
|
|
137
|
+
if ctx and ctx.session and ctx.session.client_params and ctx.session.client_params.clientInfo:
|
|
138
|
+
info = ctx.session.client_params.clientInfo
|
|
139
|
+
client_data["client_name"] = info.name
|
|
140
|
+
client_data["client_version"] = info.version
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
128
144
|
try:
|
|
129
145
|
if asyncio.iscoroutinefunction(func):
|
|
130
146
|
result = await func(*args, **kwargs)
|
|
@@ -135,7 +151,11 @@ def with_analytics(analytics_instance: Optional[Analytics], tool_name: str):
|
|
|
135
151
|
duration_ms = int((time.time() - start) * 1000)
|
|
136
152
|
|
|
137
153
|
if analytics_instance:
|
|
138
|
-
await analytics_instance.track_tool(tool_name, {
|
|
154
|
+
await analytics_instance.track_tool(tool_name, {
|
|
155
|
+
"duration_ms": duration_ms,
|
|
156
|
+
"success": True,
|
|
157
|
+
**client_data
|
|
158
|
+
})
|
|
139
159
|
|
|
140
160
|
return result
|
|
141
161
|
except Exception as error:
|
|
@@ -143,7 +163,8 @@ def with_analytics(analytics_instance: Optional[Analytics], tool_name: str):
|
|
|
143
163
|
if analytics_instance:
|
|
144
164
|
await analytics_instance.track_error(error, {
|
|
145
165
|
"tool_name": tool_name,
|
|
146
|
-
"duration_ms": duration_ms
|
|
166
|
+
"duration_ms": duration_ms,
|
|
167
|
+
**client_data
|
|
147
168
|
})
|
|
148
169
|
raise error
|
|
149
170
|
return wrapper
|
windows_mcp/desktop/service.py
CHANGED
|
@@ -53,7 +53,7 @@ class Desktop:
|
|
|
53
53
|
sleep(0.1)
|
|
54
54
|
apps=self.get_apps()
|
|
55
55
|
active_app=self.get_active_app()
|
|
56
|
-
if active_app is not None:
|
|
56
|
+
if active_app is not None and active_app in apps:
|
|
57
57
|
apps.remove(active_app)
|
|
58
58
|
logger.debug(f"Active app: {active_app}")
|
|
59
59
|
logger.debug(f"Apps: {apps}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: windows-mcp
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Lightweight MCP Server for interacting with Windows Operating System.
|
|
5
5
|
Project-URL: homepage, https://github.com/CursorTouch
|
|
6
6
|
Author-email: Jeomon George <jeogeoalukka@gmail.com>
|
|
@@ -33,7 +33,7 @@ Requires-Dist: fastmcp>=2.8.1
|
|
|
33
33
|
Requires-Dist: fuzzywuzzy>=0.18.0
|
|
34
34
|
Requires-Dist: humancursor>=1.1.5
|
|
35
35
|
Requires-Dist: ipykernel>=6.30.0
|
|
36
|
-
Requires-Dist: live-inspect>=0.1.
|
|
36
|
+
Requires-Dist: live-inspect>=0.1.2
|
|
37
37
|
Requires-Dist: markdownify>=1.1.0
|
|
38
38
|
Requires-Dist: pdfplumber>=0.11.7
|
|
39
39
|
Requires-Dist: pillow>=11.2.1
|
|
@@ -387,7 +387,7 @@ Please read our [Security Policy](SECURITY.md).
|
|
|
387
387
|
|
|
388
388
|
## 📊 Telemetry
|
|
389
389
|
|
|
390
|
-
Windows-MCP collects
|
|
390
|
+
Windows-MCP collects usage data to help improve the MCP server. No personal information, no tool arguments, no outputs are tracked.
|
|
391
391
|
|
|
392
392
|
To disable telemetry, add the following to your MCP client configuration:
|
|
393
393
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
windows_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
windows_mcp/__main__.py,sha256=
|
|
3
|
-
windows_mcp/analytics.py,sha256=
|
|
2
|
+
windows_mcp/__main__.py,sha256=TryfZevJMW53N3m8qcQXDqhcYu2KAGCbZ6ibrJjfS2o,13280
|
|
3
|
+
windows_mcp/analytics.py,sha256=kXQ2MEUaUnZUvq0nfm03YIN0kHcSq2z2dNFcFirO5Kc,6455
|
|
4
4
|
windows_mcp/desktop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
windows_mcp/desktop/config.py,sha256=7rAb64pmC275PpNRXVOyOf0Psu089AOosRC8T5kVGWA,384
|
|
6
|
-
windows_mcp/desktop/service.py,sha256=
|
|
6
|
+
windows_mcp/desktop/service.py,sha256=gJO46Hs508tJrGKOaaFkx9SxtPhEucboI3eXeI3uro0,18486
|
|
7
7
|
windows_mcp/desktop/views.py,sha256=_hZ5sfY1uWVi5mpaysVd-plwP_DT6SXpKa33Z8WT6gI,1523
|
|
8
8
|
windows_mcp/tree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
windows_mcp/tree/config.py,sha256=k-Mjo_yIn0d1AzcEW_bxiaXyBFxBZZSyy7hCNQ3XVp0,1010
|
|
10
10
|
windows_mcp/tree/service.py,sha256=evK62AwhMwifpq6lRQCdrmC4DPt1-w_HSp8nUwXsCVQ,23566
|
|
11
11
|
windows_mcp/tree/utils.py,sha256=6hbxdIQPrAY-I3jcHsRqodHlxboTQj2GnLA71bf1lqY,911
|
|
12
12
|
windows_mcp/tree/views.py,sha256=K2hTBDicjP4p_tPIRTLZ8Sq3pGYhsDtZVIROAnMGTz4,3599
|
|
13
|
-
windows_mcp-0.5.
|
|
14
|
-
windows_mcp-0.5.
|
|
15
|
-
windows_mcp-0.5.
|
|
16
|
-
windows_mcp-0.5.
|
|
17
|
-
windows_mcp-0.5.
|
|
13
|
+
windows_mcp-0.5.7.dist-info/METADATA,sha256=xbvl6qR69-KSMLqjga6gutc8goRD4IQ7jm_zyKPT2Ko,14019
|
|
14
|
+
windows_mcp-0.5.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
+
windows_mcp-0.5.7.dist-info/entry_points.txt,sha256=wW8NcVQ_OJK5e5GemZSE_nOKyxfUtBPq2acFLszRwaw,58
|
|
16
|
+
windows_mcp-0.5.7.dist-info/licenses/LICENSE.md,sha256=U1UM4Xi_IX-jHnHjGT0rETNia-Ck8gd92iSQMqQ6a8Y,1089
|
|
17
|
+
windows_mcp-0.5.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|