winterm-mcp 0.1.6__py3-none-any.whl → 0.1.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.
winterm_mcp/__init__.py CHANGED
@@ -1,9 +1,32 @@
1
- """
2
- winterm-mcp - Windows Terminal MCP Service
3
- """
4
-
5
- from .service import __version__, get_version, setup_logging
6
-
7
- __author__ = "winterm-mcp contributors"
8
-
9
- __all__ = ["__version__", "get_version", "setup_logging"]
1
+ """
2
+ winterm-mcp - Windows Terminal MCP Service
3
+ """
4
+
5
+ from .service import __version__, get_version, setup_logging, CommandService
6
+ from .models import CommandInfo, QueryStatusResponse, VersionInfo, RunCommandParams
7
+ from .store import CommandStore
8
+ from .utils import find_powershell, find_cmd, resolve_executable_path, strip_ansi_codes
9
+ from .constants import NAME, VERSION, ENV_POWERSHELL_PATH, ENV_CMD_PATH, ENV_PYTHON_PATH
10
+
11
+ __author__ = "winterm-mcp contributors"
12
+
13
+ __all__ = [
14
+ "__version__",
15
+ "get_version",
16
+ "setup_logging",
17
+ "CommandService",
18
+ "CommandInfo",
19
+ "QueryStatusResponse",
20
+ "VersionInfo",
21
+ "RunCommandParams",
22
+ "CommandStore",
23
+ "find_powershell",
24
+ "find_cmd",
25
+ "resolve_executable_path",
26
+ "strip_ansi_codes",
27
+ "NAME",
28
+ "VERSION",
29
+ "ENV_POWERSHELL_PATH",
30
+ "ENV_CMD_PATH",
31
+ "ENV_PYTHON_PATH",
32
+ ]
winterm_mcp/__main__.py CHANGED
@@ -3,7 +3,7 @@ winterm-mcp 主入口
3
3
  """
4
4
 
5
5
  from .server import app, init_service
6
- from .service import RunCmdService, setup_logging, __version__
6
+ from .service import CommandService, setup_logging, __version__
7
7
  import logging
8
8
  import os
9
9
  import tempfile
@@ -13,12 +13,10 @@ def main():
13
13
  """
14
14
  主函数,启动 MCP 服务器
