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.
- package/dist/cli.js +200 -0
- 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
|
+
}
|