@askahuman/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.
Files changed (3) hide show
  1. package/bin/cli.js +31 -0
  2. package/install.js +109 -0
  3. package/package.json +38 -0
package/bin/cli.js ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ // Thin shim: exec the downloaded `ask-a-human` binary, passing through argv and
3
+ // inheriting stdio (the MCP server speaks JSON-RPC over stdin/stdout). The
4
+ // binary is fetched by install.js into ../bin. ref. install.js
5
+ "use strict";
6
+
7
+ const path = require("path");
8
+ const fs = require("fs");
9
+ const { spawnSync } = require("child_process");
10
+
11
+ const binName = process.platform === "win32" ? "ask-a-human.exe" : "ask-a-human";
12
+ const binPath = path.join(__dirname, "..", "bin", binName);
13
+
14
+ if (!fs.existsSync(binPath)) {
15
+ console.error(
16
+ `@askahuman/mcp: binary not found at ${binPath}. ` +
17
+ "Reinstall (npm i -g @askahuman/mcp) or run install.js to fetch it.",
18
+ );
19
+ process.exit(1);
20
+ }
21
+
22
+ // Default to `serve` so a bare `npx @askahuman/mcp` starts the MCP server.
23
+ const args = process.argv.slice(2);
24
+ if (args.length === 0) args.push("serve");
25
+
26
+ const res = spawnSync(binPath, args, { stdio: "inherit" });
27
+ if (res.error) {
28
+ console.error(`@askahuman/mcp: ${res.error.message}`);
29
+ process.exit(1);
30
+ }
31
+ process.exit(res.status === null ? 1 : res.status);
package/install.js ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ // postinstall: download the prebuilt `ask-a-human` binary matching this host
3
+ // from the GitHub Release tagged v<package.version>, then extract it into bin/.
4
+ // Asset naming must match .goreleaser.yaml: ask-a-human_<os>_<arch>.<ext>.
5
+ // Zero npm deps on purpose — uses node builtins + the OS tar/unzip.
6
+ "use strict";
7
+
8
+ const fs = require("fs");
9
+ const os = require("os");
10
+ const path = require("path");
11
+ const https = require("https");
12
+ const http = require("http");
13
+ const { execFileSync } = require("child_process");
14
+
15
+ const REPO = "askahuman/askahuman";
16
+ const { version } = require("./package.json");
17
+
18
+ const OS_MAP = { darwin: "darwin", linux: "linux", win32: "windows" };
19
+ const ARCH_MAP = { x64: "amd64", arm64: "arm64" };
20
+
21
+ function fail(msg) {
22
+ console.error(`@askahuman/mcp: ${msg}`);
23
+ console.error(
24
+ "Install the binary manually from " +
25
+ `https://github.com/${REPO}/releases or build from source (backend/cmd/agent), ` +
26
+ "then point your MCP config at it directly.",
27
+ );
28
+ process.exit(1);
29
+ }
30
+
31
+ const goos = OS_MAP[process.platform];
32
+ const goarch = ARCH_MAP[process.arch];
33
+ if (!goos || !goarch) fail(`unsupported platform ${process.platform}/${process.arch}`);
34
+
35
+ const ext = goos === "windows" ? "zip" : "tar.gz";
36
+ const asset = `ask-a-human_${goos}_${goarch}.${ext}`;
37
+ const base = process.env.AAH_BINARY_BASEURL || `https://github.com/${REPO}/releases/download/v${version}`;
38
+ const url = `${base}/${asset}`;
39
+
40
+ const binDir = path.join(__dirname, "bin");
41
+ const binName = goos === "windows" ? "ask-a-human.exe" : "ask-a-human";
42
+ const binPath = path.join(binDir, binName);
43
+
44
+ if (process.env.AAH_SKIP_DOWNLOAD) {
45
+ console.log("@askahuman/mcp: AAH_SKIP_DOWNLOAD set, skipping binary download.");
46
+ process.exit(0);
47
+ }
48
+
49
+ function download(u, dest, redirects = 0) {
50
+ return new Promise((resolve, reject) => {
51
+ if (redirects > 10) return reject(new Error("too many redirects"));
52
+ const client = u.startsWith("http://") ? http : https;
53
+ client
54
+ .get(u, { headers: { "User-Agent": "@askahuman/mcp-installer" } }, (res) => {
55
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
56
+ res.resume();
57
+ return resolve(download(res.headers.location, dest, redirects + 1));
58
+ }
59
+ if (res.statusCode !== 200) {
60
+ res.resume();
61
+ return reject(new Error(`HTTP ${res.statusCode} for ${u}`));
62
+ }
63
+ const out = fs.createWriteStream(dest);
64
+ res.pipe(out);
65
+ out.on("finish", () => out.close(resolve));
66
+ out.on("error", reject);
67
+ })
68
+ .on("error", reject);
69
+ });
70
+ }
71
+
72
+ function extract(archive, destDir) {
73
+ if (ext === "zip") {
74
+ // Windows 10+ ships tar.exe (bsdtar) which reads zip; fall back to PowerShell.
75
+ try {
76
+ execFileSync("tar", ["-xf", archive, "-C", destDir], { stdio: "inherit" });
77
+ } catch {
78
+ execFileSync(
79
+ "powershell",
80
+ ["-NoProfile", "-Command", `Expand-Archive -Force -Path '${archive}' -DestinationPath '${destDir}'`],
81
+ { stdio: "inherit" },
82
+ );
83
+ }
84
+ return;
85
+ }
86
+ execFileSync("tar", ["-xzf", archive, "-C", destDir], { stdio: "inherit" });
87
+ }
88
+
89
+ (async () => {
90
+ fs.mkdirSync(binDir, { recursive: true });
91
+ const tmp = path.join(os.tmpdir(), `aah-${process.pid}-${asset}`);
92
+ try {
93
+ console.log(`@askahuman/mcp: downloading ${asset} ...`);
94
+ await download(url, tmp);
95
+ // ponytail: no integrity check on the downloaded archive. Upgrade path —
96
+ // also fetch checksums.txt from the same release and verify sha256(tmp)
97
+ // before extracting. ref. .goreleaser.yaml (checksum block).
98
+ extract(tmp, binDir);
99
+ if (!fs.existsSync(binPath)) fail(`binary ${binName} not found in archive`);
100
+ if (goos !== "windows") fs.chmodSync(binPath, 0o755);
101
+ console.log("@askahuman/mcp: installed.");
102
+ } catch (e) {
103
+ fail(`download/extract failed: ${e.message}`);
104
+ } finally {
105
+ try {
106
+ fs.unlinkSync(tmp);
107
+ } catch {}
108
+ }
109
+ })();
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@askahuman/mcp",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Copy-paste MCP server: let your AI agent ask a human for approval on their phone. End-to-end encrypted, no accounts, no database.",
8
+ "keywords": [
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "claude",
12
+ "cursor",
13
+ "agent",
14
+ "human-in-the-loop",
15
+ "approval"
16
+ ],
17
+ "homepage": "https://ask-a-human.ai",
18
+ "bugs": "https://github.com/askahuman/askahuman/issues",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/askahuman/askahuman.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "askahuman",
25
+ "bin": {
26
+ "askahuman": "bin/cli.js"
27
+ },
28
+ "files": [
29
+ "bin/cli.js",
30
+ "install.js"
31
+ ],
32
+ "scripts": {
33
+ "postinstall": "node install.js"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ }
38
+ }