@mokoconsulting/mcp-windows 3.0.0

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.
Files changed (184) hide show
  1. package/.gitattributes +94 -0
  2. package/.gitmessage +9 -0
  3. package/.mokogitea/ISSUE_TEMPLATE/adr.md +110 -0
  4. package/.mokogitea/ISSUE_TEMPLATE/bug_report.md +48 -0
  5. package/.mokogitea/ISSUE_TEMPLATE/config.yml +18 -0
  6. package/.mokogitea/ISSUE_TEMPLATE/documentation.md +52 -0
  7. package/.mokogitea/ISSUE_TEMPLATE/feature_request.md +51 -0
  8. package/.mokogitea/ISSUE_TEMPLATE/mcp_api_integration.md +48 -0
  9. package/.mokogitea/ISSUE_TEMPLATE/mcp_connection_issue.md +67 -0
  10. package/.mokogitea/ISSUE_TEMPLATE/mcp_tool_request.md +49 -0
  11. package/.mokogitea/ISSUE_TEMPLATE/question.md +82 -0
  12. package/.mokogitea/ISSUE_TEMPLATE/rfc.md +126 -0
  13. package/.mokogitea/ISSUE_TEMPLATE/security.md +51 -0
  14. package/.mokogitea/ISSUE_TEMPLATE/version.md +24 -0
  15. package/.mokogitea/branch-protection.yml +251 -0
  16. package/.mokogitea/workflows/auto-assign.yml +76 -0
  17. package/.mokogitea/workflows/auto-bump.yml +66 -0
  18. package/.mokogitea/workflows/auto-dev-issue.yml +207 -0
  19. package/.mokogitea/workflows/auto-release.yml +421 -0
  20. package/.mokogitea/workflows/branch-cleanup.yml +48 -0
  21. package/.mokogitea/workflows/cascade-dev.yml +10 -0
  22. package/.mokogitea/workflows/changelog-validation.yml +101 -0
  23. package/.mokogitea/workflows/ci-generic.yml +191 -0
  24. package/.mokogitea/workflows/cleanup.yml +87 -0
  25. package/.mokogitea/workflows/codeql-analysis.yml +115 -0
  26. package/.mokogitea/workflows/copilot-agent.yml +44 -0
  27. package/.mokogitea/workflows/deploy-manual.yml +126 -0
  28. package/.mokogitea/workflows/enterprise-firewall-setup.yml +758 -0
  29. package/.mokogitea/workflows/gitleaks.yml +92 -0
  30. package/.mokogitea/workflows/issue-branch.yml +73 -0
  31. package/.mokogitea/workflows/mcp-auto-release.yml +278 -0
  32. package/.mokogitea/workflows/mcp-build-test.yml +65 -0
  33. package/.mokogitea/workflows/mcp-sdk-check.yml +109 -0
  34. package/.mokogitea/workflows/mcp-tool-inventory.yml +61 -0
  35. package/.mokogitea/workflows/notify.yml +70 -0
  36. package/.mokogitea/workflows/npm-publish.yml +113 -0
  37. package/.mokogitea/workflows/pr-check.yml +534 -0
  38. package/.mokogitea/workflows/pre-release.yml +252 -0
  39. package/.mokogitea/workflows/rc-revert.yml +66 -0
  40. package/.mokogitea/workflows/repo-health.yml +712 -0
  41. package/.mokogitea/workflows/repository-cleanup.yml +525 -0
  42. package/.mokogitea/workflows/security-audit.yml +82 -0
  43. package/.mokogitea/workflows/standards-compliance.yml +2614 -0
  44. package/.mokogitea/workflows/sync-version-on-merge.yml +133 -0
  45. package/.mokogitea/workflows/update-server.yml +312 -0
  46. package/.mokogitea/workflows/workflow-sync-trigger.yml +73 -0
  47. package/CHANGELOG.md +130 -0
  48. package/CLAUDE.md +49 -0
  49. package/CONTRIBUTING.md +161 -0
  50. package/ISSUES.md +601 -0
  51. package/Makefile +70 -0
  52. package/README.md +80 -0
  53. package/automation/ci-issue-reporter.sh +237 -0
  54. package/config.example.json +18 -0
  55. package/dist/index.d.ts +3 -0
  56. package/dist/index.js +111 -0
  57. package/dist/shell.d.ts +50 -0
  58. package/dist/shell.js +209 -0
  59. package/dist/tools/apps.d.ts +3 -0
  60. package/dist/tools/apps.js +63 -0
  61. package/dist/tools/audio.d.ts +3 -0
  62. package/dist/tools/audio.js +142 -0
  63. package/dist/tools/audio_apps.d.ts +3 -0
  64. package/dist/tools/audio_apps.js +86 -0
  65. package/dist/tools/automation.d.ts +3 -0
  66. package/dist/tools/automation.js +261 -0
  67. package/dist/tools/bluetooth.d.ts +3 -0
  68. package/dist/tools/bluetooth.js +96 -0
  69. package/dist/tools/clipboard.d.ts +3 -0
  70. package/dist/tools/clipboard.js +118 -0
  71. package/dist/tools/config.d.ts +3 -0
  72. package/dist/tools/config.js +85 -0
  73. package/dist/tools/dialog.d.ts +3 -0
  74. package/dist/tools/dialog.js +72 -0
  75. package/dist/tools/display.d.ts +3 -0
  76. package/dist/tools/display.js +256 -0
  77. package/dist/tools/drives.d.ts +3 -0
  78. package/dist/tools/drives.js +98 -0
  79. package/dist/tools/environment.d.ts +3 -0
  80. package/dist/tools/environment.js +129 -0
  81. package/dist/tools/execute.d.ts +3 -0
  82. package/dist/tools/execute.js +28 -0
  83. package/dist/tools/filesystem.d.ts +3 -0
  84. package/dist/tools/filesystem.js +230 -0
  85. package/dist/tools/firewall.d.ts +3 -0
  86. package/dist/tools/firewall.js +108 -0
  87. package/dist/tools/hosts.d.ts +3 -0
  88. package/dist/tools/hosts.js +119 -0
  89. package/dist/tools/maintenance.d.ts +3 -0
  90. package/dist/tools/maintenance.js +236 -0
  91. package/dist/tools/netstat.d.ts +3 -0
  92. package/dist/tools/netstat.js +56 -0
  93. package/dist/tools/network.d.ts +3 -0
  94. package/dist/tools/network.js +70 -0
  95. package/dist/tools/notification.d.ts +3 -0
  96. package/dist/tools/notification.js +41 -0
  97. package/dist/tools/power.d.ts +3 -0
  98. package/dist/tools/power.js +104 -0
  99. package/dist/tools/printer.d.ts +3 -0
  100. package/dist/tools/printer.js +97 -0
  101. package/dist/tools/process.d.ts +3 -0
  102. package/dist/tools/process.js +54 -0
  103. package/dist/tools/process_kill.d.ts +3 -0
  104. package/dist/tools/process_kill.js +48 -0
  105. package/dist/tools/recycle_bin.d.ts +3 -0
  106. package/dist/tools/recycle_bin.js +108 -0
  107. package/dist/tools/registry.d.ts +3 -0
  108. package/dist/tools/registry.js +136 -0
  109. package/dist/tools/scheduler.d.ts +3 -0
  110. package/dist/tools/scheduler.js +116 -0
  111. package/dist/tools/service.d.ts +3 -0
  112. package/dist/tools/service.js +79 -0
  113. package/dist/tools/startup.d.ts +3 -0
  114. package/dist/tools/startup.js +159 -0
  115. package/dist/tools/storage.d.ts +3 -0
  116. package/dist/tools/storage.js +129 -0
  117. package/dist/tools/system.d.ts +3 -0
  118. package/dist/tools/system.js +84 -0
  119. package/dist/tools/system_mgmt.d.ts +3 -0
  120. package/dist/tools/system_mgmt.js +174 -0
  121. package/dist/tools/terminal.d.ts +3 -0
  122. package/dist/tools/terminal.js +80 -0
  123. package/dist/tools/theme.d.ts +3 -0
  124. package/dist/tools/theme.js +165 -0
  125. package/dist/tools/usb.d.ts +3 -0
  126. package/dist/tools/usb.js +52 -0
  127. package/dist/tools/virtual_desktop.d.ts +3 -0
  128. package/dist/tools/virtual_desktop.js +112 -0
  129. package/dist/tools/wifi.d.ts +3 -0
  130. package/dist/tools/wifi.js +136 -0
  131. package/dist/tools/window.d.ts +3 -0
  132. package/dist/tools/window.js +189 -0
  133. package/dist/tools/winget.d.ts +3 -0
  134. package/dist/tools/winget.js +79 -0
  135. package/dist/tools/wsl.d.ts +3 -0
  136. package/dist/tools/wsl.js +99 -0
  137. package/docs/API.md +63 -0
  138. package/docs/ARCHITECTURE.md +73 -0
  139. package/docs/INSTALLATION.md +102 -0
  140. package/docs/index.md +12 -0
  141. package/package.json +35 -0
  142. package/scripts/setup.mjs +123 -0
  143. package/src/index.ts +125 -0
  144. package/src/shell.ts +253 -0
  145. package/src/tools/apps.ts +76 -0
  146. package/src/tools/audio.ts +161 -0
  147. package/src/tools/audio_apps.ts +98 -0
  148. package/src/tools/automation.ts +297 -0
  149. package/src/tools/bluetooth.ts +114 -0
  150. package/src/tools/clipboard.ts +138 -0
  151. package/src/tools/config.ts +105 -0
  152. package/src/tools/dialog.ts +87 -0
  153. package/src/tools/display.ts +285 -0
  154. package/src/tools/drives.ts +124 -0
  155. package/src/tools/environment.ts +146 -0
  156. package/src/tools/execute.ts +35 -0
  157. package/src/tools/filesystem.ts +273 -0
  158. package/src/tools/firewall.ts +125 -0
  159. package/src/tools/hosts.ts +135 -0
  160. package/src/tools/maintenance.ts +299 -0
  161. package/src/tools/netstat.ts +72 -0
  162. package/src/tools/network.ts +84 -0
  163. package/src/tools/notification.ts +50 -0
  164. package/src/tools/power.ts +123 -0
  165. package/src/tools/printer.ts +114 -0
  166. package/src/tools/process.ts +80 -0
  167. package/src/tools/process_kill.ts +57 -0
  168. package/src/tools/recycle_bin.ts +126 -0
  169. package/src/tools/registry.ts +165 -0
  170. package/src/tools/scheduler.ts +140 -0
  171. package/src/tools/service.ts +102 -0
  172. package/src/tools/startup.ts +180 -0
  173. package/src/tools/storage.ts +141 -0
  174. package/src/tools/system.ts +99 -0
  175. package/src/tools/system_mgmt.ts +190 -0
  176. package/src/tools/terminal.ts +117 -0
  177. package/src/tools/theme.ts +205 -0
  178. package/src/tools/usb.ts +65 -0
  179. package/src/tools/virtual_desktop.ts +122 -0
  180. package/src/tools/wifi.ts +157 -0
  181. package/src/tools/window.ts +211 -0
  182. package/src/tools/winget.ts +100 -0
  183. package/src/tools/wsl.ts +112 -0
  184. package/tsconfig.json +19 -0
