mcpknife 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 (2) hide show
  1. package/dist/cli.js +200 -0
  2. package/package.json +27 -0
package/dist/cli.js ADDED
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { createRequire as createRequire2 } from "node:module";
5
+ import { readFileSync as readFileSync3 } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import path3 from "node:path";
8
+
9
+ // src/config.ts
10
+ import { readFileSync } from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ function readConfigFile(filePath) {
14
+ let content;
15
+ try {
16
+ content = readFileSync(filePath, "utf-8");
17
+ } catch (err) {
18
+ if (err.code === "ENOENT") {
19
+ return {};
20
+ }
21
+ throw err;
22
+ }
23
+ if (content.trim() === "") {
24
+ return {};
25
+ }
26
+ let parsed;
27
+ try {
28
+ parsed = JSON.parse(content);
29
+ } catch (err) {
30
+ throw new Error(`mcpknife: invalid JSON in ${filePath}: ${err.message}`);
31
+ }
32
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
33
+ return {};
34
+ }
35
+ return parsed;
36
+ }
37
+ function loadConfig(options) {
38
+ const homeDir = options?.homeDir ?? os.homedir();
39
+ const cwd = options?.cwd ?? process.cwd();
40
+ const userConfigPath = path.join(homeDir, ".mcpkniferc");
41
+ const projectConfigPath = path.join(cwd, ".mcpkniferc");
42
+ const userConfig = readConfigFile(userConfigPath);
43
+ const projectConfig = readConfigFile(projectConfigPath);
44
+ return { ...userConfig, ...projectConfig };
45
+ }
46
+
47
+ // src/resolve.ts
48
+ import { createRequire } from "node:module";
49
+ import { readFileSync as readFileSync2 } from "node:fs";
50
+ import path2 from "node:path";
51
+ var BINARY_MAP = {
52
+ boot: "mcpboot",
53
+ mod: "mcpblox",
54
+ ui: "mcp-gen-ui"
55
+ };
56
+ var require2 = createRequire(import.meta.url);
57
+ function resolveBinary(subcommand2) {
58
+ const packageName = BINARY_MAP[subcommand2];
59
+ if (!packageName) {
60
+ throw new Error(`mcpknife: unknown subcommand '${subcommand2}'`);
61
+ }
62
+ let pkgJsonPath;
63
+ try {
64
+ pkgJsonPath = require2.resolve(`${packageName}/package.json`);
65
+ } catch {
66
+ throw new Error(
67
+ `mcpknife: package '${packageName}' not found. Try reinstalling: npm install -g mcpknife`
68
+ );
69
+ }
70
+ const pkgRoot = path2.dirname(pkgJsonPath);
71
+ const pkgJson = JSON.parse(readFileSync2(pkgJsonPath, "utf-8"));
72
+ const binEntry = typeof pkgJson.bin === "string" ? pkgJson.bin : pkgJson.bin[packageName] || pkgJson.bin[Object.keys(pkgJson.bin)[0]];
73
+ if (!binEntry) {
74
+ throw new Error(
75
+ `mcpknife: package '${packageName}' has no bin entry in its package.json`
76
+ );
77
+ }
78
+ return path2.resolve(pkgRoot, binEntry);
79
+ }
80
+
81
+ // src/args.ts
82
+ var CONFIG_FLAG_MAP = [
83
+ { configKey: "provider", flag: "--provider", isBoolean: false },
84
+ { configKey: "model", flag: "--model", isBoolean: false },
85
+ { configKey: "apiKey", flag: "--api-key", isBoolean: false },
86
+ { configKey: "verbose", flag: "--verbose", isBoolean: true }
87
+ ];
88
+ function hasFlag(argv2, flag) {
89
+ return argv2.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
90
+ }
91
+ function buildArgv(config2, rawArgv2) {
92
+ const injected = [];
93
+ for (const { configKey, flag, isBoolean } of CONFIG_FLAG_MAP) {
94
+ const value = config2[configKey];
95
+ if (value === void 0 || value === false) continue;
96
+ if (hasFlag(rawArgv2, flag)) continue;
97
+ if (isBoolean) {
98
+ injected.push(flag);
99
+ } else {
100
+ injected.push(flag, String(value));
101
+ }
102
+ }
103
+ return [...injected, ...rawArgv2];
104
+ }
105
+
106
+ // src/spawn.ts
107
+ import { spawn } from "node:child_process";
108
+ function spawnTool(binaryPath2, argv2) {
109
+ const child = spawn(process.execPath, [binaryPath2, ...argv2], {
110
+ stdio: "inherit",
111
+ env: process.env
112
+ });
113
+ function forwardSignal(signal) {
114
+ process.on(signal, () => {
115
+ if (!child.killed) {
116
+ child.kill(signal);
117
+ }
118
+ });
119
+ }
120
+ forwardSignal("SIGINT");
121
+ forwardSignal("SIGTERM");
122
+ child.on("error", (err) => {
123
+ if (err.code === "ENOENT") {
124
+ console.error(`mcpknife: binary not found: ${binaryPath2}`);
125
+ console.error(`Try reinstalling: npm install -g mcpknife`);
126
+ } else {
127
+ console.error(`mcpknife: failed to start: ${err.message}`);
128
+ }
129
+ process.exit(1);
130
+ });
131
+ child.on("close", (code, signal) => {
132
+ if (signal) {
133
+ process.kill(process.pid, signal);
134
+ } else {
135
+ process.exit(code ?? 1);
136
+ }
137
+ });
138
+ }
139
+
140
+ // src/cli.ts
141
+ var __filename = fileURLToPath(import.meta.url);
142
+ var __dirname = path3.dirname(__filename);
143
+ var require3 = createRequire2(import.meta.url);
144
+ var pkgPath = path3.resolve(__dirname, "..", "package.json");
145
+ var version = "0.1.0";
146
+ try {
147
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
148
+ version = pkg.version;
149
+ } catch {
150
+ }
151
+ function printHelp() {
152
+ console.log(`mcpknife \u2014 unified CLI for the MCP power-tool suite
153
+
154
+ Usage:
155
+ mcpknife <command> [options]
156
+
157
+ Commands:
158
+ boot Generate an MCP server from a prompt and API docs
159
+ mod Transform tools on an existing MCP server
160
+ ui Add interactive UI to an MCP server
161
+
162
+ Options:
163
+ --help Show this help message
164
+ --version Show version number
165
+
166
+ Configuration:
167
+ mcpknife reads defaults from ~/.mcpkniferc and ./.mcpkniferc (JSON).
168
+ Supported fields: provider, model, apiKey, verbose.
169
+ CLI flags override config file values.
170
+
171
+ Examples:
172
+ mcpknife boot --prompt "Hacker News API" https://github.com/HackerNews/API
173
+ mcpknife mod --upstream "npx some-server" --prompt "hide write tools"
174
+ mcpknife ui --upstream-url http://localhost:3000/mcp
175
+
176
+ # Full pipeline
177
+ mcpknife boot --prompt "Yahoo Finance" | mcpknife mod --prompt "combine tools" | mcpknife ui
178
+
179
+ Run 'mcpknife <command> --help' for command-specific options.`);
180
+ }
181
+ var args = process.argv.slice(2);
182
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
183
+ printHelp();
184
+ process.exit(0);
185
+ }
186
+ if (args[0] === "--version" || args[0] === "-V") {
187
+ console.log(`mcpknife v${version}`);
188
+ process.exit(0);
189
+ }
190
+ var subcommand = args[0];
191
+ var rawArgv = args.slice(1);
192
+ if (!BINARY_MAP[subcommand]) {
193
+ console.error(`mcpknife: unknown subcommand '${subcommand}'`);
194
+ console.error(`Run 'mcpknife --help' for usage`);
195
+ process.exit(1);
196
+ }
197
+ var config = loadConfig();
198
+ var binaryPath = resolveBinary(subcommand);
199
+ var argv = buildArgv(config, rawArgv);
200
+ spawnTool(binaryPath, argv);
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "mcpknife",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "mcpknife": "dist/cli.js"
7
+ },
8
+ "scripts": {
9
+ "build": "node esbuild.config.js",
10
+ "test": "vitest run",
11
+ "dev": "tsx src/cli.ts"
12
+ },
13
+ "dependencies": {
14
+ "mcpboot": "^0.1.0",
15
+ "mcpblox": "^0.1.1",
16
+ "mcp-gen-ui": "^0.1.2"
17
+ },
18
+ "devDependencies": {
19
+ "esbuild": "^0.25.0",
20
+ "typescript": "^5.4.0",
21
+ "vitest": "^3.0.0",
22
+ "tsx": "^4.0.0"
23
+ },
24
+ "files": [
25
+ "dist/"
26
+ ]
27
+ }