winhelm-mcp 1.1.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.
- package/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +646 -0
- package/dist/config/config-manager.d.ts +50 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +300 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/default-config.d.ts +4 -0
- package/dist/config/default-config.d.ts.map +1 -0
- package/dist/config/default-config.js +48 -0
- package/dist/config/default-config.js.map +1 -0
- package/dist/config/profiles.d.ts +16 -0
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +208 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/engine/filesystem.d.ts +52 -0
- package/dist/engine/filesystem.d.ts.map +1 -0
- package/dist/engine/filesystem.js +499 -0
- package/dist/engine/filesystem.js.map +1 -0
- package/dist/engine/http-client.d.ts +10 -0
- package/dist/engine/http-client.d.ts.map +1 -0
- package/dist/engine/http-client.js +93 -0
- package/dist/engine/http-client.js.map +1 -0
- package/dist/engine/pdf-generator.d.ts +15 -0
- package/dist/engine/pdf-generator.d.ts.map +1 -0
- package/dist/engine/pdf-generator.js +325 -0
- package/dist/engine/pdf-generator.js.map +1 -0
- package/dist/engine/powershell-runner.d.ts +23 -0
- package/dist/engine/powershell-runner.d.ts.map +1 -0
- package/dist/engine/powershell-runner.js +148 -0
- package/dist/engine/powershell-runner.js.map +1 -0
- package/dist/engine/ripgrep.d.ts +20 -0
- package/dist/engine/ripgrep.d.ts.map +1 -0
- package/dist/engine/ripgrep.js +173 -0
- package/dist/engine/ripgrep.js.map +1 -0
- package/dist/engine/system.d.ts +67 -0
- package/dist/engine/system.d.ts.map +1 -0
- package/dist/engine/system.js +558 -0
- package/dist/engine/system.js.map +1 -0
- package/dist/engine/task-manager.d.ts +66 -0
- package/dist/engine/task-manager.d.ts.map +1 -0
- package/dist/engine/task-manager.js +203 -0
- package/dist/engine/task-manager.js.map +1 -0
- package/dist/gateway/dashboard-html.d.ts +7 -0
- package/dist/gateway/dashboard-html.d.ts.map +1 -0
- package/dist/gateway/dashboard-html.js +624 -0
- package/dist/gateway/dashboard-html.js.map +1 -0
- package/dist/gateway/preview-html.d.ts +5 -0
- package/dist/gateway/preview-html.d.ts.map +1 -0
- package/dist/gateway/preview-html.js +351 -0
- package/dist/gateway/preview-html.js.map +1 -0
- package/dist/gateway/rate-limiter.d.ts +8 -0
- package/dist/gateway/rate-limiter.d.ts.map +1 -0
- package/dist/gateway/rate-limiter.js +51 -0
- package/dist/gateway/rate-limiter.js.map +1 -0
- package/dist/gateway/server.d.ts +18 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +337 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/sse-gateway.d.ts +43 -0
- package/dist/gateway/sse-gateway.d.ts.map +1 -0
- package/dist/gateway/sse-gateway.js +161 -0
- package/dist/gateway/sse-gateway.js.map +1 -0
- package/dist/gateway/streamable-gateway.d.ts +39 -0
- package/dist/gateway/streamable-gateway.d.ts.map +1 -0
- package/dist/gateway/streamable-gateway.js +176 -0
- package/dist/gateway/streamable-gateway.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +164 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/desktop-tools.d.ts +3 -0
- package/dist/tools/desktop-tools.d.ts.map +1 -0
- package/dist/tools/desktop-tools.js +280 -0
- package/dist/tools/desktop-tools.js.map +1 -0
- package/dist/tools/file-tools.d.ts +3 -0
- package/dist/tools/file-tools.d.ts.map +1 -0
- package/dist/tools/file-tools.js +433 -0
- package/dist/tools/file-tools.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/network-tools.d.ts +3 -0
- package/dist/tools/network-tools.d.ts.map +1 -0
- package/dist/tools/network-tools.js +108 -0
- package/dist/tools/network-tools.js.map +1 -0
- package/dist/tools/registry.d.ts +50 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +54 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/terminal-tools.d.ts +3 -0
- package/dist/tools/terminal-tools.d.ts.map +1 -0
- package/dist/tools/terminal-tools.js +152 -0
- package/dist/tools/terminal-tools.js.map +1 -0
- package/dist/tools/tool-wrapper.d.ts +7 -0
- package/dist/tools/tool-wrapper.d.ts.map +1 -0
- package/dist/tools/tool-wrapper.js +47 -0
- package/dist/tools/tool-wrapper.js.map +1 -0
- package/dist/types/index.d.ts +261 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/file-logger.d.ts +24 -0
- package/dist/utils/file-logger.d.ts.map +1 -0
- package/dist/utils/file-logger.js +117 -0
- package/dist/utils/file-logger.js.map +1 -0
- package/dist/utils/logger.d.ts +51 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +189 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/sanitizer.d.ts +13 -0
- package/dist/utils/sanitizer.d.ts.map +1 -0
- package/dist/utils/sanitizer.js +60 -0
- package/dist/utils/sanitizer.js.map +1 -0
- package/docs/EXAMPLES.md +171 -0
- package/docs/PROFILES.md +423 -0
- package/docs/SECURITY.md +190 -0
- package/docs/TOOLS.md +366 -0
- package/manifest.json +74 -0
- package/package.json +89 -0
- package/scripts/build-exe.ps1 +37 -0
- package/scripts/manage-service.ps1 +112 -0
- package/server.json +23 -0
- package/winhelm.config.example.json +30 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { registerAllTools } from "../tools/index.js";
|
|
5
|
+
export class StreamableGateway {
|
|
6
|
+
sessions = new Map();
|
|
7
|
+
cleanupTimer = null;
|
|
8
|
+
idleTimeoutMs;
|
|
9
|
+
maxSessions;
|
|
10
|
+
constructor(idleTimeoutMs = 45 * 60 * 1000, maxSessions = 100) {
|
|
11
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
12
|
+
this.maxSessions = maxSessions;
|
|
13
|
+
// Check periodically for idle sessions; unref timer so it doesn't prevent process exit
|
|
14
|
+
const intervalMs = Math.min(60000, Math.max(1000, Math.floor(this.idleTimeoutMs / 2)));
|
|
15
|
+
this.cleanupTimer = setInterval(() => {
|
|
16
|
+
this.cleanupIdleSessions();
|
|
17
|
+
}, intervalMs);
|
|
18
|
+
if (this.cleanupTimer && this.cleanupTimer.unref) {
|
|
19
|
+
this.cleanupTimer.unref();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
createMcpServerInstance() {
|
|
23
|
+
const server = new McpServer({
|
|
24
|
+
name: "winhelm-mcp",
|
|
25
|
+
version: "1.1.0",
|
|
26
|
+
});
|
|
27
|
+
registerAllTools(server);
|
|
28
|
+
return server;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Evict the least recently used session if capacity is reached
|
|
32
|
+
*/
|
|
33
|
+
evictOldestIfNeeded() {
|
|
34
|
+
while (this.sessions.size >= this.maxSessions) {
|
|
35
|
+
let oldestKey = null;
|
|
36
|
+
let oldestTime = Infinity;
|
|
37
|
+
for (const [key, session] of this.sessions.entries()) {
|
|
38
|
+
if (session.lastActivityAt < oldestTime) {
|
|
39
|
+
oldestTime = session.lastActivityAt;
|
|
40
|
+
oldestKey = key;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (oldestKey) {
|
|
44
|
+
const session = this.sessions.get(oldestKey);
|
|
45
|
+
this.sessions.delete(oldestKey);
|
|
46
|
+
if (session) {
|
|
47
|
+
session.mcpServer.close().catch(() => { });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Cleanup sessions that have been idle longer than idleTimeoutMs
|
|
57
|
+
*/
|
|
58
|
+
cleanupIdleSessions() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
let count = 0;
|
|
61
|
+
for (const [key, session] of this.sessions.entries()) {
|
|
62
|
+
if (now - session.lastActivityAt > this.idleTimeoutMs) {
|
|
63
|
+
this.sessions.delete(key);
|
|
64
|
+
session.mcpServer.close().catch(() => { });
|
|
65
|
+
count++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return count;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Handle ALL /mcp - Streamable HTTP transport
|
|
72
|
+
*/
|
|
73
|
+
async handleRequest(req, res) {
|
|
74
|
+
try {
|
|
75
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
76
|
+
// If an existing session ID is provided and active, use it.
|
|
77
|
+
// If provided but not found (evicted, idle timeout, or server restart),
|
|
78
|
+
// return 404 with JSON-RPC error immediately according to MCP specification.
|
|
79
|
+
if (sessionId) {
|
|
80
|
+
const session = this.sessions.get(sessionId);
|
|
81
|
+
if (!session) {
|
|
82
|
+
res.status(404).json({
|
|
83
|
+
jsonrpc: "2.0",
|
|
84
|
+
error: { code: -32001, message: "Session not found" },
|
|
85
|
+
id: null,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
session.lastActivityAt = Date.now();
|
|
90
|
+
await session.transport.handleRequest(req, res);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Create a new session for initialize requests
|
|
94
|
+
const mcpServer = this.createMcpServerInstance();
|
|
95
|
+
const transport = new StreamableHTTPServerTransport({
|
|
96
|
+
sessionIdGenerator: () => randomUUID(),
|
|
97
|
+
onsessioninitialized: (newSessionId) => {
|
|
98
|
+
this.evictOldestIfNeeded();
|
|
99
|
+
this.sessions.set(newSessionId, {
|
|
100
|
+
transport,
|
|
101
|
+
mcpServer,
|
|
102
|
+
lastActivityAt: Date.now(),
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
onsessionclosed: (closedSessionId) => {
|
|
106
|
+
const session = this.sessions.get(closedSessionId);
|
|
107
|
+
if (session) {
|
|
108
|
+
this.sessions.delete(closedSessionId);
|
|
109
|
+
session.mcpServer.close().catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// Set transport.onclose BEFORE mcpServer.connect(transport)
|
|
114
|
+
// so Protocol.connect preserves our handler alongside Protocol._onclose
|
|
115
|
+
transport.onclose = () => {
|
|
116
|
+
const sid = transport.sessionId;
|
|
117
|
+
if (sid) {
|
|
118
|
+
this.sessions.delete(sid);
|
|
119
|
+
}
|
|
120
|
+
mcpServer.close().catch(() => { });
|
|
121
|
+
};
|
|
122
|
+
await mcpServer.connect(transport);
|
|
123
|
+
await transport.handleRequest(req, res);
|
|
124
|
+
// If transport.sessionId is still undefined after handleRequest
|
|
125
|
+
// (not an initialize request or request was rejected/invalid), clean up immediately to prevent leaks
|
|
126
|
+
if (!transport.sessionId) {
|
|
127
|
+
await transport.close().catch(() => { });
|
|
128
|
+
await mcpServer.close().catch(() => { });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error("[StreamableHTTP] Error handling /mcp request:", err);
|
|
133
|
+
if (!res.headersSent) {
|
|
134
|
+
res.status(500).json({ error: "Internal server error", message: err.message });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getActiveSessionCount() {
|
|
139
|
+
return this.sessions.size;
|
|
140
|
+
}
|
|
141
|
+
getSession(sessionId) {
|
|
142
|
+
return this.sessions.get(sessionId);
|
|
143
|
+
}
|
|
144
|
+
setSessionLastActivity(sessionId, timestamp) {
|
|
145
|
+
const session = this.sessions.get(sessionId);
|
|
146
|
+
if (session) {
|
|
147
|
+
session.lastActivityAt = timestamp;
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
registerSessionForTest(sessionId, session = {}) {
|
|
153
|
+
this.evictOldestIfNeeded();
|
|
154
|
+
this.sessions.set(sessionId, {
|
|
155
|
+
transport: session.transport || {},
|
|
156
|
+
mcpServer: session.mcpServer || { close: async () => { } },
|
|
157
|
+
lastActivityAt: session.lastActivityAt ?? Date.now(),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async closeAll() {
|
|
161
|
+
if (this.cleanupTimer) {
|
|
162
|
+
clearInterval(this.cleanupTimer);
|
|
163
|
+
this.cleanupTimer = null;
|
|
164
|
+
}
|
|
165
|
+
for (const session of this.sessions.values()) {
|
|
166
|
+
try {
|
|
167
|
+
await session.mcpServer.close();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// ignore
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
this.sessions.clear();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=streamable-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamable-gateway.js","sourceRoot":"","sources":["../../src/gateway/streamable-gateway.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA+KnD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { ConfigManager } from "./config/config-manager.js";
|
|
5
|
+
import { createServer } from "./gateway/server.js";
|
|
6
|
+
import { isValidProfile, resolveCustomTools } from "./config/profiles.js";
|
|
7
|
+
import { registerAllTools } from "./tools/index.js";
|
|
8
|
+
// Parse CLI arguments
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
function getArg(flag, fallback) {
|
|
11
|
+
for (const arg of args) {
|
|
12
|
+
if (arg.startsWith(`${flag}=`)) {
|
|
13
|
+
return arg.slice(flag.length + 1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const idx = args.indexOf(flag);
|
|
17
|
+
return idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("-") ? args[idx + 1] : fallback;
|
|
18
|
+
}
|
|
19
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
+
console.log(`
|
|
21
|
+
WinHelm - Windows Native MCP Server
|
|
22
|
+
Usage:
|
|
23
|
+
winhelm [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--config <path> Load configuration from a specific JSON file (e.g. project-specific config)
|
|
27
|
+
--port <number> Port to listen on (default: 8788 or PORT env)
|
|
28
|
+
--host <string> Host interface (default: 127.0.0.1 or HOST env)
|
|
29
|
+
--profile, -p <name> Tool profile: minimal (6), core (15), dev (28), sysadmin (37), full (38) (default: full)
|
|
30
|
+
--stdio Run in stdio mode for local MCP clients (OpenAI tunnel-client, Claude, Cursor)
|
|
31
|
+
--transport <type> Transport mode: http (default) or stdio
|
|
32
|
+
--auth <token> Bearer authentication token (or MCP_AUTH_TOKEN env)
|
|
33
|
+
--read-only Enable read-only mode (block mutating actions)
|
|
34
|
+
--allowed-dirs <list> Comma-separated allowed directories (e.g. "D:\\mcp,C:\\Projects")
|
|
35
|
+
--tools <list> Explicit comma-separated tools to load or +tool/-tool modifiers
|
|
36
|
+
--no-persist Do not persist CLI overrides to config file (default behavior)
|
|
37
|
+
--persist Persist CLI overrides back to config file
|
|
38
|
+
--help, -h Show this help message
|
|
39
|
+
`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
async function main() {
|
|
43
|
+
const IS_STDIO = args.includes("--stdio") || getArg("--transport") === "stdio";
|
|
44
|
+
if (IS_STDIO) {
|
|
45
|
+
// In stdio mode, process.stdout is the JSON-RPC wire. All logs must go to stderr.
|
|
46
|
+
console.log = (...a) => console.error(...a);
|
|
47
|
+
}
|
|
48
|
+
const CONFIG_ARG = getArg("--config");
|
|
49
|
+
const NO_PERSIST = args.includes("--no-persist");
|
|
50
|
+
const configManager = ConfigManager.getInstance(CONFIG_ARG);
|
|
51
|
+
await configManager.init(CONFIG_ARG);
|
|
52
|
+
const PORT = parseInt(getArg("--port", process.env.PORT || "8788"), 10);
|
|
53
|
+
const HOST = getArg("--host", process.env.HOST || "127.0.0.1");
|
|
54
|
+
const AUTH_TOKEN = getArg("--auth", process.env.MCP_AUTH_TOKEN || undefined);
|
|
55
|
+
const ALLOWED_DIRS = getArg("--allowed-dirs", process.env.MCP_ALLOWED_DIRECTORIES || undefined);
|
|
56
|
+
const READ_ONLY = args.includes("--read-only") || process.env.MCP_READ_ONLY === "true" || process.env.MCP_READ_ONLY === "1";
|
|
57
|
+
const PROFILE_ARG = getArg("--profile", getArg("-p", process.env.WINHELM_PROFILE || undefined));
|
|
58
|
+
const TOOLS_ARG = getArg("--tools");
|
|
59
|
+
const updates = {};
|
|
60
|
+
if (AUTH_TOKEN)
|
|
61
|
+
updates.authToken = AUTH_TOKEN;
|
|
62
|
+
if (READ_ONLY)
|
|
63
|
+
updates.readOnly = true;
|
|
64
|
+
if (PROFILE_ARG) {
|
|
65
|
+
if (isValidProfile(PROFILE_ARG)) {
|
|
66
|
+
updates.profile = PROFILE_ARG;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.error(`[WinHelm] Unknown profile: "${PROFILE_ARG}". Valid profiles: minimal, core, dev, sysadmin, full, custom`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (TOOLS_ARG) {
|
|
74
|
+
const baseProfile = (updates.profile || configManager.getConfig().profile || "full");
|
|
75
|
+
updates.customTools = resolveCustomTools(baseProfile, TOOLS_ARG);
|
|
76
|
+
if (!updates.profile)
|
|
77
|
+
updates.profile = "custom";
|
|
78
|
+
}
|
|
79
|
+
if (ALLOWED_DIRS) {
|
|
80
|
+
updates.allowedDirectories = ALLOWED_DIRS.split(",")
|
|
81
|
+
.map((d) => d.trim())
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
}
|
|
84
|
+
if (Object.keys(updates).length > 0) {
|
|
85
|
+
// By default, CLI overrides are in-memory session-only unless --persist is explicitly passed
|
|
86
|
+
const shouldPersist = args.includes("--persist") && !NO_PERSIST;
|
|
87
|
+
await configManager.updateConfig(updates, shouldPersist);
|
|
88
|
+
}
|
|
89
|
+
// Security warning if bound to non-loopback with no authentication token
|
|
90
|
+
const isLoopback = HOST === "127.0.0.1" || HOST.toLowerCase() === "localhost" || HOST === "::1";
|
|
91
|
+
const effectiveAuthToken = AUTH_TOKEN || configManager.getConfig().authToken;
|
|
92
|
+
if (!isLoopback && !effectiveAuthToken && !IS_STDIO) {
|
|
93
|
+
console.warn("\n" + "=".repeat(78));
|
|
94
|
+
console.warn("⚠️ WARNING: Server is bound to a non-loopback address with NO authentication token.");
|
|
95
|
+
console.warn(` Interface: ${HOST}:${PORT}`);
|
|
96
|
+
console.warn(" Anyone on this network can execute commands on this machine.");
|
|
97
|
+
console.warn(" To protect your system, specify --auth <token> or set MCP_AUTH_TOKEN in environment.");
|
|
98
|
+
console.warn("=".repeat(78) + "\n");
|
|
99
|
+
}
|
|
100
|
+
if (IS_STDIO) {
|
|
101
|
+
const mcpServer = new McpServer({
|
|
102
|
+
name: "winhelm-mcp",
|
|
103
|
+
version: "1.1.0",
|
|
104
|
+
});
|
|
105
|
+
registerAllTools(mcpServer);
|
|
106
|
+
const transport = new StdioServerTransport();
|
|
107
|
+
await mcpServer.connect(transport);
|
|
108
|
+
console.error(`[WinHelm] Connected via stdio transport (Profile: ${configManager.getConfig().profile || "full"}).`);
|
|
109
|
+
const shutdown = async () => {
|
|
110
|
+
await mcpServer.close().catch(() => { });
|
|
111
|
+
process.exit(0);
|
|
112
|
+
};
|
|
113
|
+
process.on("SIGINT", shutdown);
|
|
114
|
+
process.on("SIGTERM", shutdown);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { start, stop } = createServer({
|
|
118
|
+
port: PORT,
|
|
119
|
+
host: HOST,
|
|
120
|
+
authToken: AUTH_TOKEN,
|
|
121
|
+
});
|
|
122
|
+
const server = await start();
|
|
123
|
+
let isShuttingDown = false;
|
|
124
|
+
const shutdown = async () => {
|
|
125
|
+
if (isShuttingDown) {
|
|
126
|
+
console.log("\n[WinHelm] Force exit.");
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
isShuttingDown = true;
|
|
130
|
+
console.log("\n[WinHelm] Shutting down server gracefully...");
|
|
131
|
+
// Safety timeout in case of any lingering event loop handles
|
|
132
|
+
const forceExitTimer = setTimeout(() => {
|
|
133
|
+
console.log("[WinHelm] Server stopped. Goodbye!");
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}, 1200);
|
|
136
|
+
forceExitTimer.unref();
|
|
137
|
+
try {
|
|
138
|
+
await stop(server);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// ignore
|
|
142
|
+
}
|
|
143
|
+
console.log("[WinHelm] Server stopped. Goodbye!");
|
|
144
|
+
process.exit(0);
|
|
145
|
+
};
|
|
146
|
+
process.on("SIGINT", shutdown);
|
|
147
|
+
process.on("SIGTERM", shutdown);
|
|
148
|
+
}
|
|
149
|
+
// Auto-run if executed directly or in standalone SEA binary
|
|
150
|
+
const argv1 = process.argv[1] || "";
|
|
151
|
+
const isSeaBinary = process.execPath.toLowerCase().endsWith(".exe") && !process.execPath.toLowerCase().endsWith("node.exe");
|
|
152
|
+
const isDirectEntry = isSeaBinary ||
|
|
153
|
+
argv1.endsWith("index.ts") ||
|
|
154
|
+
argv1.endsWith("index.js") ||
|
|
155
|
+
argv1.endsWith("winhelm") ||
|
|
156
|
+
(typeof import.meta !== "undefined" && import.meta?.url && argv1 && import.meta.url.includes(argv1.replace(/\\/g, "/")));
|
|
157
|
+
if (isDirectEntry) {
|
|
158
|
+
main().catch((err) => {
|
|
159
|
+
console.error("[WinHelm] Fatal error on startup:", err);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
export { createServer, ConfigManager };
|
|
164
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop-tools.d.ts","sourceRoot":"","sources":["../../src/tools/desktop-tools.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmBzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoU5D"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { captureScreen, checkPort, controlService, getClipboard, getGpuInfo, getServiceStatus, getSystemInfo, killProcess, listProcesses, listServices, openTarget, queryEventLog, sendNotification, setClipboard, } from "../engine/system.js";
|
|
3
|
+
import { registerTracedTool } from "./tool-wrapper.js";
|
|
4
|
+
export function registerDesktopTools(server) {
|
|
5
|
+
// 1. Clipboard Get
|
|
6
|
+
registerTracedTool(server, "clipboard_get", {}, async () => {
|
|
7
|
+
try {
|
|
8
|
+
const text = await getClipboard();
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: text || "(Clipboard is empty or contains non-text data)" }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: "text", text: `Failed to get clipboard: ${err.message}` }],
|
|
16
|
+
isError: true,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
// 2. Clipboard Set
|
|
21
|
+
registerTracedTool(server, "clipboard_set", {
|
|
22
|
+
text: z.string().describe("Text content to copy to Windows clipboard"),
|
|
23
|
+
}, async ({ text }) => {
|
|
24
|
+
try {
|
|
25
|
+
await setClipboard(text);
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{ type: "text", text: `Successfully copied ${text.length} characters to clipboard` },
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: "text", text: `Failed to set clipboard: ${err.message}` }],
|
|
35
|
+
isError: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// 3. Screen Capture
|
|
40
|
+
registerTracedTool(server, "screen_capture", {}, async () => {
|
|
41
|
+
try {
|
|
42
|
+
const base64 = await captureScreen();
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: "image",
|
|
47
|
+
data: base64,
|
|
48
|
+
mimeType: "image/png",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: "Screenshot captured successfully (primary display).",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: `Screenshot failed: ${err.message}` }],
|
|
60
|
+
isError: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// 4. System Open (URL, Folder, File)
|
|
65
|
+
registerTracedTool(server, "system_open", {
|
|
66
|
+
target: z.string().describe("URL, folder path, or file to open with default application"),
|
|
67
|
+
}, async ({ target }) => {
|
|
68
|
+
try {
|
|
69
|
+
const res = await openTarget(target);
|
|
70
|
+
return {
|
|
71
|
+
content: [{ type: "text", text: `Opened: ${res.target}` }],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: `Open failed: ${err.message}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// 5. System Info
|
|
82
|
+
registerTracedTool(server, "system_info", {}, async () => {
|
|
83
|
+
try {
|
|
84
|
+
const info = await getSystemInfo();
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: JSON.stringify(info, null, 2) }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
content: [{ type: "text", text: `Failed to get system info: ${err.message}` }],
|
|
92
|
+
isError: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
// 6. Process Kill
|
|
97
|
+
registerTracedTool(server, "process_kill", {
|
|
98
|
+
pid: z.number().optional().describe("Process ID to terminate"),
|
|
99
|
+
name: z.string().optional().describe("Process Name (e.g. 'node', 'notepad')"),
|
|
100
|
+
}, async ({ pid, name }) => {
|
|
101
|
+
try {
|
|
102
|
+
const res = await killProcess(pid, name);
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: JSON.stringify(res, null, 2) }],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
content: [{ type: "text", text: `Kill process failed: ${err.message}` }],
|
|
110
|
+
isError: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// 7. Port Check
|
|
115
|
+
registerTracedTool(server, "port_check", {
|
|
116
|
+
port: z.number().describe("TCP port number to check (e.g. 8788, 3000)"),
|
|
117
|
+
}, async ({ port }) => {
|
|
118
|
+
try {
|
|
119
|
+
const res = await checkPort(port);
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: JSON.stringify(res, null, 2) }],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text: `Port check failed: ${err.message}` }],
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
// 8. GPU Info
|
|
132
|
+
registerTracedTool(server, "gpu_info", {}, async () => {
|
|
133
|
+
try {
|
|
134
|
+
const info = await getGpuInfo();
|
|
135
|
+
return {
|
|
136
|
+
content: [{ type: "text", text: JSON.stringify(info, null, 2) }],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: `Failed to query GPU info: ${err.message}` }],
|
|
142
|
+
isError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// 9. Windows Services List
|
|
147
|
+
registerTracedTool(server, "service_list", {
|
|
148
|
+
filter: z.string().optional().describe("Filter services by name substring (e.g. 'docker', 'ssh', 'postgres')"),
|
|
149
|
+
}, async ({ filter }) => {
|
|
150
|
+
try {
|
|
151
|
+
const services = await listServices(filter);
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: services.length > 0
|
|
157
|
+
? JSON.stringify(services, null, 2)
|
|
158
|
+
: `No services matching filter "${filter || ""}"`,
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
return {
|
|
165
|
+
content: [{ type: "text", text: `Failed to list services: ${err.message}` }],
|
|
166
|
+
isError: true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// 10. Windows Service Status
|
|
171
|
+
registerTracedTool(server, "service_status", {
|
|
172
|
+
service_name: z.string().describe("Exact name of the Windows service (e.g. 'ssh-agent', 'wslservice')"),
|
|
173
|
+
}, async ({ service_name }) => {
|
|
174
|
+
try {
|
|
175
|
+
const status = await getServiceStatus(service_name);
|
|
176
|
+
return {
|
|
177
|
+
content: [{ type: "text", text: JSON.stringify(status, null, 2) }],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
return {
|
|
182
|
+
content: [{ type: "text", text: `Failed to get service status: ${err.message}` }],
|
|
183
|
+
isError: true,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// 11. Windows Service Control
|
|
188
|
+
registerTracedTool(server, "service_control", {
|
|
189
|
+
service_name: z.string().describe("Exact name of the Windows service to control"),
|
|
190
|
+
action: z.enum(["start", "stop", "restart"]).describe("Action to perform on the service"),
|
|
191
|
+
}, async ({ service_name, action }) => {
|
|
192
|
+
try {
|
|
193
|
+
const res = await controlService(service_name, action);
|
|
194
|
+
return {
|
|
195
|
+
content: [{ type: "text", text: JSON.stringify(res, null, 2) }],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
return {
|
|
200
|
+
content: [{ type: "text", text: `Failed to ${action} service: ${err.message}` }],
|
|
201
|
+
isError: true,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// 12. Process List & Top Processes
|
|
206
|
+
registerTracedTool(server, "process_list", {
|
|
207
|
+
limit: z.number().optional().describe("Maximum number of processes to return (default: 20, max: 100)"),
|
|
208
|
+
sort_by: z.enum(["cpu", "memory", "name", "pid"]).optional().describe("Sort processes by resource or property (default: memory)"),
|
|
209
|
+
filter: z.string().optional().describe("Filter process name or path substring"),
|
|
210
|
+
}, async ({ limit, sort_by, filter }) => {
|
|
211
|
+
try {
|
|
212
|
+
const procs = await listProcesses({
|
|
213
|
+
limit,
|
|
214
|
+
sortBy: sort_by,
|
|
215
|
+
filter,
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: "text", text: JSON.stringify(procs, null, 2) }],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
return {
|
|
223
|
+
content: [{ type: "text", text: `Failed to list processes: ${err.message}` }],
|
|
224
|
+
isError: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
// 13. Windows Event Log Query
|
|
229
|
+
registerTracedTool(server, "eventlog_query", {
|
|
230
|
+
log_name: z.string().optional().describe("Windows Event Log name (e.g. 'Application', 'System', default: 'Application')"),
|
|
231
|
+
level: z.enum(["Critical", "Error", "Warning", "Information", "All"]).optional().describe("Event severity level (default: Error)"),
|
|
232
|
+
hours: z.number().optional().describe("Query events from past N hours (default: 24)"),
|
|
233
|
+
source: z.string().optional().describe("Filter by provider or application source name"),
|
|
234
|
+
limit: z.number().optional().describe("Maximum number of events to return (default: 20, max: 100)"),
|
|
235
|
+
}, async ({ log_name, level, hours, source, limit }) => {
|
|
236
|
+
try {
|
|
237
|
+
const events = await queryEventLog({
|
|
238
|
+
logName: log_name,
|
|
239
|
+
level,
|
|
240
|
+
hours,
|
|
241
|
+
source,
|
|
242
|
+
limit,
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
content: [
|
|
246
|
+
{
|
|
247
|
+
type: "text",
|
|
248
|
+
text: events.length > 0 ? JSON.stringify(events, null, 2) : "[] (No matching events found)",
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
return {
|
|
255
|
+
content: [{ type: "text", text: `Failed to query event log: ${err.message}` }],
|
|
256
|
+
isError: true,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
// 14. Windows Toast Notification
|
|
261
|
+
registerTracedTool(server, "notification_send", {
|
|
262
|
+
title: z.string().describe("Toast notification title (e.g. 'Task Complete')"),
|
|
263
|
+
message: z.string().describe("Notification body text"),
|
|
264
|
+
sound: z.boolean().optional().describe("Play alert sound with notification (default: true)"),
|
|
265
|
+
}, async ({ title, message, sound }) => {
|
|
266
|
+
try {
|
|
267
|
+
const res = await sendNotification(title, message, sound !== false);
|
|
268
|
+
return {
|
|
269
|
+
content: [{ type: "text", text: `Notification sent: "${res.title}" - ${res.message}` }],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: "text", text: `Failed to send notification: ${err.message}` }],
|
|
275
|
+
isError: true,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=desktop-tools.js.map
|