@@ -0,0 +1,189 @@
1
+ /* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
2
+ * SPDX-License-Identifier: GPL-3.0-or-later
3
+ *
4
+ * Tools: windows_window_list (#14), windows_window_control (#15)
5
+ */
6
+ import { z } from 'zod';
7
+ import { runPowerShell } from '../shell.js';
8
+ const WIN32_TYPES = `
9
+ Add-Type @'
10
+ using System;
11
+ using System.Text;
12
+ using System.Collections.Generic;
13
+ using System.Runtime.InteropServices;
14
+
15
+ public class WindowManager {
16
+ [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
17
+ [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
18
+ [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
19
+ [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd);
20
+ [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
21
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
22
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
23
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
24
+ [DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
25
+ [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
26
+ [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);
27
+ [DllImport("user32.dll")] public static extern bool IsZoomed(IntPtr hWnd);
28
+ [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
29
+
30
+ public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
31
+
32
+ [StructLayout(LayoutKind.Sequential)]
33
+ public struct RECT { public int Left, Top, Right, Bottom; }
34
+
35
+ public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
36
+ public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
37
+ public const uint SWP_NOMOVE = 0x0002;
38
+ public const uint SWP_NOSIZE = 0x0001;
39
+ public const uint WM_CLOSE = 0x0010;
40
+ }
41
+ '@ -ErrorAction SilentlyContinue
42
+ `;
43
+ export function registerWindowTools(server) {
44
+ server.tool('windows_window_list', 'List all visible windows with title, PID, process name, position, size, and state.', {
45
+ filter: z.string().optional().describe('Filter by window title (substring)'),
46
+ }, async ({ filter }) => {
47
+ const filterClause = filter
48
+ ? `| Where-Object { $_.Title -like '*${filter.replace(/'/g, "''")}*' }`
49
+ : '';
50
+ const ps = `
51
+ ${WIN32_TYPES}
52
+ $windows = [System.Collections.Generic.List[PSObject]]::new()
53
+ $zOrder = 0
54
+ [WindowManager]::EnumWindows({
55
+ param($hWnd, $lParam)
56
+ if (-not [WindowManager]::IsWindowVisible($hWnd)) { return $true }
57
+ $len = [WindowManager]::GetWindowTextLength($hWnd)
58
+ if ($len -eq 0) { return $true }
59
+ $sb = New-Object System.Text.StringBuilder($len + 1)
60
+ [WindowManager]::GetWindowText($hWnd, $sb, $sb.Capacity) | Out-Null
61
+ $title = $sb.ToString()
62
+ if (-not $title) { return $true }
63
+
64
+ $pid = [uint32]0
65
+ [WindowManager]::GetWindowThreadProcessId($hWnd, [ref]$pid) | Out-Null
66
+ $proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
67
+
68
+ $rect = New-Object WindowManager+RECT
69
+ [WindowManager]::GetWindowRect($hWnd, [ref]$rect) | Out-Null
70
+
71
+ $state = 'Normal'
72
+ if ([WindowManager]::IsIconic($hWnd)) { $state = 'Minimized' }
73
+ elseif ([WindowManager]::IsZoomed($hWnd)) { $state = 'Maximized' }
74
+
75
+ $script:windows.Add([PSCustomObject]@{
76
+ ZOrder = $script:zOrder++
77
+ Title = $title
78
+ PID = $pid
79
+ Process = if ($proc) { $proc.ProcessName } else { '?' }
80
+ X = $rect.Left
81
+ Y = $rect.Top
82
+ Width = $rect.Right - $rect.Left
83
+ Height = $rect.Bottom - $rect.Top
84
+ State = $state
85
+ })
86
+ return $true
87
+ }, [IntPtr]::Zero) | Out-Null
88
+
89
+ $windows ${filterClause} | ConvertTo-Json -Depth 3 -Compress`;
90
+ const result = await runPowerShell(ps, { timeout: 10000 });
91
+ if (result.exitCode !== 0) {
92
+ return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
93
+ }
94
+ if (!result.stdout) {
95
+ return { content: [{ type: 'text', text: 'No visible windows found.' }] };
96
+ }
97
+ const windows = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
98
+ const lines = windows.map((w) => {
99
+ const stateIcon = w.State === 'Minimized' ? '[-]' : w.State === 'Maximized' ? '[+]' : '[ ]';
100
+ return `${stateIcon} ${String(w.PID).padStart(6)} ${w.Process.padEnd(20).slice(0, 20)} ${String(w.Width).padStart(5)}x${String(w.Height).padEnd(5)} (${w.X},${w.Y}) ${w.Title.slice(0, 60)}`;
101
+ });
102
+ const header = `Sta ${'PID'.padStart(6)} ${'Process'.padEnd(20)} ${'Size'.padStart(11)} Pos Title`;
103
+ return {
104
+ content: [{ type: 'text', text: `${header}\n${'─'.repeat(110)}\n${lines.join('\n')}\n\n${windows.length} windows` }],
105
+ };
106
+ });
107
+ server.tool('windows_window_control', 'Move, resize, minimize, maximize, restore, close, or focus a window.', {
108
+ title: z.string().optional().describe('Window title (substring match)'),
109
+ pid: z.number().optional().describe('Process ID'),
110
+ action: z.enum(['minimize', 'maximize', 'restore', 'close', 'focus', 'move', 'resize', 'topmost']).describe('Action'),
111
+ x: z.number().optional().describe('X position (for move)'),
112
+ y: z.number().optional().describe('Y position (for move)'),
113
+ width: z.number().optional().describe('Width (for resize)'),
114
+ height: z.number().optional().describe('Height (for resize)'),
115
+ topmost: z.boolean().optional().describe('Set always-on-top (for topmost action)'),
116
+ }, async ({ title, pid, action, x, y, width, height, topmost }) => {
117
+ if (!title && !pid) {
118
+ return { content: [{ type: 'text', text: 'Provide either title or pid.' }], isError: true };
119
+ }
120
+ const findWindow = title
121
+ ? `
122
+ $target = '${title.replace(/'/g, "''")}'
123
+ $hWnd = [IntPtr]::Zero
124
+ [WindowManager]::EnumWindows({
125
+ param($h, $l)
126
+ $sb = New-Object System.Text.StringBuilder 256
127
+ [WindowManager]::GetWindowText($h, $sb, 256) | Out-Null
128
+ if ($sb.ToString() -like "*$target*" -and [WindowManager]::IsWindowVisible($h)) {
129
+ $script:hWnd = $h; return $false
130
+ }
131
+ return $true
132
+ }, [IntPtr]::Zero) | Out-Null
133
+ if ($hWnd -eq [IntPtr]::Zero) { throw "Window not found: $target" }`
134
+ : `
135
+ $proc = Get-Process -Id ${pid} -ErrorAction Stop
136
+ $hWnd = $proc.MainWindowHandle
137
+ if ($hWnd -eq [IntPtr]::Zero) { throw "Process ${pid} has no visible window" }`;
138
+ let actionCode;
139
+ switch (action) {
140
+ case 'minimize':
141
+ actionCode = `[WindowManager]::ShowWindow($hWnd, 6) | Out-Null; "Minimized"`;
142
+ break;
143
+ case 'maximize':
144
+ actionCode = `[WindowManager]::ShowWindow($hWnd, 3) | Out-Null; "Maximized"`;
145
+ break;
146
+ case 'restore':
147
+ actionCode = `[WindowManager]::ShowWindow($hWnd, 9) | Out-Null; "Restored"`;
148
+ break;
149
+ case 'close':
150
+ actionCode = `[WindowManager]::PostMessage($hWnd, [WindowManager]::WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null; "Close message sent"`;
151
+ break;
152
+ case 'focus':
153
+ actionCode = `[WindowManager]::ShowWindow($hWnd, 9) | Out-Null; [WindowManager]::SetForegroundWindow($hWnd) | Out-Null; "Focused"`;
154
+ break;
155
+ case 'move':
156
+ if (x === undefined || y === undefined) {
157
+ return { content: [{ type: 'text', text: 'Move requires x and y.' }], isError: true };
158
+ }
159
+ actionCode = `
160
+ $rect = New-Object WindowManager+RECT
161
+ [WindowManager]::GetWindowRect($hWnd, [ref]$rect) | Out-Null
162
+ $w = $rect.Right - $rect.Left; $h = $rect.Bottom - $rect.Top
163
+ [WindowManager]::MoveWindow($hWnd, ${x}, ${y}, $w, $h, $true) | Out-Null
164
+ "Moved to (${x}, ${y})"`;
165
+ break;
166
+ case 'resize':
167
+ if (!width || !height) {
168
+ return { content: [{ type: 'text', text: 'Resize requires width and height.' }], isError: true };
169
+ }
170
+ actionCode = `
171
+ $rect = New-Object WindowManager+RECT
172
+ [WindowManager]::GetWindowRect($hWnd, [ref]$rect) | Out-Null
173
+ [WindowManager]::MoveWindow($hWnd, $rect.Left, $rect.Top, ${width}, ${height}, $true) | Out-Null
174
+ "Resized to ${width}x${height}"`;
175
+ break;
176
+ case 'topmost':
177
+ const insertAfter = topmost !== false ? '[WindowManager]::HWND_TOPMOST' : '[WindowManager]::HWND_NOTOPMOST';
178
+ actionCode = `[WindowManager]::SetWindowPos($hWnd, ${insertAfter}, 0, 0, 0, 0, [WindowManager]::SWP_NOMOVE -bor [WindowManager]::SWP_NOSIZE) | Out-Null; "Topmost: ${topmost !== false}"`;
179
+ break;
180
+ }
181
+ const ps = `${WIN32_TYPES}\n${findWindow}\n${actionCode}`;
182
+ const result = await runPowerShell(ps, { timeout: 10000 });
183
+ return {
184
+ content: [{ type: 'text', text: result.stdout || result.stderr }],
185
+ isError: result.exitCode !== 0,
186
+ };
187
+ });
188
+ }
189
+ //# sourceMappingURL=window.js.map
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerWingetTools(server: McpServer): void;
3
+ //# sourceMappingURL=winget.d.ts.map
@@ -0,0 +1,79 @@
1
+ /* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
2
+ * SPDX-License-Identifier: GPL-3.0-or-later
3
+ *
4
+ * Tools: windows_winget_search (#69), windows_winget_manage (#70), windows_winget_export (#71)
5
+ */
6
+ import { z } from 'zod';
7
+ import { runShell } from '../shell.js';
8
+ export function registerWingetTools(server) {
9
+ server.tool('windows_winget_search', 'Search for packages in the winget repository.', {
10
+ query: z.string().describe('Search query (name or ID)'),
11
+ source: z.enum(['winget', 'msstore', 'all']).default('all').describe('Package source'),
12
+ limit: z.number().default(20).describe('Max results'),
13
+ }, async ({ query, source, limit }) => {
14
+ const sourceArg = source !== 'all' ? `--source ${source}` : '';
15
+ const cmd = `winget search "${query}" ${sourceArg} --count ${limit} --accept-source-agreements --disable-interactivity`;
16
+ const result = await runShell(cmd, { shell: 'cmd', timeout: 30000 });
17
+ const clean = result.stdout.replace(/\0/g, '').trim();
18
+ return {
19
+ content: [{ type: 'text', text: clean || 'No packages found.' }],
20
+ isError: result.exitCode !== 0 && !clean,
21
+ };
22
+ });
23
+ server.tool('windows_winget_manage', 'Install, upgrade, or uninstall packages via winget. Also list installed or upgradable packages.', {
24
+ action: z.enum(['install', 'upgrade', 'uninstall', 'list', 'upgradable', 'upgrade_all']).describe('Action'),
25
+ id: z.string().optional().describe('Package ID (e.g. "Microsoft.VisualStudioCode")'),
26
+ version: z.string().optional().describe('Specific version to install'),
27
+ }, async ({ action, id, version }) => {
28
+ let cmd;
29
+ switch (action) {
30
+ case 'install':
31
+ if (!id)
32
+ return { content: [{ type: 'text', text: 'install requires id.' }], isError: true };
33
+ cmd = `winget install --id "${id}" ${version ? `--version "${version}"` : ''} --accept-package-agreements --accept-source-agreements --disable-interactivity`;
34
+ break;
35
+ case 'upgrade':
36
+ if (!id)
37
+ return { content: [{ type: 'text', text: 'upgrade requires id.' }], isError: true };
38
+ cmd = `winget upgrade --id "${id}" --accept-package-agreements --accept-source-agreements --disable-interactivity`;
39
+ break;
40
+ case 'upgrade_all':
41
+ cmd = `winget upgrade --all --accept-package-agreements --accept-source-agreements --disable-interactivity`;
42
+ break;
43
+ case 'uninstall':
44
+ if (!id)
45
+ return { content: [{ type: 'text', text: 'uninstall requires id.' }], isError: true };
46
+ cmd = `winget uninstall --id "${id}" --disable-interactivity`;
47
+ break;
48
+ case 'list':
49
+ cmd = `winget list --accept-source-agreements --disable-interactivity`;
50
+ break;
51
+ case 'upgradable':
52
+ cmd = `winget upgrade --accept-source-agreements --disable-interactivity`;
53
+ break;
54
+ }
55
+ const timeout = action === 'install' || action === 'upgrade' || action === 'upgrade_all' ? 300000 : 30000;
56
+ const result = await runShell(cmd, { shell: 'cmd', timeout });
57
+ const clean = result.stdout.replace(/\0/g, '').trim();
58
+ return {
59
+ content: [{ type: 'text', text: clean || result.stderr || `${action} completed.` }],
60
+ isError: result.exitCode !== 0 && !clean,
61
+ };
62
+ });
63
+ server.tool('windows_winget_export', 'Export installed packages to JSON or import from a JSON file.', {
64
+ action: z.enum(['export', 'import']).describe('Action'),
65
+ path: z.string().describe('File path for export/import JSON'),
66
+ }, async ({ action, path }) => {
67
+ const cmd = action === 'export'
68
+ ? `winget export -o "${path}" --accept-source-agreements --disable-interactivity`
69
+ : `winget import -i "${path}" --accept-package-agreements --accept-source-agreements --disable-interactivity`;
70
+ const timeout = action === 'import' ? 600000 : 30000;
71
+ const result = await runShell(cmd, { shell: 'cmd', timeout });
72
+ const clean = result.stdout.replace(/\0/g, '').trim();
73
+ return {
74
+ content: [{ type: 'text', text: clean || `${action} completed: ${path}` }],
75
+ isError: result.exitCode !== 0 && !clean,
76
+ };
77
+ });
78
+ }
79
+ //# sourceMappingURL=winget.js.map
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerWslTools(server: McpServer): void;
3
+ //# sourceMappingURL=wsl.d.ts.map
@@ -0,0 +1,99 @@
1
+ /* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
2
+ * SPDX-License-Identifier: GPL-3.0-or-later
3
+ *
4
+ * Tools: windows_wsl_list (#66), windows_wsl_manage (#67), windows_wsl_exec (#68)
5
+ */
6
+ import { z } from 'zod';
7
+ import { runShell, runPowerShell } from '../shell.js';
8
+ export function registerWslTools(server) {
9
+ server.tool('windows_wsl_list', 'List installed WSL distributions with status, version, and default flag.', {}, async () => {
10
+ // wsl.exe outputs UTF-16; use PowerShell to decode properly
11
+ const ps = `$out = wsl --list --verbose 2>&1; if ($LASTEXITCODE -ne 0) { "WSL_ERROR:$out" } else { $out }`;
12
+ const result = await runPowerShell(ps, { timeout: 15000 });
13
+ const clean = result.stdout.replace(/\0/g, '').trim();
14
+ if (clean.startsWith('WSL_ERROR:') || result.exitCode !== 0) {
15
+ const msg = clean.replace('WSL_ERROR:', '').trim();
16
+ return { content: [{ type: 'text', text: msg || 'WSL is not installed or not available. Install with: wsl --install' }] };
17
+ }
18
+ return { content: [{ type: 'text', text: clean || 'No WSL distributions installed.' }] };
19
+ });
20
+ server.tool('windows_wsl_manage', 'Manage WSL: start, stop, shutdown, set default, export, import, or remove distros.', {
21
+ action: z.enum(['shutdown', 'terminate', 'set_default', 'set_version', 'export', 'import', 'unregister']).describe('Action'),
22
+ distro: z.string().optional().describe('Distribution name'),
23
+ version: z.number().optional().describe('WSL version 1 or 2 (for set_version)'),
24
+ path: z.string().optional().describe('File path (for export/import)'),
25
+ }, async ({ action, distro, version, path }) => {
26
+ let cmd;
27
+ switch (action) {
28
+ case 'shutdown':
29
+ cmd = 'wsl --shutdown';
30
+ break;
31
+ case 'terminate':
32
+ if (!distro)
33
+ return { content: [{ type: 'text', text: 'terminate requires distro.' }], isError: true };
34
+ cmd = `wsl --terminate ${distro}`;
35
+ break;
36
+ case 'set_default':
37
+ if (!distro)
38
+ return { content: [{ type: 'text', text: 'set_default requires distro.' }], isError: true };
39
+ cmd = `wsl --set-default ${distro}`;
40
+ break;
41
+ case 'set_version':
42
+ if (!distro || !version)
43
+ return { content: [{ type: 'text', text: 'set_version requires distro and version.' }], isError: true };
44
+ cmd = `wsl --set-version ${distro} ${version}`;
45
+ break;
46
+ case 'export':
47
+ if (!distro || !path)
48
+ return { content: [{ type: 'text', text: 'export requires distro and path.' }], isError: true };
49
+ cmd = `wsl --export ${distro} "${path}"`;
50
+ break;
51
+ case 'import':
52
+ if (!distro || !path)
53
+ return { content: [{ type: 'text', text: 'import requires distro and path.' }], isError: true };
54
+ cmd = `wsl --import ${distro} "C:\\WSL\\${distro}" "${path}"`;
55
+ break;
56
+ case 'unregister':
57
+ if (!distro)
58
+ return { content: [{ type: 'text', text: 'unregister requires distro.' }], isError: true };
59
+ cmd = `wsl --unregister ${distro}`;
60
+ break;
61
+ }
62
+ const result = await runShell(cmd, { shell: 'cmd', timeout: 60000 });
63
+ const clean = (result.stdout + '\n' + result.stderr).replace(/\0/g, '').trim();
64
+ return {
65
+ content: [{ type: 'text', text: clean || `${action} completed.` }],
66
+ isError: result.exitCode !== 0,
67
+ };
68
+ });
69
+ server.tool('windows_wsl_exec', 'Execute a command inside a WSL distribution. Returns stdout, stderr, exit code.', {
70
+ command: z.string().describe('Command to execute'),
71
+ distro: z.string().optional().describe('Distribution name (default distro if omitted)'),
72
+ user: z.string().optional().describe('Run as user'),
73
+ cwd: z.string().optional().describe('Working directory inside WSL'),
74
+ timeout: z.number().default(30000).describe('Timeout in ms'),
75
+ }, async ({ command, distro, user, cwd, timeout }) => {
76
+ const parts = ['wsl'];
77
+ if (distro)
78
+ parts.push('-d', distro);
79
+ if (user)
80
+ parts.push('-u', user);
81
+ if (cwd)
82
+ parts.push('--cd', cwd);
83
+ parts.push('--', 'bash', '-c', `"${command.replace(/"/g, '\\"')}"`);
84
+ const result = await runShell(parts.join(' '), { shell: 'cmd', timeout });
85
+ const clean = result.stdout.replace(/\0/g, '').trim();
86
+ const cleanErr = result.stderr.replace(/\0/g, '').trim();
87
+ const output = [];
88
+ if (clean)
89
+ output.push(clean);
90
+ if (cleanErr)
91
+ output.push(`[stderr]\n${cleanErr}`);
92
+ output.push(`[exit code: ${result.exitCode}]`);
93
+ return {
94
+ content: [{ type: 'text', text: output.join('\n\n') }],
95
+ isError: result.exitCode !== 0,
96
+ };
97
+ });
98
+ }
99
+ //# sourceMappingURL=wsl.js.map
package/docs/API.md ADDED
@@ -0,0 +1,63 @@
1
+ <!--
2
+ Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
3
+ SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ # FILE INFORMATION
6
+ DEFGROUP: mcp_windows.Documentation
7
+ PATH: /docs/API.md
8
+ VERSION: 01.00.00
9
+ BRIEF: MCP tool reference documentation
10
+ -->
11
+
12
+ # API Reference
13
+
14
+ All tools accept an optional `connection` parameter to target a specific named connection. If omitted, the default connection is used.
15
+
16
+ ## Example Resources
17
+
18
+ > Replace these example tools with your actual API tools.
19
+
20
+ ### `example_resources_list`
21
+ List resources with optional search.
22
+
23
+ | Parameter | Type | Required | Description |
24
+ |-----------|------|----------|-------------|
25
+ | `search` | string | No | Search query |
26
+ | `limit` | number | No | Max results |
27
+ | `page` | number | No | Page number (0-based) |
28
+
29
+ ### `example_resource_get`
30
+ Get a single resource by ID.
31
+
32
+ | Parameter | Type | Required | Description |
33
+ |-----------|------|----------|-------------|
34
+ | `id` | number | Yes | Resource ID |
35
+
36
+ ### `example_resource_create`
37
+ Create a new resource.
38
+
39
+ | Parameter | Type | Required | Description |
40
+ |-----------|------|----------|-------------|
41
+ | `name` | string | Yes | Resource name |
42
+ | `description` | string | No | Resource description |
43
+
44
+ ## Generic
45
+
46
+ ### `api_request`
47
+ Make a raw API request to any endpoint.
48
+
49
+ | Parameter | Type | Required | Description |
50
+ |-----------|------|----------|-------------|
51
+ | `method` | `"GET"` / `"POST"` / `"PUT"` / `"PATCH"` / `"DELETE"` | Yes | HTTP method |
52
+ | `endpoint` | string | Yes | API path |
53
+ | `body` | object | No | Request body |
54
+ | `params` | object | No | Query parameters |
55
+
56
+ ### `list_connections`
57
+ List all configured connections. No parameters.
58
+
59
+ ## Revision History
60
+
61
+ | Date | Version | Author | Notes |
62
+ | --- | --- | --- | --- |
63
+ | 2026-05-07 | 0.0.1 | jmiller | Initial template API reference |
@@ -0,0 +1,73 @@
1
+ <!--
2
+ Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
3
+ SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ # FILE INFORMATION
6
+ DEFGROUP: mcp_windows.Documentation
7
+ PATH: /docs/ARCHITECTURE.md
8
+ VERSION: 01.00.00
9
+ BRIEF: Architecture overview and design decisions
10
+ -->
11
+
12
+ # Architecture
13
+
14
+ ## Overview
15
+
16
+ mcp_windows is a Model Context Protocol (MCP) server that bridges AI assistants with a REST API.
17
+
18
+ ```
19
+ AI Assistant <--> MCP (stdio) <--> ApiClient <--> REST API
20
+ ```
21
+
22
+ ## Components
23
+
24
+ ### `src/index.ts` — Server Entry Point
25
+
26
+ Registers all MCP tools with `McpServer` from `@modelcontextprotocol/sdk`. Each tool maps to one or more API endpoints. Uses Zod schemas for input validation.
27
+
28
+ Includes shared helpers:
29
+ - `formatResponse()` — normalizes error/success responses into MCP text content
30
+ - `paginationQuery()` — builds pagination query params
31
+ - `ConnectionParam` / `PaginationParams` — reusable Zod parameter spreads
32
+
33
+ ### `src/client.ts` — HTTP Client
34
+
35
+ The `ApiClient` class handles all HTTP communication:
36
+ - Uses `node:https` / `node:http` (not `fetch`) for reliable self-signed cert support
37
+ - Supports GET, POST, PUT, PATCH, DELETE
38
+ - JSON serialization/deserialization with error handling
39
+
40
+ ### `src/config.ts` — Configuration Loader
41
+
42
+ Loads connection details from `~/.<project>.json`. Supports multiple named connections with a configurable default.
43
+
44
+ ### `src/types.ts` — Type Definitions
45
+
46
+ TypeScript interfaces for `ApiConnection`, `ApiConfig`, and `ApiResponse`.
47
+
48
+ ### `scripts/setup.mjs` — Interactive Setup
49
+
50
+ Node.js script using `readline/promises` for interactive config creation.
51
+
52
+ ## Design Decisions
53
+
54
+ ### Why `node:https` instead of `fetch`?
55
+
56
+ Node.js 24's built-in `fetch` does not honor self-signed certificate bypass. The classic `node:https` module with `rejectUnauthorized: false` works reliably across all Node.js versions.
57
+
58
+ ### Why multiple named connections?
59
+
60
+ Multi-instance support is a core use case — managing staging, production, and dev environments from a single MCP server.
61
+
62
+ ## Data Flow
63
+
64
+ 1. AI assistant sends a tool call via MCP stdio transport
65
+ 2. `index.ts` validates parameters with Zod and resolves the connection
66
+ 3. `ApiClient` constructs the API URL, attaches auth headers, and makes the HTTP request
67
+ 4. Response is parsed as JSON and returned as MCP tool output
68
+
69
+ ## Revision History
70
+
71
+ | Date | Version | Author | Notes |
72
+ | --- | --- | --- | --- |
73
+ | 2026-05-07 | 0.0.1 | jmiller | Initial architecture document |
@@ -0,0 +1,102 @@
1
+ <!--
2
+ Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
3
+ SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ # FILE INFORMATION
6
+ DEFGROUP: mcp_windows.Documentation
7
+ PATH: /docs/INSTALLATION.md
8
+ VERSION: 01.00.00
9
+ BRIEF: Installation and setup instructions
10
+ -->
11
+
12
+ # Installation
13
+
14
+ ## Prerequisites
15
+
16
+ - **Node.js** 20.0.0 or later
17
+ - **npm** (included with Node.js)
18
+ - Access to the target API with valid credentials
19
+
20
+ ## Install
21
+
22
+ ```sh
23
+ git clone https://git.mokoconsulting.tech/MokoConsulting/mcp_windows.git
24
+ cd mcp_windows
25
+ npm install
26
+ npm run build
27
+ npm run setup
28
+ ```
29
+
30
+ The setup wizard will prompt for:
31
+
32
+ 1. **Connection name** — a label for this instance (e.g. `production`, `staging`)
33
+ 2. **API URL** — the base URL of the instance
34
+ 3. **API key/token** — authentication credentials
35
+ 4. **TLS verification** — whether to skip certificate verification (for self-signed certs)
36
+
37
+ Run `npm run setup` again to add more connections.
38
+
39
+ ## Register with Claude Code
40
+
41
+ Add to your Claude Code MCP settings (`~/.claude.json` or project `.mcp.json`):
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "mcp_windows": {
47
+ "type": "stdio",
48
+ "command": "node",
49
+ "args": ["/path/to/mcp_windows/dist/index.js"]
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ Restart Claude Code after adding the server.
56
+
57
+ ## Configuration File
58
+
59
+ The config is stored at `~/.mcp_windows.json`:
60
+
61
+ ```json
62
+ {
63
+ "defaultConnection": "production",
64
+ "connections": {
65
+ "production": {
66
+ "baseUrl": "https://api.example.com",
67
+ "apiKey": "your-api-key"
68
+ },
69
+ "staging": {
70
+ "baseUrl": "https://api-staging.example.com",
71
+ "apiKey": "your-staging-key",
72
+ "insecure": true
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ You can also set the `MCP_WINDOWS_CONFIG` environment variable to use a config file at a custom path.
79
+
80
+ ## Verification
81
+
82
+ ```sh
83
+ npm start
84
+ ```
85
+
86
+ If configured correctly, the server will start listening on stdio.
87
+
88
+ ## Troubleshooting
89
+
90
+ ### "Failed to load config" error
91
+ Run `npm run setup` to create the config file.
92
+
93
+ ### Connection errors
94
+ - Verify the API is reachable from your machine
95
+ - For self-signed certs, set `"insecure": true`
96
+ - Ensure the API key/token is valid
97
+
98
+ ## Revision History
99
+
100
+ | Date | Version | Author | Notes |
101
+ | --- | --- | --- | --- |
102
+ | 2026-05-07 | 0.0.1 | jmiller | Initial installation guide |
package/docs/index.md ADDED
@@ -0,0 +1,12 @@
1
+ <!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
2
+ SPDX-License-Identifier: GPL-3.0-or-later -->
3
+
4
+ # mcp_windows Documentation
5
+
6
+ See the [README](../README.md) for quick start, customization guide, and tool patterns.
7
+
8
+ ## Documents
9
+
10
+ - [INSTALLATION.md](./INSTALLATION.md) — Prerequisites, install, setup, troubleshooting
11
+ - [ARCHITECTURE.md](./ARCHITECTURE.md) — Component overview, design decisions, data flow
12
+ - [API.md](./API.md) — Full MCP tool reference with parameter tables
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@mokoconsulting/mcp-windows",
3
+ "version": "3.0.0",
4
+ "description": "MCP server for Windows desktop system operations",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mcp-windows": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "lint": "eslint src/",
15
+ "setup": "node scripts/setup.mjs",
16
+ "clean": "rm -rf dist/"
17
+ },
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.12.1",
20
+ "zod": "^3.24.4"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.15.3",
24
+ "typescript": "^5.8.3"
25
+ },
26
+ "engines": {
27
+ "node": ">=20.0.0"
28
+ },
29
+ "license": "GPL-3.0-or-later",
30
+ "author": "Moko Consulting <hello@mokoconsulting.tech>",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://git.mokoconsulting.tech/MokoConsulting/mcp-windows.git"
34
+ }
35
+ }