winterm-mcp 0.1.3__py3-none-any.whl → 0.1.5__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.
- winterm_mcp/__init__.py +4 -1
- winterm_mcp/__main__.py +24 -1
- winterm_mcp/server.py +50 -6
- winterm_mcp/service.py +218 -6
- {winterm_mcp-0.1.3.dist-info → winterm_mcp-0.1.5.dist-info}/METADATA +47 -5
- winterm_mcp-0.1.5.dist-info/RECORD +10 -0
- winterm_mcp-0.1.3.dist-info/RECORD +0 -10
- {winterm_mcp-0.1.3.dist-info → winterm_mcp-0.1.5.dist-info}/WHEEL +0 -0
- {winterm_mcp-0.1.3.dist-info → winterm_mcp-0.1.5.dist-info}/entry_points.txt +0 -0
- {winterm_mcp-0.1.3.dist-info → winterm_mcp-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {winterm_mcp-0.1.3.dist-info → winterm_mcp-0.1.5.dist-info}/top_level.txt +0 -0
winterm_mcp/__init__.py
CHANGED
winterm_mcp/__main__.py
CHANGED
|
@@ -3,15 +3,38 @@ winterm-mcp 主入口
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from .server import app, init_service
|
|
6
|
-
from .service import RunCmdService
|
|
6
|
+
from .service import RunCmdService, setup_logging, __version__
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import tempfile
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def main():
|
|
10
13
|
"""
|
|
11
14
|
主函数,启动 MCP 服务器
|
|
12
15
|
"""
|
|
16
|
+
# 初始化日志
|
|
17
|
+
setup_logging(logging.INFO)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger("winterm-mcp")
|
|
20
|
+
|
|
21
|
+
# 获取日志文件路径并记录
|
|
22
|
+
log_file = os.environ.get("WINTERM_LOG_FILE") or os.path.join(
|
|
23
|
+
tempfile.gettempdir(), "winterm-mcp.log"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger.info("=" * 60)
|
|
27
|
+
logger.info(f"winterm-mcp v{__version__} starting...")
|
|
28
|
+
logger.info(f"Log file: {log_file}")
|
|
29
|
+
logger.info(f"Temp dir: {tempfile.gettempdir()}")
|
|
30
|
+
logger.info(f"Working dir: {os.getcwd()}")
|
|
31
|
+
logger.info(f"WINTERM_POWERSHELL_PATH: {os.environ.get('WINTERM_POWERSHELL_PATH', '(not set)')}")
|
|
32
|
+
logger.info("=" * 60)
|
|
33
|
+
|
|
13
34
|
service = RunCmdService()
|
|
14
35
|
init_service(service)
|
|
36
|
+
|
|
37
|
+
logger.info("Service initialized, starting MCP server...")
|
|
15
38
|
app.run()
|
|
16
39
|
|
|
17
40
|
|
winterm_mcp/server.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Annotated, Optional, Dict, Any
|
|
4
4
|
from mcp.server.fastmcp import FastMCP
|
|
5
|
-
from .service import RunCmdService
|
|
5
|
+
from .service import RunCmdService, get_version, setup_logging, __version__
|
|
6
6
|
from pydantic import Field
|
|
7
7
|
|
|
8
8
|
CommandStr = Annotated[
|
|
@@ -54,8 +54,7 @@ def init_service(service: RunCmdService) -> None:
|
|
|
54
54
|
def _svc() -> RunCmdService:
|
|
55
55
|
if _service is None:
|
|
56
56
|
raise RuntimeError(
|
|
57
|
-
"Service not initialized. "
|
|
58
|
-
"Call init_service() before running the server."
|
|
57
|
+
"Service not initialized. " "Call init_service() before running the server."
|
|
59
58
|
)
|
|
60
59
|
return _service
|
|
61
60
|
|
|
@@ -93,9 +92,7 @@ def run_command(
|
|
|
93
92
|
包含token和状态信息的字典
|
|
94
93
|
"""
|
|
95
94
|
try:
|
|
96
|
-
token = _svc().run_command(
|
|
97
|
-
command, shell_type, timeout, working_directory
|
|
98
|
-
)
|
|
95
|
+
token = _svc().run_command(command, shell_type, timeout, working_directory)
|
|
99
96
|
return {"token": token, "status": "pending", "message": "submitted"}
|
|
100
97
|
except Exception as e:
|
|
101
98
|
return {"error": str(e)}
|
|
@@ -127,3 +124,50 @@ def query_command_status(token: str) -> Dict[str, Any]:
|
|
|
127
124
|
return result
|
|
128
125
|
except Exception as e:
|
|
129
126
|
return {"error": str(e)}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.tool(
|
|
130
|
+
name="get_version",
|
|
131
|
+
description="获取 winterm-mcp 服务的版本信息和运行状态。",
|
|
132
|
+
annotations={
|
|
133
|
+
"title": "版本信息",
|
|
134
|
+
"readOnlyHint": True,
|
|
135
|
+
"destructiveHint": False,
|
|
136
|
+
"idempotentHint": True,
|
|
137
|
+
"openWorldHint": False,
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
def get_version_tool() -> Dict[str, Any]:
|
|
141
|
+
"""
|
|
142
|
+
获取 winterm-mcp 版本信息
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
包含版本号和服务状态的字典
|
|
146
|
+
"""
|
|
147
|
+
import os
|
|
148
|
+
import sys
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
# 尝试获取 PowerShell 路径信息
|
|
152
|
+
ps_path = None
|
|
153
|
+
ps_error = None
|
|
154
|
+
try:
|
|
155
|
+
from .service import _find_powershell
|
|
156
|
+
ps_path = _find_powershell()
|
|
157
|
+
except FileNotFoundError as e:
|
|
158
|
+
ps_error = str(e)
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
"version": get_version(),
|
|
162
|
+
"service_status": "running",
|
|
163
|
+
"python_version": sys.version,
|
|
164
|
+
"platform": sys.platform,
|
|
165
|
+
"powershell_path": ps_path,
|
|
166
|
+
"powershell_error": ps_error,
|
|
167
|
+
"env": {
|
|
168
|
+
"WINTERM_POWERSHELL_PATH": os.environ.get("WINTERM_POWERSHELL_PATH"),
|
|
169
|
+
"WINTERM_LOG_LEVEL": os.environ.get("WINTERM_LOG_LEVEL"),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
except Exception as e:
|
|
173
|
+
return {"error": str(e), "version": __version__}
|
winterm_mcp/service.py
CHANGED
|
@@ -6,8 +6,161 @@ import subprocess
|
|
|
6
6
|
import threading
|
|
7
7
|
import uuid
|
|
8
8
|
import time
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
import logging
|
|
9
12
|
from datetime import datetime
|
|
10
|
-
from typing import Dict, Optional, Any
|
|
13
|
+
from typing import Dict, Optional, Any, List
|
|
14
|
+
|
|
15
|
+
# 版本号
|
|
16
|
+
__version__ = "0.1.5"
|
|
17
|
+
|
|
18
|
+
# 配置日志
|
|
19
|
+
logger = logging.getLogger("winterm-mcp")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def setup_logging(level: int = logging.INFO) -> None:
|
|
23
|
+
"""
|
|
24
|
+
配置日志输出
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
level: 日志级别,默认 INFO
|
|
28
|
+
|
|
29
|
+
日志输出位置:
|
|
30
|
+
1. 控制台 (stderr)
|
|
31
|
+
2. 文件: %TEMP%/winterm-mcp.log 或 /tmp/winterm-mcp.log
|
|
32
|
+
|
|
33
|
+
可通过环境变量配置:
|
|
34
|
+
- WINTERM_LOG_LEVEL: 日志级别 (DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
|
35
|
+
- WINTERM_LOG_FILE: 自定义日志文件路径
|
|
36
|
+
"""
|
|
37
|
+
import tempfile
|
|
38
|
+
|
|
39
|
+
formatter = logging.Formatter(
|
|
40
|
+
"[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s",
|
|
41
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# 控制台输出
|
|
45
|
+
console_handler = logging.StreamHandler()
|
|
46
|
+
console_handler.setFormatter(formatter)
|
|
47
|
+
logger.addHandler(console_handler)
|
|
48
|
+
|
|
49
|
+
# 文件输出
|
|
50
|
+
log_file = os.environ.get("WINTERM_LOG_FILE")
|
|
51
|
+
if not log_file:
|
|
52
|
+
# 默认日志文件路径
|
|
53
|
+
log_file = os.path.join(tempfile.gettempdir(), "winterm-mcp.log")
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
57
|
+
file_handler.setFormatter(formatter)
|
|
58
|
+
logger.addHandler(file_handler)
|
|
59
|
+
logger.info(f"Log file: {log_file}")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"Failed to create log file {log_file}: {e}")
|
|
62
|
+
|
|
63
|
+
logger.setLevel(level)
|
|
64
|
+
|
|
65
|
+
# 检查环境变量设置日志级别
|
|
66
|
+
env_level = os.environ.get("WINTERM_LOG_LEVEL", "").upper()
|
|
67
|
+
if env_level in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
|
|
68
|
+
logger.setLevel(getattr(logging, env_level))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# PowerShell 可执行文件的标准路径(按优先级排序)
|
|
72
|
+
POWERSHELL_PATHS: List[str] = [
|
|
73
|
+
r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe",
|
|
74
|
+
r"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# PowerShell Core (pwsh) 的常见路径
|
|
78
|
+
PWSH_PATHS: List[str] = [
|
|
79
|
+
r"C:\Program Files\PowerShell\7\pwsh.exe",
|
|
80
|
+
r"C:\Program Files (x86)\PowerShell\7\pwsh.exe",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
# 环境变量名称
|
|
84
|
+
ENV_POWERSHELL_PATH = "WINTERM_POWERSHELL_PATH"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _find_powershell() -> str:
|
|
88
|
+
"""
|
|
89
|
+
查找可用的 PowerShell 可执行文件路径
|
|
90
|
+
|
|
91
|
+
查找顺序:
|
|
92
|
+
1. 环境变量 WINTERM_POWERSHELL_PATH(用户自定义)
|
|
93
|
+
2. Windows PowerShell 标准路径
|
|
94
|
+
3. PowerShell Core 标准路径
|
|
95
|
+
4. PATH 环境变量中的 powershell/pwsh(兼容正常环境)
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
PowerShell 可执行文件的绝对路径
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
FileNotFoundError: 如果找不到 PowerShell
|
|
102
|
+
"""
|
|
103
|
+
logger.debug("Starting PowerShell path discovery...")
|
|
104
|
+
|
|
105
|
+
# 1. 检查用户配置的环境变量
|
|
106
|
+
custom_path = os.environ.get(ENV_POWERSHELL_PATH)
|
|
107
|
+
if custom_path:
|
|
108
|
+
logger.debug(f"Found env var {ENV_POWERSHELL_PATH}={custom_path}")
|
|
109
|
+
if os.path.isfile(custom_path):
|
|
110
|
+
logger.info(f"Using custom PowerShell path: {custom_path}")
|
|
111
|
+
return custom_path
|
|
112
|
+
else:
|
|
113
|
+
logger.warning(
|
|
114
|
+
f"Custom PowerShell path not found: {custom_path}, "
|
|
115
|
+
"falling back to standard paths"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# 2. 检查 Windows PowerShell 标准路径
|
|
119
|
+
for path in POWERSHELL_PATHS:
|
|
120
|
+
logger.debug(f"Checking standard path: {path}")
|
|
121
|
+
if os.path.isfile(path):
|
|
122
|
+
logger.info(f"Found Windows PowerShell: {path}")
|
|
123
|
+
return path
|
|
124
|
+
|
|
125
|
+
# 3. 检查 PowerShell Core 标准路径
|
|
126
|
+
for path in PWSH_PATHS:
|
|
127
|
+
logger.debug(f"Checking PowerShell Core path: {path}")
|
|
128
|
+
if os.path.isfile(path):
|
|
129
|
+
logger.info(f"Found PowerShell Core: {path}")
|
|
130
|
+
return path
|
|
131
|
+
|
|
132
|
+
# 4. 尝试 PATH 环境变量(兼容正常环境)
|
|
133
|
+
logger.debug("Checking PATH environment variable...")
|
|
134
|
+
ps_path = shutil.which("powershell")
|
|
135
|
+
if ps_path:
|
|
136
|
+
logger.info(f"Found PowerShell in PATH: {ps_path}")
|
|
137
|
+
return ps_path
|
|
138
|
+
|
|
139
|
+
pwsh_path = shutil.which("pwsh")
|
|
140
|
+
if pwsh_path:
|
|
141
|
+
logger.info(f"Found pwsh in PATH: {pwsh_path}")
|
|
142
|
+
return pwsh_path
|
|
143
|
+
|
|
144
|
+
# 所有方法都失败
|
|
145
|
+
checked_paths = POWERSHELL_PATHS + PWSH_PATHS
|
|
146
|
+
error_msg = (
|
|
147
|
+
f"PowerShell not found. "
|
|
148
|
+
f"Set {ENV_POWERSHELL_PATH} environment variable or "
|
|
149
|
+
f"ensure PowerShell is installed. "
|
|
150
|
+
f"Checked paths: {', '.join(checked_paths)}"
|
|
151
|
+
)
|
|
152
|
+
logger.error(error_msg)
|
|
153
|
+
raise FileNotFoundError(error_msg)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_version() -> str:
|
|
157
|
+
"""
|
|
158
|
+
获取 winterm-mcp 版本号
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
版本号字符串
|
|
162
|
+
"""
|
|
163
|
+
return __version__
|
|
11
164
|
|
|
12
165
|
|
|
13
166
|
class RunCmdService:
|
|
@@ -18,6 +171,24 @@ class RunCmdService:
|
|
|
18
171
|
def __init__(self):
|
|
19
172
|
self.commands: Dict[str, Dict[str, Any]] = {}
|
|
20
173
|
self.lock = threading.Lock()
|
|
174
|
+
self._powershell_path: Optional[str] = None
|
|
175
|
+
|
|
176
|
+
def _get_powershell_path(self) -> str:
|
|
177
|
+
"""
|
|
178
|
+
获取 PowerShell 可执行文件路径(带缓存)
|
|
179
|
+
|
|
180
|
+
首次调用时查找并缓存路径,后续调用直接返回缓存值。
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
PowerShell 可执行文件的绝对路径
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
FileNotFoundError: 如果找不到 PowerShell
|
|
187
|
+
"""
|
|
188
|
+
if self._powershell_path is None:
|
|
189
|
+
self._powershell_path = _find_powershell()
|
|
190
|
+
logger.debug(f"PowerShell path cached: {self._powershell_path}")
|
|
191
|
+
return self._powershell_path
|
|
21
192
|
|
|
22
193
|
def run_command(
|
|
23
194
|
self,
|
|
@@ -39,6 +210,12 @@ class RunCmdService:
|
|
|
39
210
|
命令执行的token
|
|
40
211
|
"""
|
|
41
212
|
token = str(uuid.uuid4())
|
|
213
|
+
|
|
214
|
+
logger.info(
|
|
215
|
+
f"Submitting command: token={token}, shell={shell_type}, "
|
|
216
|
+
f"timeout={timeout}, cwd={working_directory}"
|
|
217
|
+
)
|
|
218
|
+
logger.debug(f"Command content: {command[:100]}{'...' if len(command) > 100 else ''}")
|
|
42
219
|
|
|
43
220
|
cmd_info = {
|
|
44
221
|
"token": token,
|
|
@@ -80,6 +257,7 @@ class RunCmdService:
|
|
|
80
257
|
"""
|
|
81
258
|
try:
|
|
82
259
|
start_time = time.time()
|
|
260
|
+
logger.debug(f"[{token}] Starting command execution...")
|
|
83
261
|
|
|
84
262
|
with self.lock:
|
|
85
263
|
if token in self.commands:
|
|
@@ -88,19 +266,25 @@ class RunCmdService:
|
|
|
88
266
|
encoding = "gbk"
|
|
89
267
|
|
|
90
268
|
if shell_type == "powershell":
|
|
91
|
-
#
|
|
92
|
-
|
|
269
|
+
# 使用绝对路径调用 PowerShell,避免 PATH 环境变量限制
|
|
270
|
+
ps_path = self._get_powershell_path()
|
|
271
|
+
logger.info(f"[{token}] Using PowerShell: {ps_path}")
|
|
93
272
|
cmd_args = [
|
|
94
|
-
|
|
273
|
+
ps_path,
|
|
95
274
|
"-NoProfile",
|
|
96
275
|
"-NoLogo",
|
|
97
276
|
"-NonInteractive",
|
|
98
|
-
"-ExecutionPolicy",
|
|
99
|
-
"
|
|
277
|
+
"-ExecutionPolicy",
|
|
278
|
+
"Bypass",
|
|
279
|
+
"-Command",
|
|
280
|
+
command,
|
|
100
281
|
]
|
|
101
282
|
else:
|
|
283
|
+
logger.debug(f"[{token}] Using cmd.exe")
|
|
102
284
|
cmd_args = ["cmd", "/c", command]
|
|
103
285
|
|
|
286
|
+
logger.debug(f"[{token}] Executing: {cmd_args}")
|
|
287
|
+
|
|
104
288
|
result = subprocess.run(
|
|
105
289
|
cmd_args,
|
|
106
290
|
capture_output=True,
|
|
@@ -112,6 +296,13 @@ class RunCmdService:
|
|
|
112
296
|
)
|
|
113
297
|
|
|
114
298
|
execution_time = time.time() - start_time
|
|
299
|
+
|
|
300
|
+
logger.info(
|
|
301
|
+
f"[{token}] Command completed: exit_code={result.returncode}, "
|
|
302
|
+
f"time={execution_time:.3f}s"
|
|
303
|
+
)
|
|
304
|
+
logger.debug(f"[{token}] stdout: {result.stdout[:200] if result.stdout else '(empty)'}")
|
|
305
|
+
logger.debug(f"[{token}] stderr: {result.stderr[:200] if result.stderr else '(empty)'}")
|
|
115
306
|
|
|
116
307
|
with self.lock:
|
|
117
308
|
if token in self.commands:
|
|
@@ -125,8 +316,24 @@ class RunCmdService:
|
|
|
125
316
|
}
|
|
126
317
|
)
|
|
127
318
|
|
|
319
|
+
except FileNotFoundError as e:
|
|
320
|
+
execution_time = time.time() - start_time
|
|
321
|
+
logger.error(f"[{token}] PowerShell not found: {e}")
|
|
322
|
+
with self.lock:
|
|
323
|
+
if token in self.commands:
|
|
324
|
+
self.commands[token].update(
|
|
325
|
+
{
|
|
326
|
+
"status": "completed",
|
|
327
|
+
"stdout": "",
|
|
328
|
+
"stderr": f"PowerShell not found: {e}",
|
|
329
|
+
"exit_code": -2,
|
|
330
|
+
"execution_time": execution_time,
|
|
331
|
+
"timeout_occurred": False,
|
|
332
|
+
}
|
|
333
|
+
)
|
|
128
334
|
except subprocess.TimeoutExpired:
|
|
129
335
|
execution_time = time.time() - start_time
|
|
336
|
+
logger.warning(f"[{token}] Command timed out after {timeout}s")
|
|
130
337
|
with self.lock:
|
|
131
338
|
if token in self.commands:
|
|
132
339
|
self.commands[token].update(
|
|
@@ -143,6 +350,7 @@ class RunCmdService:
|
|
|
143
350
|
)
|
|
144
351
|
except Exception as e:
|
|
145
352
|
execution_time = time.time() - start_time
|
|
353
|
+
logger.error(f"[{token}] Command failed with exception: {e}")
|
|
146
354
|
with self.lock:
|
|
147
355
|
if token in self.commands:
|
|
148
356
|
self.commands[token].update(
|
|
@@ -166,8 +374,11 @@ class RunCmdService:
|
|
|
166
374
|
Returns:
|
|
167
375
|
包含命令状态的字典
|
|
168
376
|
"""
|
|
377
|
+
logger.debug(f"Querying status for token: {token}")
|
|
378
|
+
|
|
169
379
|
with self.lock:
|
|
170
380
|
if token not in self.commands:
|
|
381
|
+
logger.warning(f"Token not found: {token}")
|
|
171
382
|
return {
|
|
172
383
|
"token": token,
|
|
173
384
|
"status": "not_found",
|
|
@@ -175,6 +386,7 @@ class RunCmdService:
|
|
|
175
386
|
}
|
|
176
387
|
|
|
177
388
|
cmd_info = self.commands[token].copy()
|
|
389
|
+
logger.debug(f"[{token}] Status: {cmd_info['status']}")
|
|
178
390
|
|
|
179
391
|
if cmd_info["status"] == "running":
|
|
180
392
|
return {"token": cmd_info["token"], "status": "running"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: winterm-mcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A Model Context Protocol (MCP) service for executing Windows terminal commands asynchronously
|
|
5
5
|
Author-email: winterm-mcp contributors <maintainer@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -27,8 +27,8 @@ Dynamic: license-file
|
|
|
27
27
|
|
|
28
28
|
# winterm-mcp
|
|
29
29
|
|
|
30
|
-
**更新日期**: 2026-01-16
|
|
31
|
-
**版本**: 0.1.
|
|
30
|
+
**更新日期**: 2026-01-16
|
|
31
|
+
**版本**: 0.1.4
|
|
32
32
|
|
|
33
33
|
Windows Terminal MCP Service - 专门支持 Windows 终端的异步命令执行工具。
|
|
34
34
|
|
|
@@ -58,8 +58,50 @@ pip install -e .
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
60
60
|
"mcpServers": {
|
|
61
|
-
"winterm
|
|
62
|
-
"command": "
|
|
61
|
+
"winterm": {
|
|
62
|
+
"command": "uvx",
|
|
63
|
+
"args": [
|
|
64
|
+
"winterm-mcp"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 环境配置
|
|
72
|
+
|
|
73
|
+
#### PowerShell 路径配置
|
|
74
|
+
|
|
75
|
+
在某些受限环境(如沙箱环境)中,系统 PATH 可能不包含 PowerShell 路径。winterm-mcp 会自动探测以下位置:
|
|
76
|
+
|
|
77
|
+
1. `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`
|
|
78
|
+
2. `C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe`
|
|
79
|
+
3. `C:\Program Files\PowerShell\7\pwsh.exe`(PowerShell Core)
|
|
80
|
+
4. `C:\Program Files (x86)\PowerShell\7\pwsh.exe`
|
|
81
|
+
|
|
82
|
+
如果 PowerShell 安装在非标准位置,可通过环境变量指定:
|
|
83
|
+
|
|
84
|
+
**方式一:系统环境变量**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Windows CMD
|
|
88
|
+
set WINTERM_POWERSHELL_PATH=D:\CustomPath\powershell.exe
|
|
89
|
+
|
|
90
|
+
# Windows PowerShell
|
|
91
|
+
$env:WINTERM_POWERSHELL_PATH = "D:\CustomPath\powershell.exe"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**方式二:MCP 配置**
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"winterm": {
|
|
100
|
+
"command": "uvx",
|
|
101
|
+
"args": ["winterm-mcp"],
|
|
102
|
+
"env": {
|
|
103
|
+
"WINTERM_POWERSHELL_PATH": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
|
104
|
+
}
|
|
63
105
|
}
|
|
64
106
|
}
|
|
65
107
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
winterm_mcp/__init__.py,sha256=SKtzW0lc0ixl5FUdEaJA6J7tryL-JYTvsPG8wUi5Dxk,222
|
|
2
|
+
winterm_mcp/__main__.py,sha256=5K6N5K-ooaAb1X17EmEQUid8zne-A457NAGCT33qGIg,1097
|
|
3
|
+
winterm_mcp/server.py,sha256=wKoCItWQBG4b2cODluWfk8ABjmXlDlFxVyK4tD7NBiY,4541
|
|
4
|
+
winterm_mcp/service.py,sha256=T7nlrKZDTqudScm-eEhDi72JRN82qxU3C9l_rOzMpOw,13213
|
|
5
|
+
winterm_mcp-0.1.5.dist-info/licenses/LICENSE,sha256=GPZ4VAbf_gxeFMSUHCmNQING30GK0kNduK8EJIO5-gc,1081
|
|
6
|
+
winterm_mcp-0.1.5.dist-info/METADATA,sha256=PCRDUAN9ZtgRcxl-9PkH44jDBxwP6IAiHKBBsHyCd5I,5106
|
|
7
|
+
winterm_mcp-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
winterm_mcp-0.1.5.dist-info/entry_points.txt,sha256=0OGioH1DKGxuxvSFD1OCRcbLoutfqdEPmJy_j2GhJTA,113
|
|
9
|
+
winterm_mcp-0.1.5.dist-info/top_level.txt,sha256=S7w96DR3MB-CMIuXLnrMOvs5ApqPF-2PsAcfVCphCHw,12
|
|
10
|
+
winterm_mcp-0.1.5.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
winterm_mcp/__init__.py,sha256=2Z5_ooE56FyBhidVPwkvcWPyl4ONfzi5ZRmmg2RvUm4,120
|
|
2
|
-
winterm_mcp/__main__.py,sha256=aCm888vzPOYtBDLtV02zAKkjIhrFAd2xB-TZAl4qHCc,300
|
|
3
|
-
winterm_mcp/server.py,sha256=JEZc6NJeOrMuZejV6aho1UINZEOTDWNor3YYEdzPWCI,3204
|
|
4
|
-
winterm_mcp/service.py,sha256=x0Ve8VS_ZodSNaZbDkO-pkFGui_6v_ooam_3WezcKxc,5995
|
|
5
|
-
winterm_mcp-0.1.3.dist-info/licenses/LICENSE,sha256=GPZ4VAbf_gxeFMSUHCmNQING30GK0kNduK8EJIO5-gc,1081
|
|
6
|
-
winterm_mcp-0.1.3.dist-info/METADATA,sha256=43DUZ9PYyPZXGJ4Qo8_0CiH8YwBNLH80NN7jjHDdcKg,4052
|
|
7
|
-
winterm_mcp-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
winterm_mcp-0.1.3.dist-info/entry_points.txt,sha256=0OGioH1DKGxuxvSFD1OCRcbLoutfqdEPmJy_j2GhJTA,113
|
|
9
|
-
winterm_mcp-0.1.3.dist-info/top_level.txt,sha256=S7w96DR3MB-CMIuXLnrMOvs5ApqPF-2PsAcfVCphCHw,12
|
|
10
|
-
winterm_mcp-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|