15
15
  """
16
- # 初始化日志
17
16
  setup_logging(logging.INFO)
18
17
 
19
18
  logger = logging.getLogger("winterm-mcp")
20
19
 
21
- # 获取日志文件路径并记录
22
20
  log_file = os.environ.get("WINTERM_LOG_FILE") or os.path.join(
23
21
  tempfile.gettempdir(), "winterm-mcp.log"
24
22
  )
@@ -34,7 +32,7 @@ def main():
34
32
  )
35
33
  logger.info("=" * 60)
36
34
 
37
- service = RunCmdService()
35
+ service = CommandService()
38
36
  init_service(service)
39
37
 
40
38
  logger.info("Service initialized, starting MCP server...")
@@ -0,0 +1,32 @@
1
+ """
2
+ 常量定义 - winterm-mcp
3
+ """
4
+
5
+ NAME = "winterm-mcp"
6
+ VERSION = "0.1.7"
7
+
8
+ ENV_POWERSHELL_PATH = "WINTERM_POWERSHELL_PATH"
9
+ ENV_CMD_PATH = "WINTERM_CMD_PATH"
10
+ ENV_PYTHON_PATH = "WINTERM_PYTHON_PATH"
11
+ ENV_LOG_LEVEL = "WINTERM_LOG_LEVEL"
12
+
13
+ POWERSHELL_PATHS = [
14
+ r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe",
15
+ r"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe",
16
+ ]
17
+
18
+ PWSH_PATHS = [
19
+ r"C:\Program Files\PowerShell\7\pwsh.exe",
20
+ r"C:\Program Files (x86)\PowerShell\7\pwsh.exe",
21
+ ]
22
+
23
+ CMD_PATHS = [
24
+ r"C:\Windows\System32\cmd.exe",
25
+ ]
26
+
27
+ PTY_COLS = 80
28
+ PTY_ROWS = 30
29
+
30
+ MIN_TIMEOUT = 1
31
+ MAX_TIMEOUT = 3600
32
+ DEFAULT_TIMEOUT = 30
winterm_mcp/models.py ADDED
@@ -0,0 +1,73 @@
1
+ """
2
+ 数据模型定义 - winterm-mcp
3
+ """
4
+
5
+ from dataclasses import dataclass, field
6
+ from datetime import datetime
7
+ from typing import Literal, Any
8
+
9
+
10
+ @dataclass
11
+ class CommandInfo:
12
+ """
13
+ 命令信息数据类
14
+ """
15
+ token: str
16
+ executable: str
17
+ args: list[str]
18
+ command: str
19
+ shell_type: Literal["powershell", "cmd", "executable"]
20
+ status: Literal["pending", "running", "completed", "not_found", "terminated"]
21
+ start_time: datetime
22
+ timeout: int
23
+ working_directory: str | None
24
+ stdout: str
25
+ stderr: str
26
+ exit_code: int | None
27
+ execution_time: int | None
28
+ timeout_occurred: bool
29
+ pty_process: Any | None
30
+ enable_streaming: bool
31
+ last_output_timestamp: int = field(default_factory=lambda: int(datetime.now().timestamp() * 1000))
32
+
33
+
34
+ @dataclass
35
+ class QueryStatusResponse:
36
+ """
37
+ 状态查询响应数据类
38
+ """
39
+ token: str
40
+ status: str
41
+ exit_code: int | None = None
42
+ stdout: str | None = None
43
+ stderr: str | None = None
44
+ execution_time: int | None = None
45
+ timeout_occurred: bool | None = None
46
+ message: str | None = None
47
+
48
+
49
+ @dataclass
50
+ class VersionInfo:
51
+ """
52
+ 版本信息数据类
53
+ """
54
+ version: str
55
+ service_status: str
56
+ python_version: str
57
+ platform: str
58
+ arch: str
59
+ env: dict[str, str | None]
60
+
61
+
62
+ @dataclass
63
+ class RunCommandParams:
64
+ """
65
+ 执行命令参数数据类
66
+ """
67
+ command: str
68
+ executable: str | None = None
69
+ args: list[str] | None = None
70
+ shell_type: Literal["powershell", "cmd", "executable"] = "executable"
71
+ timeout: int = 30
72
+ working_directory: str | None = None
73
+ enable_streaming: bool = False
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, get_version, __version__
5
+ from .service import CommandService, get_version, __version__
6
6
  from pydantic import Field
7
7
 
8
8
  CommandStr = Annotated[
@@ -17,8 +17,8 @@ CommandStr = Annotated[
17
17
  ShellTypeStr = Annotated[
18
18
  str,
19
19
  Field(
20
- description="Shell 类型 (powershell cmd),默认 powershell",
21
- pattern="^(powershell|cmd)$",
20
+ description="Shell 类型 (powershell, cmd, executable),默认 executable",
21
+ pattern="^(powershell|cmd|executable)$",
22
22
  ),
23
23
  ]
24
24
 
@@ -41,17 +41,33 @@ WorkingDirectoryStr = Annotated[
41
41
  ),
42
42
  ]
43
43
 
44
+ ExecutableStr = Annotated[
45
+ Optional[str],
46
+ Field(
47
+ description="可执行文件路径(仅当 shell_type 为 executable 时使用)",
48
+ default=None,
49
+ ),
50
+ ]
51
+
52
+ ArgsList = Annotated[
53
+ Optional[list[str]],
54
+ Field(
55
+ description="可执行文件参数列表(仅当 shell_type 为 executable 时使用)",
56
+ default=None,
57
+ ),
58
+ ]
59
+
44
60
  app = FastMCP("winterm-mcp")
45
61
 
46
- _service: Optional[RunCmdService] = None
62
+ _service: Optional[CommandService] = None
47
63
 
48
64
 
49
- def init_service(service: RunCmdService) -> None:
65
+ def init_service(service: CommandService) -> None:
50
66
  global _service
51
67
  _service = service
52
68
 
53
69
 
54
- def _svc() -> RunCmdService:
70
+ def _svc() -> CommandService:
55
71
  if _service is None:
56
72
  raise RuntimeError(
57
73
  "Service not initialized. "
@@ -76,25 +92,37 @@ def _svc() -> RunCmdService:
76
92
  )
77
93
  def run_command(
78
94
  command: CommandStr,
79
- shell_type: ShellTypeStr = "powershell",
95
+ shell_type: ShellTypeStr = "executable",
80
96
  timeout: TimeoutInt = 30,
81
97
  working_directory: WorkingDirectoryStr = None,
98
+ executable: ExecutableStr = None,
99
+ args: ArgsList = None,
100
+ enable_streaming: bool = False,
82
101
  ) -> Dict[str, Any]:
83
102
  """
84
103
  异步执行Windows终端命令
85
104
 
86
105
  Args:
87
106
  command: 要执行的命令
88
- shell_type: Shell 类型 (powershell cmd),默认 powershell
107
+ shell_type: Shell 类型 (powershell, cmd, executable),默认 executable
89
108
  timeout: 超时秒数 (1-3600),默认 30 秒
90
109
  working_directory: 工作目录(可选,默认为当前目录)
110
+ executable: 可执行文件路径(仅当 shell_type 为 executable 时使用)
111
+ args: 可执行文件参数列表(仅当 shell_type 为 executable 时使用)
112
+ enable_streaming: 启用实时流式输出
91
113
 
92
114
  Returns:
93
115
  包含token和状态信息的字典
