@skuzadev/wpfpilot-mcp 0.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/README.md +25 -0
- package/bin/wpfpilot-mcp.js +120 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @skuzadev/wpfpilot-mcp
|
|
2
|
+
|
|
3
|
+
npx launcher for WpfPilot MCP.
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
npx -y @skuzadev/wpfpilot-mcp
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
MCP config:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"wpfpilot-mcp": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@skuzadev/wpfpilot-mcp"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The launcher downloads the latest `wpfpilot-mcp-win-x64.zip` release from `skuzadev/wpfpilot-mcp` on first run and caches it under `%LOCALAPPDATA%\\WpfPilot\\npm`.
|
|
23
|
+
|
|
24
|
+
WpfPilot MCP is Windows-only.
|
|
25
|
+
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createReadStream, createWriteStream, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { chmod, rename } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { Readable } from "node:stream";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { pipeline } from "node:stream/promises";
|
|
11
|
+
import unzipStream from "unzip-stream";
|
|
12
|
+
|
|
13
|
+
const { Extract } = unzipStream;
|
|
14
|
+
|
|
15
|
+
const REPO = process.env.WPFPILOT_REPO || "skuzadev/wpfpilot-mcp";
|
|
16
|
+
const VERSION = process.env.WPFPILOT_VERSION || "latest";
|
|
17
|
+
const ASSET = "wpfpilot-mcp-win-x64.zip";
|
|
18
|
+
const INSTALL_DIR =
|
|
19
|
+
process.env.WPFPILOT_INSTALL_DIR ||
|
|
20
|
+
join(process.env.LOCALAPPDATA || process.env.USERPROFILE || dirname(fileURLToPath(import.meta.url)), "WpfPilot", "npm");
|
|
21
|
+
const EXE = join(INSTALL_DIR, "wpfpilot-mcp.exe");
|
|
22
|
+
|
|
23
|
+
if (process.platform !== "win32") {
|
|
24
|
+
console.error("WpfPilot MCP supports Windows only.");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function apiUrl() {
|
|
29
|
+
return VERSION === "latest"
|
|
30
|
+
? `https://api.github.com/repos/${REPO}/releases/latest`
|
|
31
|
+
: `https://api.github.com/repos/${REPO}/releases/tags/${VERSION}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function fetchJson(url) {
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
headers: {
|
|
37
|
+
"User-Agent": "wpfpilot-mcp-npm"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Request failed: ${response.status} ${response.statusText} (${url})`);
|
|
42
|
+
}
|
|
43
|
+
return response.json();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function download(url, destination) {
|
|
47
|
+
const response = await fetch(url, {
|
|
48
|
+
headers: {
|
|
49
|
+
"User-Agent": "wpfpilot-mcp-npm"
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok || !response.body) {
|
|
53
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText} (${url})`);
|
|
54
|
+
}
|
|
55
|
+
await pipeline(Readable.fromWeb(response.body), createWriteStream(destination));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function ensureInstalled() {
|
|
59
|
+
if (existsSync(EXE)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
mkdirSync(INSTALL_DIR, { recursive: true });
|
|
64
|
+
const release = await fetchJson(apiUrl());
|
|
65
|
+
const asset = release.assets?.find((item) => item.name === ASSET);
|
|
66
|
+
if (!asset) {
|
|
67
|
+
throw new Error(`Release asset '${ASSET}' was not found in ${REPO} ${VERSION}.`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const tempRoot = join(tmpdir(), `wpfpilot-${process.pid}-${Date.now()}`);
|
|
71
|
+
const zipPath = join(tempRoot, ASSET);
|
|
72
|
+
const extractDir = join(tempRoot, "extract");
|
|
73
|
+
mkdirSync(extractDir, { recursive: true });
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await download(asset.browser_download_url, zipPath);
|
|
77
|
+
await pipeline(createReadStream(zipPath), Extract({ path: extractDir }));
|
|
78
|
+
rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
79
|
+
mkdirSync(INSTALL_DIR, { recursive: true });
|
|
80
|
+
await moveDirectoryContents(extractDir, INSTALL_DIR);
|
|
81
|
+
if (!existsSync(EXE)) {
|
|
82
|
+
throw new Error(`Install completed, but '${EXE}' was not found.`);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await chmod(EXE, 0o755);
|
|
86
|
+
} catch {
|
|
87
|
+
// Windows usually does not need chmod.
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function moveDirectoryContents(from, to) {
|
|
95
|
+
const { readdir } = await import("node:fs/promises");
|
|
96
|
+
for (const entry of await readdir(from)) {
|
|
97
|
+
await rename(join(from, entry), join(to, entry));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function main() {
|
|
102
|
+
await ensureInstalled();
|
|
103
|
+
const child = spawn(EXE, process.argv.slice(2), {
|
|
104
|
+
stdio: "inherit",
|
|
105
|
+
windowsHide: true
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
child.on("exit", (code, signal) => {
|
|
109
|
+
if (signal) {
|
|
110
|
+
process.kill(process.pid, signal);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
process.exit(code ?? 0);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
main().catch((error) => {
|
|
118
|
+
console.error(`[wpfpilot-mcp] ${error.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skuzadev/wpfpilot-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "npx launcher for WpfPilot MCP, a local MCP server for Windows WPF automation.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"wpfpilot-mcp": "./bin/wpfpilot-mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"package.json"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"os": [
|
|
19
|
+
"win32"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"wpf",
|
|
25
|
+
"automation",
|
|
26
|
+
"flaui",
|
|
27
|
+
"testing"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"unzip-stream": "^0.3.4"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/skuzadev/wpfpilot-mcp.git",
|
|
35
|
+
"directory": "packages/npm"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/skuzadev/wpfpilot-mcp/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/skuzadev/wpfpilot-mcp#readme",
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
44
|
+
}
|