@smithery/cli 1.0.5 → 1.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithery/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "commonjs",
5
5
  "private": false,
6
6
  "homepage": "https://smithery.ai/",
@@ -22,7 +22,9 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.0.3",
25
+ "@smithery/sdk": "^0.0.22",
25
26
  "chalk": "^4.1.2",
27
+ "eventsource": "^3.0.2",
26
28
  "inquirer": "^8.2.4",
27
29
  "inquirer-autocomplete-prompt": "^2.0.0"
28
30
  },
@@ -47,5 +49,8 @@
47
49
  ".": {
48
50
  "import": "./dist/index.js"
49
51
  }
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
50
55
  }
51
56
  }
@@ -1,60 +0,0 @@
1
- import chalk from "chalk";
2
- import { createServerConnection, inspectServer, } from "../utils/server-inspector.js";
3
- import { ConfigManager } from "../utils/config-manager.js";
4
- import { createListChoices } from "../utils/server-display.js";
5
- import inquirer from "inquirer";
6
- import { VALID_CLIENTS } from "../constants.js";
7
- export async function inspect(client) {
8
- try {
9
- // ensure client is valid
10
- if (client && !VALID_CLIENTS.includes(client)) {
11
- console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
12
- process.exit(1);
13
- }
14
- const installedIds = ConfigManager.getInstalledServerIds(client);
15
- if (installedIds.length === 0) {
16
- console.log(chalk.yellow("\nNo MCP servers are currently installed."));
17
- return;
18
- }
19
- const config = ConfigManager.readConfig(client);
20
- while (true) {
21
- const choices = createListChoices(installedIds.map((id) => ({
22
- id,
23
- name: ConfigManager.denormalizeServerId(id),
24
- isInstalled: true,
25
- connections: [],
26
- })), false, true);
27
- const { selectedId } = await inquirer.prompt([
28
- {
29
- type: "list",
30
- name: "selectedId",
31
- message: "Select a server to inspect:",
32
- choices,
33
- },
34
- ]);
35
- if (selectedId === "exit") {
36
- process.exit(0);
37
- }
38
- if (!selectedId) {
39
- return;
40
- }
41
- console.log(chalk.blue("\nConnecting to server..."));
42
- const connectionConfig = config.mcpServers[selectedId.id];
43
- if ("command" in connectionConfig) {
44
- const client = await createServerConnection(selectedId.id, connectionConfig);
45
- const result = await inspectServer(client);
46
- if (result === "exit") {
47
- process.exit(0);
48
- }
49
- }
50
- else {
51
- throw new Error("Only stdio connections are supported");
52
- }
53
- }
54
- }
55
- catch (error) {
56
- console.error(chalk.red("Error during inspection:"));
57
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
58
- process.exit(1);
59
- }
60
- }
@@ -1,21 +0,0 @@
1
- import chalk from "chalk";
2
- import { ServerManager } from "../utils/server-manager.js";
3
- import { resolveServer } from "../utils/registry-utils.js";
4
- import { VALID_CLIENTS } from "../constants.js";
5
- const serverManager = new ServerManager();
6
- export async function install(serverId, client) {
7
- // ensure client is valid
8
- if (client && !VALID_CLIENTS.includes(client)) {
9
- console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
10
- process.exit(1);
11
- }
12
- // get package details and connection template
13
- const server = await resolveServer(serverId, client);
14
- if (!server) {
15
- console.error(chalk.red(`Server '${serverId}' not found in registry`));
16
- process.exit(1);
17
- }
18
- // install server using the serverManager instance
19
- await serverManager.installServer(server, client);
20
- console.log(chalk.green(`✓ Successfully installed package '${serverId}' for ${client}`));
21
- }
@@ -1,50 +0,0 @@
1
- import inquirer from "inquirer";
2
- import chalk from "chalk";
3
- import { fetchServers } from "../utils/registry-utils.js";
4
- import AutocompletePrompt from "inquirer-autocomplete-prompt";
5
- import { handleServerAction } from "../utils/server-actions.js";
6
- import { ConfigManager } from "../utils/config-manager.js";
7
- import { displayServerDetails, printServerListHeader, createListChoices, } from "../utils/server-display.js";
8
- import { VALID_CLIENTS } from "../constants.js";
9
- inquirer.registerPrompt("autocomplete", AutocompletePrompt);
10
- let installedServersCache = null;
11
- export async function listInstalledServers(client) {
12
- // ensure client is valid
13
- if (client && !VALID_CLIENTS.includes(client)) {
14
- console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
15
- process.exit(1);
16
- }
17
- const installedIds = ConfigManager.getInstalledServerIds(client);
18
- if (installedIds.length === 0) {
19
- console.log(chalk.yellow("\nNo MCP servers are currently installed."));
20
- return;
21
- }
22
- const denormalizedIds = installedIds.map((id) => ConfigManager.denormalizeServerId(id));
23
- if (!installedServersCache ||
24
- !areArraysEqual(denormalizedIds, installedServersCache.map((server) => server.id))) {
25
- installedServersCache = await fetchServers(client, denormalizedIds);
26
- installedServersCache.forEach((server) => {
27
- server.isInstalled = true;
28
- });
29
- }
30
- printServerListHeader(installedServersCache.length, "installed");
31
- const prompt = {
32
- type: "list",
33
- name: "selectedServer",
34
- message: "Search and select a server:",
35
- choices: createListChoices(installedServersCache, false, true),
36
- };
37
- const answer = await inquirer.prompt([prompt]);
38
- if (!answer.selectedServer || answer.selectedServer === "exit") {
39
- return;
40
- }
41
- const action = await displayServerDetails(answer.selectedServer);
42
- await handleServerAction(answer.selectedServer, action, {
43
- onUninstall: () => listInstalledServers(client),
44
- onBack: () => listInstalledServers(client),
45
- }, true, client);
46
- }
47
- function areArraysEqual(arr1, arr2) {
48
- return (arr1.length === arr2.length &&
49
- arr1.every((value, index) => value === arr2[index]));
50
- }
@@ -1,44 +0,0 @@
1
- import chalk from "chalk";
2
- import inquirer from "inquirer";
3
- import { ServerManager } from "../utils/server-manager.js";
4
- import { VALID_CLIENTS } from "../constants.js";
5
- const serverManager = new ServerManager();
6
- export async function uninstall(serverId, client) {
7
- try {
8
- // ensure client is valid
9
- if (client && !VALID_CLIENTS.includes(client)) {
10
- console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
11
- process.exit(1);
12
- }
13
- // If no server name provided, show error
14
- if (!serverId) {
15
- console.error(chalk.red("Error: Server ID is required"));
16
- console.log("Usage: @smithery/cli uninstall <server-id>");
17
- process.exit(1);
18
- }
19
- // Confirm uninstallation
20
- const { confirmUninstall } = await inquirer.prompt([
21
- {
22
- type: "confirm",
23
- name: "confirmUninstall",
24
- message: `Are you sure you want to uninstall ${serverId}?`,
25
- default: false,
26
- },
27
- ]);
28
- if (!confirmUninstall) {
29
- console.log("Uninstallation cancelled.");
30
- return;
31
- }
32
- // Perform uninstallation
33
- await serverManager.uninstallServer(serverId, client);
34
- console.log(chalk.green(`\nSuccessfully uninstalled ${serverId} for ${client}`));
35
- if (client === "claude") {
36
- console.log(chalk.yellow("\nNote: Please restart Claude for the changes to take effect."));
37
- }
38
- }
39
- catch (error) {
40
- console.error(chalk.red("Failed to uninstall server:"));
41
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
42
- process.exit(1);
43
- }
44
- }
@@ -1,31 +0,0 @@
1
- import chalk from "chalk";
2
- import { resolveServer } from "../utils/registry-utils.js";
3
- import { handleServerAction } from "../utils/server-actions.js";
4
- import { displayServerDetails } from "../utils/server-display.js";
5
- import { VALID_CLIENTS } from "../constants.js";
6
- export async function get(serverId, client) {
7
- try {
8
- // ensure client is valid
9
- if (client && !VALID_CLIENTS.includes(client)) {
10
- console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
11
- process.exit(1);
12
- }
13
- const server = await resolveServer(serverId, client);
14
- if (!server) {
15
- console.log(chalk.yellow(`No server found with ID: ${serverId}`));
16
- return;
17
- }
18
- const action = await displayServerDetails(server, false);
19
- await handleServerAction(server, action, {}, false, client);
20
- }
21
- catch (error) {
22
- console.error(chalk.red("Error loading server:"));
23
- if (error instanceof Error && error.message.includes("fetch")) {
24
- console.error(chalk.red("Failed to connect to the registry. Please check your internet connection."));
25
- }
26
- else {
27
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
28
- }
29
- process.exit(1);
30
- }
31
- }
package/dist/constants.js DELETED
@@ -1 +0,0 @@
1
- export const VALID_CLIENTS = ["claude", "cline", "roo-cline"];
@@ -1,29 +0,0 @@
1
- /**
2
- * Types for entries in the Smithery registry
3
- */
4
- import { z } from "zod";
5
- export const JSONSchemaSchema = z.lazy(() => z.object({
6
- type: z.string().optional(),
7
- properties: z.record(JSONSchemaSchema).optional(),
8
- items: JSONSchemaSchema.optional(),
9
- required: z.array(z.string()).optional(),
10
- description: z.string().optional(),
11
- default: z.unknown().optional(),
12
- }));
13
- export const ConnectionDetailsSchema = z.object({
14
- type: z.enum(["stdio"]),
15
- configSchema: JSONSchemaSchema.optional(),
16
- exampleConfig: z.record(z.any()).optional(),
17
- });
18
- // stdio connection
19
- export const StdioConnectionSchema = z.object({
20
- command: z.string().describe("The executable to run to start the server."),
21
- args: z
22
- .array(z.string())
23
- .optional()
24
- .describe("Command line arguments to pass to the executable."),
25
- env: z
26
- .record(z.string(), z.string())
27
- .optional()
28
- .describe("The environment to use when spawning the process."),
29
- });
@@ -1,93 +0,0 @@
1
- import { exec } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import inquirer from "inquirer";
4
- const execAsync = promisify(exec);
5
- async function isClientRunning(client) {
6
- if (!client)
7
- return false;
8
- try {
9
- const platform = process.platform;
10
- // Map of supported clients to their process names
11
- // Currently only Claude is officially supported
12
- // Other entries are placeholders for future integrations
13
- const clientProcess = {
14
- claude: "Claude",
15
- // jan: "Jan", // Placeholder for future client integration
16
- // Add more clients here as they become supported
17
- }[client] || client;
18
- if (platform === "win32") {
19
- const { stdout } = await execAsync(`tasklist /FI "IMAGENAME eq ${clientProcess}.exe" /NH`);
20
- return stdout.includes(`${clientProcess}.exe`);
21
- }
22
- else if (platform === "darwin") {
23
- const { stdout } = await execAsync(`pgrep -x "${clientProcess}"`);
24
- return !!stdout.trim();
25
- }
26
- else if (platform === "linux") {
27
- const { stdout } = await execAsync(`pgrep -f "${clientProcess.toLowerCase()}"`);
28
- return !!stdout.trim();
29
- }
30
- return false;
31
- }
32
- catch (error) {
33
- // If the command fails, assume client is not running
34
- return false;
35
- }
36
- }
37
- async function restartClient(client) {
38
- const clientProcess = {
39
- claude: "Claude",
40
- jan: "Jan",
41
- // Add more clients here
42
- }[client] || client;
43
- try {
44
- const platform = process.platform;
45
- if (platform === "win32") {
46
- await execAsync(`taskkill /F /IM "${clientProcess}.exe" && start "" "${clientProcess}.exe"`);
47
- }
48
- else if (platform === "darwin") {
49
- await execAsync(`killall "${clientProcess}" && open -a "${clientProcess}"`);
50
- }
51
- else if (platform === "linux") {
52
- await execAsync(`pkill -f "${clientProcess.toLowerCase()}" && ${clientProcess.toLowerCase()}`);
53
- }
54
- // Wait a moment for the app to close before reopening
55
- await new Promise((resolve) => setTimeout(resolve, 2000));
56
- // Reopen the app
57
- if (platform === "win32") {
58
- await execAsync(`start "" "${clientProcess}.exe"`);
59
- }
60
- else if (platform === "darwin") {
61
- await execAsync(`open -a "${clientProcess}"`);
62
- }
63
- else if (platform === "linux") {
64
- await execAsync(clientProcess.toLowerCase());
65
- }
66
- console.log(`${clientProcess} has been restarted.`);
67
- }
68
- catch (error) {
69
- console.error(`Failed to restart ${clientProcess}:`, error);
70
- }
71
- }
72
- export async function promptForRestart(client) {
73
- if (!client)
74
- return false;
75
- // Check if client is running first
76
- const isRunning = await isClientRunning(client);
77
- if (!isRunning) {
78
- return false;
79
- }
80
- const { shouldRestart } = await inquirer.prompt([
81
- {
82
- type: "confirm",
83
- name: "shouldRestart",
84
- message: `Would you like to restart the ${client} app to apply changes?`,
85
- default: true,
86
- },
87
- ]);
88
- if (shouldRestart) {
89
- console.log(`Restarting ${client} app...`);
90
- await restartClient(client);
91
- }
92
- return shouldRestart;
93
- }
@@ -1,120 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- // biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
5
- export class ConfigManager {
6
- static getConfigPath(client) {
7
- const normalizedClient = client?.toLowerCase() || "claude";
8
- return (ConfigManager.clientPaths[normalizedClient] ||
9
- path.join(path.dirname(ConfigManager.configPath), "..", client || "claude", `${normalizedClient}_config.json`));
10
- }
11
- static readConfig(client) {
12
- try {
13
- const configPath = ConfigManager.getConfigPath(client);
14
- if (!fs.existsSync(configPath)) {
15
- return { mcpServers: {} };
16
- }
17
- const rawConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
18
- return {
19
- mcpServers: rawConfig.mcpServers || {},
20
- };
21
- }
22
- catch (error) {
23
- return { mcpServers: {} };
24
- }
25
- }
26
- static writeConfig(config, client) {
27
- const configPath = ConfigManager.getConfigPath(client);
28
- const configDir = path.dirname(configPath);
29
- if (!fs.existsSync(configDir)) {
30
- fs.mkdirSync(configDir, { recursive: true });
31
- }
32
- if (!config.mcpServers || typeof config.mcpServers !== "object") {
33
- throw new Error("Invalid config structure");
34
- }
35
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
36
- }
37
- static isServerInstalled(id, client) {
38
- const config = ConfigManager.readConfig(client);
39
- const normalizedId = ConfigManager.normalizeServerId(id);
40
- return normalizedId in config.mcpServers;
41
- }
42
- static async installServer(id, serverConfig, client) {
43
- const normalizedId = ConfigManager.normalizeServerId(id);
44
- const config = ConfigManager.readConfig(client);
45
- config.mcpServers[normalizedId] = serverConfig;
46
- ConfigManager.writeConfig(config, client);
47
- }
48
- static async uninstallServer(id, client) {
49
- const normalizedId = ConfigManager.normalizeServerId(id);
50
- const config = ConfigManager.readConfig(client);
51
- if (!config.mcpServers[normalizedId]) {
52
- console.log(`Server ${normalizedId} not found in configuration`);
53
- return;
54
- }
55
- delete config.mcpServers[normalizedId];
56
- ConfigManager.writeConfig(config, client);
57
- }
58
- static getServerConfig(id, client) {
59
- const config = ConfigManager.readConfig(client);
60
- return config.mcpServers[id] || null;
61
- }
62
- static envVarsToArgs(envVars) {
63
- return Object.entries(envVars).flatMap(([key, value]) => {
64
- const argName = key.toLowerCase().replace(/_/g, "-");
65
- return [`--${argName}`, value];
66
- });
67
- }
68
- static normalizeServerId(serverId) {
69
- if (serverId.startsWith("@")) {
70
- const firstSlashIndex = serverId.indexOf("/");
71
- if (firstSlashIndex !== -1) {
72
- return `${serverId.substring(0, firstSlashIndex)}-${serverId.substring(firstSlashIndex + 1)}`;
73
- }
74
- }
75
- return serverId;
76
- }
77
- static denormalizeServerId(normalizedId) {
78
- if (normalizedId.startsWith("@")) {
79
- const dashIndex = normalizedId.indexOf("-");
80
- if (dashIndex !== -1) {
81
- return `${normalizedId.substring(0, dashIndex)}/${normalizedId.substring(dashIndex + 1)}`;
82
- }
83
- }
84
- return normalizedId;
85
- }
86
- // get locally installed servers
87
- static getInstalledServerIds(client) {
88
- const config = ConfigManager.readConfig(client);
89
- const ids = Object.keys(config.mcpServers || {});
90
- return ids;
91
- }
92
- }
93
- (() => {
94
- const homeDir = os.homedir();
95
- // Define platform-specific base directories
96
- const platformPaths = {
97
- win32: {
98
- baseDir: process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
99
- vscodePath: path.join("Code", "User", "globalStorage"),
100
- },
101
- darwin: {
102
- baseDir: path.join(homeDir, "Library", "Application Support"),
103
- vscodePath: path.join("Code", "User", "globalStorage"),
104
- },
105
- linux: {
106
- baseDir: process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
107
- vscodePath: path.join("Code/User/globalStorage"),
108
- },
109
- };
110
- const platform = process.platform;
111
- const { baseDir, vscodePath } = platformPaths[platform];
112
- // Define client paths using the platform-specific base directories
113
- const clientPaths = {
114
- claude: path.join(baseDir, "Claude", "claude_desktop_config.json"),
115
- cline: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
116
- "roo-cline": path.join(baseDir, vscodePath, "rooveterinaryinc.roo-cline", "settings", "cline_mcp_settings.json"),
117
- };
118
- ConfigManager.configPath = clientPaths.claude;
119
- ConfigManager.clientPaths = clientPaths;
120
- })();
@@ -1,83 +0,0 @@
1
- import dotenv from "dotenv";
2
- import { ConfigManager } from "./config-manager.js";
3
- dotenv.config();
4
- export const REGISTRY_ENDPOINT = process.env.REGISTRY_ENDPOINT || "https://registry.smithery.ai";
5
- export async function fetchServers(client, serverIds = []) {
6
- try {
7
- if (serverIds.length === 0) {
8
- return [];
9
- }
10
- // Fetch all servers in parallel
11
- const serverPromises = serverIds.map((id) => resolveServer(id, client));
12
- const resolvedServers = await Promise.all(serverPromises);
13
- // Filter out null results
14
- return resolvedServers.filter((server) => server !== null);
15
- }
16
- catch (error) {
17
- throw new Error(`Failed to resolve servers: ${error instanceof Error ? error.message : String(error)}`);
18
- }
19
- }
20
- // Resolves a single servers by ID from registry or local installation
21
- // Returns null if server cannot be found in either location
22
- export async function resolveServer(serverId, client) {
23
- try {
24
- // Check if server is installed first
25
- // const config = ConfigManager.readConfig()
26
- const isInstalled = ConfigManager.isServerInstalled(serverId, client);
27
- const response = await fetch(`${REGISTRY_ENDPOINT}/servers/${serverId}`);
28
- if (!response.ok) {
29
- // If server is installed but not in registry, return basic info
30
- if (isInstalled) {
31
- return {
32
- id: serverId,
33
- name: serverId,
34
- connections: [],
35
- isInstalled: true,
36
- client: client,
37
- };
38
- }
39
- return null;
40
- }
41
- const registryServer = await response.json();
42
- const resolvedServer = {
43
- id: registryServer.qualifiedName,
44
- name: registryServer.displayName,
45
- connections: registryServer.connections,
46
- isInstalled: isInstalled, // Use the checked installation status
47
- client: client,
48
- };
49
- return resolvedServer;
50
- }
51
- catch (error) {
52
- return null;
53
- }
54
- }
55
- export async function getServerConfiguration(serverId, configValues, connectionType = "stdio") {
56
- try {
57
- const requestBody = {
58
- connectionType,
59
- config: configValues,
60
- };
61
- const response = await fetch(`${REGISTRY_ENDPOINT}/servers/${serverId}`, {
62
- method: "POST",
63
- headers: {
64
- "Content-Type": "application/json",
65
- },
66
- body: JSON.stringify(requestBody),
67
- });
68
- try {
69
- const parsed = await response.json();
70
- return parsed.result;
71
- }
72
- catch (parseError) {
73
- const errorMessage = parseError instanceof Error
74
- ? parseError.message
75
- : "Unknown parsing error";
76
- throw new Error(`Invalid JSON response from registry: ${errorMessage}`);
77
- }
78
- }
79
- catch (error) {
80
- console.error("Error getting server configuration:", error);
81
- return null;
82
- }
83
- }