94
116
  """
95
117
  try:
96
118
  token = _svc().run_command(
97
- command, shell_type, timeout, working_directory
119
+ command,
120
+ executable,
121
+ args,
122
+ shell_type,
123
+ timeout,
124
+ working_directory,
125
+ enable_streaming,
98
126
  )
99
127
  return {"token": token, "status": "pending", "message": "submitted"}
100
128
  except Exception as e:
@@ -129,6 +157,109 @@ def query_command_status(token: str) -> Dict[str, Any]:
129
157
  return {"error": str(e)}
130
158
 
131
159
 
160
+ @app.tool(
161
+ name="enhanced_query_command_status",
162
+ description=(
163
+ "增强版命令状态查询,支持流式输出。"
164
+ "可以指定时间戳,只返回该时间戳之后的输出。"
165
+ ),
166
+ annotations={
167
+ "title": "增强版命令状态查询器",
168
+ "readOnlyHint": True,
169
+ "destructiveHint": False,
170
+ "idempotentHint": True,
171
+ "openWorldHint": False,
172
+ },
173
+ )
174
+ def enhanced_query_command_status(
175
+ token: str,
176
+ since_timestamp: Optional[int] = None,
177
+ ) -> Dict[str, Any]:
178
+ """
179
+ 增强版命令状态查询,支持流式输出
180
+
181
+ Args:
182
+ token: 任务 token (GUID 字符串)
183
+ since_timestamp: 时间戳(毫秒),只返回此时间戳之后的输出
184
+
185
+ Returns:
186
+ 包含命令状态和结果的字典
187
+ """
188
+ try:
189
+ result = _svc().enhanced_query_command_status(token, since_timestamp)
190
+ return result
191
+ except Exception as e:
192
+ return {"error": str(e)}
193
+
194
+
195
+ @app.tool(
196
+ name="send_command_input",
197
+ description=(
198
+ "向正在运行的命令发送输入。"
199
+ "用于交互式命令,例如需要用户输入的程序。"
200
+ ),
201
+ annotations={
202
+ "title": "命令输入发送器",
203
+ "readOnlyHint": False,
204
+ "destructiveHint": False,
205
+ "idempotentHint": False,
206
+ "openWorldHint": False,
207
+ },
208
+ )
209
+ def send_command_input(
210
+ token: str,
211
+ input: str,
212
+ append_newline: bool = True,
213
+ ) -> Dict[str, Any]:
214
+ """
215
+ 向正在运行的命令发送输入
216
+
217
+ Args:
218
+ token: 任务 token (GUID 字符串)
219
+ input: 要发送的输入内容
220
+ append_newline: 是否在输入后追加换行符,默认 True
221
+
222
+ Returns:
223
+ 包含操作结果的字典
224
+ """
225
+ try:
226
+ result = _svc().send_command_input(token, input, append_newline)
227
+ return result
228
+ except Exception as e:
229
+ return {"error": str(e)}
230
+
231
+
232
+ @app.tool(
233
+ name="terminate_command",
234
+ description=(
235
+ "终止正在运行的命令。"
236
+ "强制停止命令执行,适用于长时间运行的命令。"
237
+ ),
238
+ annotations={
239
+ "title": "命令终止器",
240
+ "readOnlyHint": False,
241
+ "destructiveHint": True,
242
+ "idempotentHint": False,
243
+ "openWorldHint": False,
244
+ },
245
+ )
246
+ def terminate_command(token: str) -> Dict[str, Any]:
247
+ """
248
+ 终止正在运行的命令
249
+
250
+ Args:
251
+ token: 任务 token (GUID 字符串)
252
+
253
+ Returns:
254
+ 包含操作结果的字典
255
+ """
256
+ try:
257
+ result = _svc().terminate_command(token)
258
+ return result
259
+ except Exception as e:
260
+ return {"error": str(e)}
261
+
262
+
132
263
  @app.tool(
133
264
  name="get_version",
134
265
  description="获取 winterm-mcp 服务的版本信息和运行状态。",
@@ -147,33 +278,8 @@ def get_version_tool() -> Dict[str, Any]:
147
278
  Returns:
148
279
  包含版本号和服务状态的字典
149
280
  """
150
- import os
151
- import sys
152
-
153
281
  try:
154
- # 尝试获取 PowerShell 路径信息
155
- ps_path = None
156
- ps_error = None
157
- try:
158
- from .service import _find_powershell
159
- ps_path = _find_powershell()
160
- except FileNotFoundError as e:
161
- ps_error = str(e)
162
-
163
- return {
164
- "version": get_version(),
165
- "service_status": "running",
166
- "python_version": sys.version,
167
- "platform": sys.platform,
168
- "powershell_path": ps_path,
169
- "powershell_error": ps_error,
170
- "env": {
171
- "WINTERM_POWERSHELL_PATH": os.environ.get(
172
- "WINTERM_POWERSHELL_PATH"
173
- ),
174
- "WINTERM_PYTHON_PATH": os.environ.get("WINTERM_PYTHON_PATH"),
175
- "WINTERM_LOG_LEVEL": os.environ.get("WINTERM_LOG_LEVEL"),
176
- }
177
- }
282
+ result = _svc().get_version_info()
283
+ return result
178
284
  except Exception as e:
179
285
  return {"error": str(e), "version": __version__}