@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.
@@ -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
- }