@smithery/cli 1.0.4 → 1.0.6
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 +3 -0
- package/dist/index.js +65 -61
- package/package.json +6 -1
- package/dist/commands/inspect.js +0 -60
- package/dist/commands/install.js +0 -21
- package/dist/commands/installed.js +0 -50
- package/dist/commands/uninstall.js +0 -44
- package/dist/commands/view.js +0 -31
- package/dist/constants.js +0 -1
- package/dist/types/registry.js +0 -29
- package/dist/utils/client-utils.js +0 -93
- package/dist/utils/config-manager.js +0 -120
- package/dist/utils/registry-utils.js +0 -83
- package/dist/utils/runtime-utils.js +0 -141
- package/dist/utils/server-actions.js +0 -43
- package/dist/utils/server-display.js +0 -80
- package/dist/utils/server-inspector.js +0 -124
- package/dist/utils/server-manager.js +0 -37
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { exec } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
import inquirer from "inquirer";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
const execAsync = promisify(exec);
|
|
6
|
-
export async function checkUVInstalled() {
|
|
7
|
-
try {
|
|
8
|
-
await execAsync("uvx --version");
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export async function promptForUVInstall(inquirerInstance) {
|
|
16
|
-
const { shouldInstall } = await inquirerInstance.prompt([
|
|
17
|
-
{
|
|
18
|
-
type: "confirm",
|
|
19
|
-
name: "shouldInstall",
|
|
20
|
-
message: "UV package manager is required for Python MCP servers. Would you like to install it?",
|
|
21
|
-
default: true,
|
|
22
|
-
},
|
|
23
|
-
]);
|
|
24
|
-
if (!shouldInstall) {
|
|
25
|
-
console.warn(chalk.yellow("UV installation was declined. You can install it manually from https://astral.sh/uv"));
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
console.log("Installing uv package manager...");
|
|
29
|
-
try {
|
|
30
|
-
await execAsync("curl -LsSf https://astral.sh/uv/install.sh | sh");
|
|
31
|
-
console.log(chalk.green("✓ UV installed successfully"));
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.warn(chalk.yellow("Failed to install UV. You can install it manually from https://astral.sh/uv"));
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export async function collectConfigValues(connection) {
|
|
40
|
-
const promptsMap = new Map();
|
|
41
|
-
// Process config schema if it exists
|
|
42
|
-
if (connection.configSchema?.properties) {
|
|
43
|
-
const required = new Set(connection.configSchema.required || []);
|
|
44
|
-
Object.entries(connection.configSchema.properties).forEach(([key, prop]) => {
|
|
45
|
-
const schemaProp = prop;
|
|
46
|
-
promptsMap.set(key, {
|
|
47
|
-
key,
|
|
48
|
-
description: schemaProp.description || `Enter value for ${key}`,
|
|
49
|
-
required: required.has(key),
|
|
50
|
-
default: schemaProp.default,
|
|
51
|
-
type: schemaProp.type,
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
const configValues = {};
|
|
56
|
-
function convertValueToType(value, type) {
|
|
57
|
-
if (!type)
|
|
58
|
-
return value;
|
|
59
|
-
// Handle empty string inputs
|
|
60
|
-
if (value === "") {
|
|
61
|
-
switch (type) {
|
|
62
|
-
case "array":
|
|
63
|
-
return [];
|
|
64
|
-
case "number":
|
|
65
|
-
case "integer":
|
|
66
|
-
return null;
|
|
67
|
-
default:
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (!value)
|
|
72
|
-
return value;
|
|
73
|
-
switch (type) {
|
|
74
|
-
case "boolean":
|
|
75
|
-
return String(value).toLowerCase() === "true";
|
|
76
|
-
case "number":
|
|
77
|
-
return Number(value);
|
|
78
|
-
case "integer":
|
|
79
|
-
return Number.parseInt(String(value), 10);
|
|
80
|
-
case "array":
|
|
81
|
-
// Parse comma-separated string into array
|
|
82
|
-
return String(value)
|
|
83
|
-
.split(",")
|
|
84
|
-
.map((item) => item.trim())
|
|
85
|
-
.filter((item) => item !== ""); // Remove empty items
|
|
86
|
-
default:
|
|
87
|
-
return value;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
// Iterate through each configuration prompt
|
|
91
|
-
for (const prompt of promptsMap.values()) {
|
|
92
|
-
// If env var exists and setting is optional, ask if user wants to reuse it
|
|
93
|
-
if (process.env[prompt.key] && !prompt.required) {
|
|
94
|
-
const { reuseExisting } = await inquirer.prompt([
|
|
95
|
-
{
|
|
96
|
-
type: "confirm",
|
|
97
|
-
name: "reuseExisting",
|
|
98
|
-
message: `Found ${prompt.key} in environment. Use it?`,
|
|
99
|
-
default: true,
|
|
100
|
-
},
|
|
101
|
-
]);
|
|
102
|
-
if (reuseExisting) {
|
|
103
|
-
configValues[prompt.key] = convertValueToType(process.env[prompt.key], prompt.type);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
const requiredText = prompt.required
|
|
108
|
-
? chalk.red(" (required)")
|
|
109
|
-
: chalk.gray(" (optional)");
|
|
110
|
-
const promptType = prompt.key.toLowerCase().includes("key")
|
|
111
|
-
? "password" // Use password type for any field containing 'key'
|
|
112
|
-
: prompt.type === "boolean"
|
|
113
|
-
? "confirm"
|
|
114
|
-
: prompt.type === "array"
|
|
115
|
-
? "input"
|
|
116
|
-
: prompt.type === "number" || prompt.type === "integer"
|
|
117
|
-
? "number"
|
|
118
|
-
: "input";
|
|
119
|
-
const { value } = await inquirer.prompt([
|
|
120
|
-
{
|
|
121
|
-
type: promptType,
|
|
122
|
-
name: "value",
|
|
123
|
-
message: `${prompt.description}${requiredText}${prompt.type === "array" ? " (comma-separated)" : ""}`,
|
|
124
|
-
default: prompt.default,
|
|
125
|
-
mask: promptType === "password" ? "*" : undefined, // Add masking for password fields
|
|
126
|
-
validate: (input) => {
|
|
127
|
-
if (prompt.required && !input)
|
|
128
|
-
return false;
|
|
129
|
-
if (prompt.type === "number" || prompt.type === "integer") {
|
|
130
|
-
return !Number.isNaN(Number(input)) || "Please enter a valid number";
|
|
131
|
-
}
|
|
132
|
-
return true;
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
]);
|
|
136
|
-
if (value !== undefined || prompt.default !== undefined) {
|
|
137
|
-
configValues[prompt.key] = convertValueToType(value ?? prompt.default, prompt.type);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return configValues;
|
|
141
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { ServerManager } from "./server-manager.js";
|
|
3
|
-
import { displayServerDetails, confirmUninstall } from "./server-display.js";
|
|
4
|
-
const serverManager = new ServerManager();
|
|
5
|
-
export async function handleServerAction(server, action, handlers, showActionsAfter = true, client = "claude") {
|
|
6
|
-
switch (action) {
|
|
7
|
-
case "install":
|
|
8
|
-
console.log(chalk.cyan(`\nPreparing to install ${server.name}...`));
|
|
9
|
-
await serverManager.installServer(server, client);
|
|
10
|
-
server.isInstalled = true;
|
|
11
|
-
if (handlers.onInstall) {
|
|
12
|
-
await handlers.onInstall(server);
|
|
13
|
-
}
|
|
14
|
-
console.log(chalk.green(`\nSuccessfully installed ${server.name}`));
|
|
15
|
-
return; // Exit after successful installation
|
|
16
|
-
case "uninstall":
|
|
17
|
-
if (await confirmUninstall(server.name)) {
|
|
18
|
-
await serverManager.uninstallServer(server.id, client);
|
|
19
|
-
console.log(chalk.green(`Successfully uninstalled ${server.name}`));
|
|
20
|
-
server.isInstalled = false;
|
|
21
|
-
if (handlers.onUninstall) {
|
|
22
|
-
await handlers.onUninstall(server);
|
|
23
|
-
}
|
|
24
|
-
return; // Exit after successful uninstallation
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
console.log("Uninstallation cancelled.");
|
|
28
|
-
}
|
|
29
|
-
break;
|
|
30
|
-
case "back":
|
|
31
|
-
if (handlers.onBack) {
|
|
32
|
-
await handlers.onBack();
|
|
33
|
-
}
|
|
34
|
-
return;
|
|
35
|
-
case "exit":
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
// Show actions again after completing an action (except for exit/back)
|
|
39
|
-
if (showActionsAfter) {
|
|
40
|
-
const nextAction = await displayServerDetails(server);
|
|
41
|
-
await handleServerAction(server, nextAction, handlers, showActionsAfter, client);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import inquirer from "inquirer";
|
|
3
|
-
export function formatServerChoice(server, showInstallStatus = false) {
|
|
4
|
-
const prefix = showInstallStatus ? (server.isInstalled ? "✓ " : " ") : "";
|
|
5
|
-
return {
|
|
6
|
-
name: `${prefix}${server.name} | ${server.id}`,
|
|
7
|
-
value: server,
|
|
8
|
-
short: server.name,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export function createListChoices(servers, includeBack = true, includeExit = true) {
|
|
12
|
-
const choices = servers.map((server) => formatServerChoice(server, true));
|
|
13
|
-
if (includeBack || includeExit) {
|
|
14
|
-
choices.push(new inquirer.Separator());
|
|
15
|
-
}
|
|
16
|
-
if (includeBack) {
|
|
17
|
-
choices.push({
|
|
18
|
-
name: chalk.yellow("↩ Back"),
|
|
19
|
-
value: "back",
|
|
20
|
-
short: "Back",
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
if (includeExit) {
|
|
24
|
-
choices.push({
|
|
25
|
-
name: chalk.red("✖ Exit"),
|
|
26
|
-
value: "exit",
|
|
27
|
-
short: "Exit",
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return choices;
|
|
31
|
-
}
|
|
32
|
-
export function printServerListHeader(count, type = "all") {
|
|
33
|
-
console.log(chalk.bold.cyan(`\n📦 ${type === "installed" ? "Installed Servers" : "Available Servers"}`));
|
|
34
|
-
console.log(chalk.gray(`Found ${count} ${type === "installed" ? "installed " : ""}servers\n`));
|
|
35
|
-
}
|
|
36
|
-
export async function displayServerDetails(server, includeBack = true) {
|
|
37
|
-
console.log(`\n${chalk.bold.cyan("Server Details:")}`);
|
|
38
|
-
console.log(chalk.bold("ID: ") + server.id);
|
|
39
|
-
console.log(chalk.bold("Name: ") + server.name);
|
|
40
|
-
const choices = [
|
|
41
|
-
{
|
|
42
|
-
name: server.isInstalled
|
|
43
|
-
? chalk.yellow("🔄 Reinstall this server")
|
|
44
|
-
: chalk.yellow("📦 Install this server"),
|
|
45
|
-
value: "install",
|
|
46
|
-
},
|
|
47
|
-
...(server.isInstalled
|
|
48
|
-
? [
|
|
49
|
-
{
|
|
50
|
-
name: chalk.yellow("🗑️ Uninstall this server"),
|
|
51
|
-
value: "uninstall",
|
|
52
|
-
},
|
|
53
|
-
]
|
|
54
|
-
: []),
|
|
55
|
-
...(includeBack
|
|
56
|
-
? [{ name: chalk.yellow("↩ Back to list"), value: "back" }]
|
|
57
|
-
: []),
|
|
58
|
-
{ name: chalk.red("✖ Exit"), value: "exit" },
|
|
59
|
-
];
|
|
60
|
-
const { action } = await inquirer.prompt([
|
|
61
|
-
{
|
|
62
|
-
type: "list",
|
|
63
|
-
name: "action",
|
|
64
|
-
message: "What would you like to do?",
|
|
65
|
-
choices,
|
|
66
|
-
},
|
|
67
|
-
]);
|
|
68
|
-
return action;
|
|
69
|
-
}
|
|
70
|
-
export async function confirmUninstall(serverName) {
|
|
71
|
-
const { confirm } = await inquirer.prompt([
|
|
72
|
-
{
|
|
73
|
-
type: "confirm",
|
|
74
|
-
name: "confirm",
|
|
75
|
-
message: `Are you sure you want to uninstall ${serverName}?`,
|
|
76
|
-
default: false,
|
|
77
|
-
},
|
|
78
|
-
]);
|
|
79
|
-
return confirm;
|
|
80
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
-
import inquirer from "inquirer";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
export async function createServerConnection(id, config) {
|
|
6
|
-
// console.log('Creating server connection with config:', JSON.stringify(config, null, 2));
|
|
7
|
-
if (!config) {
|
|
8
|
-
throw new Error(`No connection configuration found for server ${id}`);
|
|
9
|
-
}
|
|
10
|
-
const transport = new StdioClientTransport({
|
|
11
|
-
command: process.platform === "win32" ? "npx.cmd" : "/usr/local/bin/npx",
|
|
12
|
-
args: config.args || [],
|
|
13
|
-
env: {
|
|
14
|
-
...process.env,
|
|
15
|
-
...(config.env || {}),
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
const client = new Client({ name: "mcp-cli", version: "1.0.0" }, { capabilities: {} });
|
|
19
|
-
await client.connect(transport);
|
|
20
|
-
return client;
|
|
21
|
-
}
|
|
22
|
-
export async function listServerPrimitives(client) {
|
|
23
|
-
const capabilities = client.getServerCapabilities() || {};
|
|
24
|
-
const primitives = [];
|
|
25
|
-
const promises = [];
|
|
26
|
-
if (capabilities.resources) {
|
|
27
|
-
promises.push(client.listResources().then(({ resources }) => {
|
|
28
|
-
resources.forEach((item) => primitives.push({ type: "resource", value: item }));
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
if (capabilities.tools) {
|
|
32
|
-
promises.push(client.listTools().then(({ tools }) => {
|
|
33
|
-
tools.forEach((item) => primitives.push({ type: "tool", value: item }));
|
|
34
|
-
}));
|
|
35
|
-
}
|
|
36
|
-
if (capabilities.prompts) {
|
|
37
|
-
promises.push(client.listPrompts().then(({ prompts }) => {
|
|
38
|
-
prompts.forEach((item) => primitives.push({ type: "prompt", value: item }));
|
|
39
|
-
}));
|
|
40
|
-
}
|
|
41
|
-
await Promise.all(promises);
|
|
42
|
-
return primitives;
|
|
43
|
-
}
|
|
44
|
-
export async function selectPrimitive(primitives) {
|
|
45
|
-
const truncateDescription = (desc, maxLength = 100) => {
|
|
46
|
-
if (!desc)
|
|
47
|
-
return "No description";
|
|
48
|
-
return desc.length > maxLength ? `${desc.slice(0, maxLength)}...` : desc;
|
|
49
|
-
};
|
|
50
|
-
const choices = [
|
|
51
|
-
...primitives.map((p) => ({
|
|
52
|
-
name: `${chalk.green(`${p.type}(${p.value.name})`)} - ${chalk.gray(truncateDescription(p.value.description || ""))}`,
|
|
53
|
-
value: p,
|
|
54
|
-
description: p.value.description,
|
|
55
|
-
})),
|
|
56
|
-
new inquirer.Separator(),
|
|
57
|
-
{
|
|
58
|
-
name: chalk.yellow("↩ Back"),
|
|
59
|
-
value: "back",
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: chalk.red("✖ Exit"),
|
|
63
|
-
value: "exit",
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
const { selected } = await inquirer.prompt([
|
|
67
|
-
{
|
|
68
|
-
type: "list",
|
|
69
|
-
name: "selected",
|
|
70
|
-
message: "Pick a primitive",
|
|
71
|
-
choices,
|
|
72
|
-
pageSize: 10,
|
|
73
|
-
},
|
|
74
|
-
]);
|
|
75
|
-
return selected;
|
|
76
|
-
}
|
|
77
|
-
export async function inspectServer(client) {
|
|
78
|
-
const primitives = await listServerPrimitives(client);
|
|
79
|
-
while (true) {
|
|
80
|
-
const selected = await selectPrimitive(primitives);
|
|
81
|
-
if (selected === "back") {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
if (selected === "exit") {
|
|
85
|
-
return "exit";
|
|
86
|
-
}
|
|
87
|
-
// Pretty print the specification with colors
|
|
88
|
-
const spec = {
|
|
89
|
-
type: selected.type,
|
|
90
|
-
value: {
|
|
91
|
-
name: selected.value.name,
|
|
92
|
-
description: selected.value.description,
|
|
93
|
-
...(selected.type === "tool" && {
|
|
94
|
-
inputSchema: (await client.listTools({ name: selected.value.name }))
|
|
95
|
-
?.tools?.[0]?.inputSchema,
|
|
96
|
-
}),
|
|
97
|
-
...(selected.value.uri && { uri: selected.value.uri }),
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
console.log("\n", chalk.cyan(JSON.stringify(spec, null, 2)
|
|
101
|
-
.replace(/"(\w+)":/g, (match) => chalk.green(match))
|
|
102
|
-
.replace(/"([^"]+)"(?=,|\n|\})/g, (match) => chalk.yellow(match))));
|
|
103
|
-
const { action } = await inquirer.prompt([
|
|
104
|
-
{
|
|
105
|
-
type: "list",
|
|
106
|
-
name: "action",
|
|
107
|
-
message: "What would you like to do?",
|
|
108
|
-
choices: [
|
|
109
|
-
{
|
|
110
|
-
name: chalk.yellow("↩ Back to primitives"),
|
|
111
|
-
value: "back",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: chalk.red("✖ Exit"),
|
|
115
|
-
value: "exit",
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
},
|
|
119
|
-
]);
|
|
120
|
-
if (action === "exit") {
|
|
121
|
-
return "exit";
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { ConfigManager } from "./config-manager.js";
|
|
2
|
-
import { getServerConfiguration } from "./registry-utils.js";
|
|
3
|
-
import { promptForRestart } from "./client-utils.js";
|
|
4
|
-
import { collectConfigValues } from "./runtime-utils.js";
|
|
5
|
-
export class ServerManager {
|
|
6
|
-
constructor(configManager = ConfigManager) {
|
|
7
|
-
this.configManager = configManager;
|
|
8
|
-
}
|
|
9
|
-
validateConnection(server) {
|
|
10
|
-
const connection = server.connections?.[0];
|
|
11
|
-
if (!connection) {
|
|
12
|
-
throw new Error("No connection configuration found");
|
|
13
|
-
}
|
|
14
|
-
return connection;
|
|
15
|
-
}
|
|
16
|
-
async installServer(server, client) {
|
|
17
|
-
const connection = this.validateConnection(server);
|
|
18
|
-
const values = await collectConfigValues(connection);
|
|
19
|
-
const serverConfig = await getServerConfiguration(server.id, values, connection.type);
|
|
20
|
-
if (!serverConfig) {
|
|
21
|
-
throw new Error(`Unable to fetch server configuration for server ${server.id}`);
|
|
22
|
-
}
|
|
23
|
-
await this.configManager.installServer(server.id, serverConfig, client);
|
|
24
|
-
await promptForRestart(client);
|
|
25
|
-
}
|
|
26
|
-
async uninstallServer(serverId, client) {
|
|
27
|
-
try {
|
|
28
|
-
await this.configManager.uninstallServer(serverId, client);
|
|
29
|
-
console.log(`\nUninstalled ${serverId}`);
|
|
30
|
-
await promptForRestart(client);
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
console.error("Failed to uninstall server:", error);
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|