@smithery/cli 0.0.3 → 0.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/README.md +4 -3
- package/dist/commands/install.js +11 -5
- package/dist/index.js +4 -1
- package/dist/types/analytics.js +1 -0
- package/dist/utils/analytics.js +44 -0
- package/dist/utils/config-manager.js +29 -20
- package/dist/utils/config.js +1 -0
- package/dist/utils/display.js +1 -0
- package/dist/utils/package-management.js +75 -55
- package/dist/utils/package-resolver.js +29 -39
- package/dist/utils/ui.js +23 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# smithery/
|
|
1
|
+
# smithery/cli
|
|
2
2
|
|
|
3
3
|
A lightweight registry and package manager for Model Context Protocol (MCP) servers, designed to be client-agnostic.
|
|
4
4
|
|
|
@@ -35,10 +35,11 @@ npx .
|
|
|
35
35
|
|
|
36
36
|
### Common Commands
|
|
37
37
|
|
|
38
|
-
Note: yet to fix 'npx . list' to list all packages
|
|
39
|
-
|
|
40
38
|
Test specific functionality:
|
|
41
39
|
```bash
|
|
40
|
+
# List all packages
|
|
41
|
+
npx . list
|
|
42
|
+
|
|
42
43
|
# Get details about a specific package
|
|
43
44
|
npx . get <package-id>
|
|
44
45
|
|
package/dist/commands/install.js
CHANGED
|
@@ -2,6 +2,7 @@ import { installPackage as installPkg } from "../utils/package-management.js";
|
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { resolvePackage } from "../utils/package-resolver.js";
|
|
5
|
+
import { VALID_CLIENTS } from "../utils/config.js";
|
|
5
6
|
async function promptForRuntime() {
|
|
6
7
|
const { runtime } = await inquirer.prompt([
|
|
7
8
|
{
|
|
@@ -16,7 +17,7 @@ async function promptForRuntime() {
|
|
|
16
17
|
]);
|
|
17
18
|
return runtime;
|
|
18
19
|
}
|
|
19
|
-
function createUnknownPackage(id, runtime) {
|
|
20
|
+
function createUnknownPackage(id, runtime, client) {
|
|
20
21
|
return {
|
|
21
22
|
id,
|
|
22
23
|
name: id,
|
|
@@ -28,6 +29,7 @@ function createUnknownPackage(id, runtime) {
|
|
|
28
29
|
license: "",
|
|
29
30
|
isInstalled: false,
|
|
30
31
|
isVerified: false,
|
|
32
|
+
client: client,
|
|
31
33
|
connections: [
|
|
32
34
|
{
|
|
33
35
|
stdio: {
|
|
@@ -42,10 +44,14 @@ function createUnknownPackage(id, runtime) {
|
|
|
42
44
|
export async function installPackage(pkg) {
|
|
43
45
|
return installPkg(pkg);
|
|
44
46
|
}
|
|
45
|
-
export async function install(packageId) {
|
|
46
|
-
|
|
47
|
+
export async function install(packageId, client) {
|
|
48
|
+
if (client && !VALID_CLIENTS.includes(client)) {
|
|
49
|
+
console.error(chalk.red(`Invalid client: ${client}\nValid clients are: ${VALID_CLIENTS.join(", ")}`));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const pkg = await resolvePackage(packageId, client);
|
|
47
53
|
if (!pkg) {
|
|
48
|
-
console.warn(chalk.yellow(`Package ${packageId} not found in
|
|
54
|
+
console.warn(chalk.yellow(`Package ${packageId} not found in registry.`));
|
|
49
55
|
const { proceedWithInstall } = await inquirer.prompt([
|
|
50
56
|
{
|
|
51
57
|
type: "confirm",
|
|
@@ -57,7 +63,7 @@ export async function install(packageId) {
|
|
|
57
63
|
if (proceedWithInstall) {
|
|
58
64
|
console.log(chalk.cyan(`Proceeding with installation of ${packageId}...`));
|
|
59
65
|
const runtime = await promptForRuntime();
|
|
60
|
-
const unknownPkg = createUnknownPackage(packageId, runtime);
|
|
66
|
+
const unknownPkg = createUnknownPackage(packageId, runtime, client);
|
|
61
67
|
await installPkg(unknownPkg);
|
|
62
68
|
}
|
|
63
69
|
else {
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import { listInstalledPackages } from "./commands/installed.js";
|
|
|
6
6
|
import { get } from "./commands/get.js";
|
|
7
7
|
const command = process.argv[2];
|
|
8
8
|
const packageName = process.argv[3];
|
|
9
|
+
const clientFlag = process.argv.indexOf("--client");
|
|
10
|
+
const client = clientFlag !== -1 ? process.argv[clientFlag + 1] : undefined;
|
|
9
11
|
async function main() {
|
|
10
12
|
switch (command) {
|
|
11
13
|
case "list":
|
|
@@ -16,7 +18,7 @@ async function main() {
|
|
|
16
18
|
console.error("Please provide a package name to install");
|
|
17
19
|
process.exit(1);
|
|
18
20
|
}
|
|
19
|
-
await install(packageName);
|
|
21
|
+
await install(packageName, client);
|
|
20
22
|
break;
|
|
21
23
|
case "uninstall":
|
|
22
24
|
await uninstall(packageName);
|
|
@@ -35,6 +37,7 @@ async function main() {
|
|
|
35
37
|
console.log("Available commands:");
|
|
36
38
|
console.log(" list List all available packages");
|
|
37
39
|
console.log(" install <package> Install a package");
|
|
40
|
+
console.log(" --client <name> Specify the AI client");
|
|
38
41
|
console.log(" uninstall [package] Uninstall a package");
|
|
39
42
|
console.log(" installed List installed packages");
|
|
40
43
|
console.log(" get <package> Get details for a specific package");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { ConfigManager } from "./config-manager.js";
|
|
3
|
+
import { REGISTRY_ENDPOINT } from "./package-resolver.js";
|
|
4
|
+
export class Analytics {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.sessionId = randomUUID();
|
|
7
|
+
const prefs = ConfigManager.readPreferences();
|
|
8
|
+
if (!prefs.anonUserId) {
|
|
9
|
+
ConfigManager.writePreferences({ ...prefs, anonUserId: randomUUID() });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async trackInstallation(packageId, clientType) {
|
|
13
|
+
const prefs = ConfigManager.readPreferences();
|
|
14
|
+
try {
|
|
15
|
+
const event = {
|
|
16
|
+
anonUserId: prefs.anonUserId,
|
|
17
|
+
eventType: "package_installation",
|
|
18
|
+
packageId: packageId,
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
platform: process.platform,
|
|
21
|
+
nodeVersion: process.version,
|
|
22
|
+
sessionId: this.sessionId,
|
|
23
|
+
clientType,
|
|
24
|
+
};
|
|
25
|
+
await this.sendEvent(event);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("Failed to track installation:", error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async sendEvent(event) {
|
|
32
|
+
const response = await fetch(`${REGISTRY_ENDPOINT}/install`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify(event),
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`Analytics request failed: ${response.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const analytics = new Analytics();
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
4
5
|
export class ConfigManager {
|
|
5
|
-
static getConfigPath() {
|
|
6
|
-
|
|
6
|
+
static getConfigPath(client) {
|
|
7
|
+
const baseDir = path.dirname(ConfigManager.configPath);
|
|
8
|
+
// Default to Claude if no client specified or if explicitly Claude
|
|
9
|
+
if (!client || client.toLowerCase() === "claude") {
|
|
10
|
+
return ConfigManager.configPath;
|
|
11
|
+
}
|
|
12
|
+
// Support for future AI clients
|
|
13
|
+
// Currently only Claude is officially supported
|
|
14
|
+
// 'jan' and others are placeholders for future integrations
|
|
15
|
+
return path.join(baseDir, "..", client, `${client.toLowerCase()}_config.json`);
|
|
7
16
|
}
|
|
8
|
-
static readConfig() {
|
|
17
|
+
static readConfig(client) {
|
|
9
18
|
try {
|
|
10
|
-
|
|
19
|
+
const configPath = ConfigManager.getConfigPath(client);
|
|
20
|
+
if (!fs.existsSync(configPath)) {
|
|
11
21
|
return { mcpServers: {} };
|
|
12
22
|
}
|
|
13
|
-
const rawConfig = JSON.parse(fs.readFileSync(
|
|
14
|
-
// Ensure we have the correct structure
|
|
23
|
+
const rawConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
15
24
|
return {
|
|
16
25
|
mcpServers: rawConfig.mcpServers || {},
|
|
17
26
|
};
|
|
@@ -21,17 +30,17 @@ export class ConfigManager {
|
|
|
21
30
|
return { mcpServers: {} };
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
|
-
static writeConfig(config) {
|
|
33
|
+
static writeConfig(config, client) {
|
|
25
34
|
try {
|
|
26
|
-
const
|
|
35
|
+
const configPath = ConfigManager.getConfigPath(client);
|
|
36
|
+
const configDir = path.dirname(configPath);
|
|
27
37
|
if (!fs.existsSync(configDir)) {
|
|
28
38
|
fs.mkdirSync(configDir, { recursive: true });
|
|
29
39
|
}
|
|
30
|
-
// Validate config structure before writing
|
|
31
40
|
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
32
41
|
throw new Error("Invalid config structure");
|
|
33
42
|
}
|
|
34
|
-
fs.writeFileSync(
|
|
43
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
35
44
|
}
|
|
36
45
|
catch (error) {
|
|
37
46
|
console.error("Error writing config:", error);
|
|
@@ -62,32 +71,32 @@ export class ConfigManager {
|
|
|
62
71
|
throw error;
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
|
-
static isPackageInstalled(id) {
|
|
66
|
-
const config = ConfigManager.readConfig();
|
|
74
|
+
static isPackageInstalled(id, client) {
|
|
75
|
+
const config = ConfigManager.readConfig(client);
|
|
67
76
|
return id in config.mcpServers;
|
|
68
77
|
}
|
|
69
|
-
static async installPackage(id, serverConfig) {
|
|
78
|
+
static async installPackage(id, serverConfig, client) {
|
|
70
79
|
try {
|
|
71
|
-
const config = ConfigManager.readConfig();
|
|
80
|
+
const config = ConfigManager.readConfig(client);
|
|
72
81
|
config.mcpServers[id] = serverConfig;
|
|
73
|
-
ConfigManager.writeConfig(config);
|
|
82
|
+
ConfigManager.writeConfig(config, client);
|
|
74
83
|
}
|
|
75
84
|
catch (error) {
|
|
76
85
|
console.error("Error installing server:", error);
|
|
77
86
|
throw error;
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
|
-
static async uninstallPackage(id) {
|
|
81
|
-
const config = ConfigManager.readConfig();
|
|
89
|
+
static async uninstallPackage(id, client) {
|
|
90
|
+
const config = ConfigManager.readConfig(client);
|
|
82
91
|
if (!config.mcpServers[id]) {
|
|
83
92
|
console.log(`Server ${id} is not installed.`);
|
|
84
93
|
return;
|
|
85
94
|
}
|
|
86
95
|
delete config.mcpServers[id];
|
|
87
|
-
ConfigManager.writeConfig(config);
|
|
96
|
+
ConfigManager.writeConfig(config, client);
|
|
88
97
|
}
|
|
89
|
-
static getServerConfig(id) {
|
|
90
|
-
const config = ConfigManager.readConfig();
|
|
98
|
+
static getServerConfig(id, client) {
|
|
99
|
+
const config = ConfigManager.readConfig(client);
|
|
91
100
|
return config.mcpServers[id] || null;
|
|
92
101
|
}
|
|
93
102
|
}
|
package/dist/utils/config.js
CHANGED
|
@@ -8,6 +8,7 @@ import { promisify } from "node:util";
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
const execAsync = promisify(exec);
|
|
11
|
+
export const VALID_CLIENTS = ["claude"];
|
|
11
12
|
function getPackageRuntime(packageName) {
|
|
12
13
|
const packageListPath = path.join(dirname(__dirname), "../packages/package-list.json");
|
|
13
14
|
if (!fs.existsSync(packageListPath)) {
|
package/dist/utils/display.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
export async function displayPackageDetailsWithActions(pkg) {
|
|
4
4
|
console.log(`\n${chalk.bold.cyan("Package Details:")}`);
|
|
5
|
+
console.log(chalk.bold("ID: ") + pkg.id);
|
|
5
6
|
console.log(chalk.bold("Name: ") + pkg.name);
|
|
6
7
|
console.log(chalk.bold("Description: ") + pkg.description);
|
|
7
8
|
console.log(chalk.bold("Vendor: ") + pkg.vendor);
|
|
@@ -2,6 +2,7 @@ import { exec } from "node:child_process";
|
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { ConfigManager } from "./config-manager.js";
|
|
5
|
+
import { analytics } from "./analytics.js";
|
|
5
6
|
const execAsync = promisify(exec);
|
|
6
7
|
async function checkAnalyticsConsent() {
|
|
7
8
|
const prefs = ConfigManager.readPreferences();
|
|
@@ -19,9 +20,8 @@ async function checkAnalyticsConsent() {
|
|
|
19
20
|
ConfigManager.writePreferences({ ...prefs, allowAnalytics });
|
|
20
21
|
return allowAnalytics;
|
|
21
22
|
}
|
|
22
|
-
async function trackInstallation(packageName) {
|
|
23
|
-
|
|
24
|
-
return;
|
|
23
|
+
async function trackInstallation(packageName, client) {
|
|
24
|
+
return analytics.trackInstallation(packageName, client);
|
|
25
25
|
}
|
|
26
26
|
async function promptForPlaceholder(key, message) {
|
|
27
27
|
const { value } = await inquirer.prompt([
|
|
@@ -80,72 +80,92 @@ async function promptForEnvVars(pkg) {
|
|
|
80
80
|
}
|
|
81
81
|
return Object.keys(envVars).length > 0 ? envVars : undefined;
|
|
82
82
|
}
|
|
83
|
-
async function
|
|
83
|
+
async function isClientRunning(client) {
|
|
84
|
+
if (!client)
|
|
85
|
+
return false;
|
|
84
86
|
try {
|
|
85
87
|
const platform = process.platform;
|
|
88
|
+
// Map of supported clients to their process names
|
|
89
|
+
// Currently only Claude is officially supported
|
|
90
|
+
// Other entries are placeholders for future integrations
|
|
91
|
+
const clientProcess = {
|
|
92
|
+
claude: "Claude",
|
|
93
|
+
// jan: "Jan", // Placeholder for future client integration
|
|
94
|
+
// Add more clients here as they become supported
|
|
95
|
+
}[client] || client;
|
|
86
96
|
if (platform === "win32") {
|
|
87
|
-
const { stdout } = await execAsync(
|
|
88
|
-
return stdout.includes(
|
|
97
|
+
const { stdout } = await execAsync(`tasklist /FI "IMAGENAME eq ${clientProcess}.exe" /NH`);
|
|
98
|
+
return stdout.includes(`${clientProcess}.exe`);
|
|
89
99
|
}
|
|
90
100
|
else if (platform === "darwin") {
|
|
91
|
-
const { stdout } = await execAsync(
|
|
101
|
+
const { stdout } = await execAsync(`pgrep -x "${clientProcess}"`);
|
|
92
102
|
return !!stdout.trim();
|
|
93
103
|
}
|
|
94
104
|
else if (platform === "linux") {
|
|
95
|
-
const { stdout } = await execAsync(
|
|
105
|
+
const { stdout } = await execAsync(`pgrep -f "${clientProcess.toLowerCase()}"`);
|
|
96
106
|
return !!stdout.trim();
|
|
97
107
|
}
|
|
98
108
|
return false;
|
|
99
109
|
}
|
|
100
110
|
catch (error) {
|
|
101
|
-
// If the command fails, assume
|
|
111
|
+
// If the command fails, assume client is not running
|
|
102
112
|
return false;
|
|
103
113
|
}
|
|
104
114
|
}
|
|
105
|
-
async function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
async function restartClient(client) {
|
|
116
|
+
const clientProcess = {
|
|
117
|
+
claude: "Claude",
|
|
118
|
+
jan: "Jan",
|
|
119
|
+
// Add more clients here
|
|
120
|
+
}[client] || client;
|
|
121
|
+
try {
|
|
122
|
+
const platform = process.platform;
|
|
123
|
+
if (platform === "win32") {
|
|
124
|
+
await execAsync(`taskkill /F /IM "${clientProcess}.exe" && start "" "${clientProcess}.exe"`);
|
|
125
|
+
}
|
|
126
|
+
else if (platform === "darwin") {
|
|
127
|
+
await execAsync(`killall "${clientProcess}" && open -a "${clientProcess}"`);
|
|
128
|
+
}
|
|
129
|
+
else if (platform === "linux") {
|
|
130
|
+
await execAsync(`pkill -f "${clientProcess.toLowerCase()}" && ${clientProcess.toLowerCase()}`);
|
|
131
|
+
}
|
|
132
|
+
// Wait a moment for the app to close before reopening
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
134
|
+
// Reopen the app
|
|
135
|
+
if (platform === "win32") {
|
|
136
|
+
await execAsync(`start "" "${clientProcess}.exe"`);
|
|
137
|
+
}
|
|
138
|
+
else if (platform === "darwin") {
|
|
139
|
+
await execAsync(`open -a "${clientProcess}"`);
|
|
140
|
+
}
|
|
141
|
+
else if (platform === "linux") {
|
|
142
|
+
await execAsync(clientProcess.toLowerCase());
|
|
143
|
+
}
|
|
144
|
+
console.log(`${clientProcess} has been restarted.`);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(`Failed to restart ${clientProcess}:`, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function promptForRestart(client) {
|
|
151
|
+
if (!client)
|
|
152
|
+
return false;
|
|
153
|
+
// Check if client is running first
|
|
154
|
+
const isRunning = await isClientRunning(client);
|
|
155
|
+
if (!isRunning) {
|
|
109
156
|
return false;
|
|
110
157
|
}
|
|
111
158
|
const { shouldRestart } = await inquirer.prompt([
|
|
112
159
|
{
|
|
113
160
|
type: "confirm",
|
|
114
161
|
name: "shouldRestart",
|
|
115
|
-
message:
|
|
162
|
+
message: `Would you like to restart the ${client} app to apply changes?`,
|
|
116
163
|
default: true,
|
|
117
164
|
},
|
|
118
165
|
]);
|
|
119
166
|
if (shouldRestart) {
|
|
120
|
-
console.log(
|
|
121
|
-
|
|
122
|
-
const platform = process.platform;
|
|
123
|
-
if (platform === "win32") {
|
|
124
|
-
await execAsync('taskkill /F /IM "Claude.exe" && start "" "Claude.exe"');
|
|
125
|
-
}
|
|
126
|
-
else if (platform === "darwin") {
|
|
127
|
-
await execAsync('killall "Claude" && open -a "Claude"');
|
|
128
|
-
}
|
|
129
|
-
else if (platform === "linux") {
|
|
130
|
-
await execAsync('pkill -f "claude" && claude');
|
|
131
|
-
}
|
|
132
|
-
// Wait a moment for the app to close before reopening
|
|
133
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
134
|
-
// Reopen the app
|
|
135
|
-
if (platform === "win32") {
|
|
136
|
-
await execAsync('start "" "Claude.exe"');
|
|
137
|
-
}
|
|
138
|
-
else if (platform === "darwin") {
|
|
139
|
-
await execAsync('open -a "Claude"');
|
|
140
|
-
}
|
|
141
|
-
else if (platform === "linux") {
|
|
142
|
-
await execAsync("claude");
|
|
143
|
-
}
|
|
144
|
-
console.log("Claude desktop app has been restarted.");
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
console.error("Failed to restart Claude desktop app:", error);
|
|
148
|
-
}
|
|
167
|
+
console.log(`Restarting ${client} app...`);
|
|
168
|
+
await restartClient(client);
|
|
149
169
|
}
|
|
150
170
|
return shouldRestart;
|
|
151
171
|
}
|
|
@@ -163,32 +183,32 @@ export async function installPackage(pkg) {
|
|
|
163
183
|
} */
|
|
164
184
|
const envVars = await promptForEnvVars(pkg);
|
|
165
185
|
// Use the connection stdio config directly
|
|
166
|
-
const connection = pkg.connections?.[0];
|
|
186
|
+
const connection = pkg.connections?.[0];
|
|
167
187
|
const serverConfig = {
|
|
168
188
|
command: connection.stdio.command,
|
|
169
189
|
args: connection.stdio.args,
|
|
170
|
-
env:
|
|
171
|
-
|
|
190
|
+
env: {
|
|
191
|
+
...(envVars || {}),
|
|
192
|
+
},
|
|
172
193
|
};
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
await trackInstallation(pkg.name);
|
|
194
|
+
// Install package with client-specific configuration
|
|
195
|
+
await ConfigManager.installPackage(pkg.id, serverConfig, pkg.client);
|
|
196
|
+
console.log(`Updated ${pkg.client || "default"} configuration`);
|
|
197
|
+
if (await checkAnalyticsConsent()) {
|
|
198
|
+
await trackInstallation(pkg.id, pkg.client);
|
|
179
199
|
}
|
|
180
|
-
await promptForRestart();
|
|
200
|
+
await promptForRestart(pkg.client);
|
|
181
201
|
}
|
|
182
202
|
catch (error) {
|
|
183
203
|
console.error("Failed to install package:", error);
|
|
184
204
|
throw error;
|
|
185
205
|
}
|
|
186
206
|
}
|
|
187
|
-
export async function uninstallPackage(packageName) {
|
|
207
|
+
export async function uninstallPackage(packageName, client) {
|
|
188
208
|
try {
|
|
189
209
|
await ConfigManager.uninstallPackage(packageName);
|
|
190
210
|
console.log(`\nUninstalled ${packageName}`);
|
|
191
|
-
await promptForRestart();
|
|
211
|
+
await promptForRestart(client);
|
|
192
212
|
}
|
|
193
213
|
catch (error) {
|
|
194
214
|
console.error("Failed to uninstall package:", error);
|
|
@@ -3,13 +3,16 @@ import { ConfigManager } from "./config-manager.js";
|
|
|
3
3
|
// import fs from 'fs';
|
|
4
4
|
// import { dirname } from 'path';
|
|
5
5
|
// import { fileURLToPath } from 'url';
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
dotenv.config();
|
|
8
|
+
export const REGISTRY_ENDPOINT = process.env.REGISTRY_ENDPOINT || "https://registry.smithery.ai";
|
|
6
9
|
export function isPackageInstalled(packageName) {
|
|
7
10
|
return ConfigManager.isPackageInstalled(packageName);
|
|
8
11
|
}
|
|
9
|
-
export async function resolvePackages() {
|
|
12
|
+
export async function resolvePackages(client) {
|
|
10
13
|
try {
|
|
11
|
-
//
|
|
12
|
-
const response = await fetch(
|
|
14
|
+
// Updated API endpoint
|
|
15
|
+
const response = await fetch(`${REGISTRY_ENDPOINT}/servers/`);
|
|
13
16
|
if (!response.ok) {
|
|
14
17
|
throw new Error(`Failed to fetch packages: ${response.statusText} (${response.status})`);
|
|
15
18
|
}
|
|
@@ -28,64 +31,51 @@ export async function resolvePackages() {
|
|
|
28
31
|
: "Unknown parsing error";
|
|
29
32
|
throw new Error(`Invalid JSON response from registry: ${errorMessage}`);
|
|
30
33
|
}
|
|
31
|
-
const config = ConfigManager.readConfig();
|
|
32
|
-
const installedServers = config.mcpServers || {};
|
|
33
|
-
const installedIds = Object.keys(installedServers);
|
|
34
|
-
// Create a map using IDs
|
|
35
|
-
const packageMap = new Map();
|
|
36
|
-
for (const pkg of packages) {
|
|
37
|
-
packageMap.set(pkg.id, pkg);
|
|
38
|
-
}
|
|
39
34
|
const resolvedPackages = new Map();
|
|
40
35
|
// Add packages from registry
|
|
41
36
|
for (const pkg of packages) {
|
|
42
37
|
resolvedPackages.set(pkg.id, {
|
|
43
|
-
id: pkg.id,
|
|
44
|
-
name: pkg.name,
|
|
45
|
-
description: pkg.description,
|
|
46
|
-
vendor: pkg.vendor,
|
|
47
|
-
sourceUrl: pkg.sourceUrl,
|
|
48
|
-
license: pkg.license,
|
|
49
|
-
connections: pkg.connections,
|
|
50
|
-
homepage: pkg.homepage,
|
|
51
|
-
// runtime: 'node',
|
|
38
|
+
id: pkg.id || "",
|
|
39
|
+
name: pkg.name || "",
|
|
40
|
+
description: pkg.description || "",
|
|
41
|
+
vendor: pkg.vendor || "",
|
|
42
|
+
sourceUrl: pkg.sourceUrl || "",
|
|
43
|
+
license: pkg.license || "",
|
|
44
|
+
connections: pkg.connections || [],
|
|
45
|
+
homepage: pkg.homepage || "",
|
|
52
46
|
isInstalled: false,
|
|
53
47
|
isVerified: true,
|
|
48
|
+
client: client,
|
|
54
49
|
});
|
|
55
50
|
}
|
|
56
51
|
// Process installed packages
|
|
52
|
+
const config = ConfigManager.readConfig();
|
|
53
|
+
const installedIds = Object.keys(config.mcpServers || {});
|
|
54
|
+
const installedServers = config.mcpServers || {};
|
|
57
55
|
for (const id of installedIds) {
|
|
58
56
|
const installedServer = installedServers[id];
|
|
59
|
-
const existingPkg =
|
|
57
|
+
const existingPkg = resolvedPackages.get(id);
|
|
60
58
|
if (existingPkg) {
|
|
61
59
|
resolvedPackages.set(id, {
|
|
62
|
-
|
|
63
|
-
name: existingPkg.name,
|
|
64
|
-
description: existingPkg.description,
|
|
65
|
-
vendor: existingPkg.vendor,
|
|
66
|
-
sourceUrl: existingPkg.sourceUrl,
|
|
67
|
-
license: existingPkg.license,
|
|
68
|
-
connections: existingPkg.connections,
|
|
69
|
-
homepage: existingPkg.homepage,
|
|
70
|
-
// runtime: installedServer?.runtime || 'node',
|
|
60
|
+
...existingPkg,
|
|
71
61
|
isInstalled: true,
|
|
72
|
-
|
|
62
|
+
client: client || existingPkg.client,
|
|
73
63
|
});
|
|
74
64
|
}
|
|
75
65
|
else {
|
|
76
66
|
resolvedPackages.set(id, {
|
|
77
67
|
...installedServer,
|
|
78
|
-
id,
|
|
79
|
-
name: id,
|
|
68
|
+
id: id || "",
|
|
69
|
+
name: id || "",
|
|
80
70
|
description: "Local package",
|
|
81
71
|
vendor: "Local",
|
|
82
72
|
sourceUrl: "",
|
|
83
73
|
license: "",
|
|
84
74
|
connections: [],
|
|
85
75
|
homepage: "",
|
|
86
|
-
// runtime: installedServer?.runtime || 'node',
|
|
87
76
|
isInstalled: true,
|
|
88
77
|
isVerified: false,
|
|
78
|
+
client: client,
|
|
89
79
|
});
|
|
90
80
|
}
|
|
91
81
|
}
|
|
@@ -95,10 +85,10 @@ export async function resolvePackages() {
|
|
|
95
85
|
throw new Error(`Failed to resolve packages: ${error instanceof Error ? error.message : String(error)}`);
|
|
96
86
|
}
|
|
97
87
|
}
|
|
98
|
-
export async function resolvePackage(packageId) {
|
|
88
|
+
export async function resolvePackage(packageId, client) {
|
|
99
89
|
try {
|
|
100
|
-
//
|
|
101
|
-
const response = await fetch(
|
|
90
|
+
// Updated API endpoint
|
|
91
|
+
const response = await fetch(`${REGISTRY_ENDPOINT}/servers/${packageId}`);
|
|
102
92
|
if (!response.ok) {
|
|
103
93
|
// Check if it's an installed package
|
|
104
94
|
const config = ConfigManager.readConfig();
|
|
@@ -113,9 +103,9 @@ export async function resolvePackage(packageId) {
|
|
|
113
103
|
license: "",
|
|
114
104
|
connections: [],
|
|
115
105
|
homepage: "",
|
|
116
|
-
// runtime: installedServer.runtime || 'node',
|
|
117
106
|
isInstalled: true,
|
|
118
107
|
isVerified: false,
|
|
108
|
+
client: client,
|
|
119
109
|
};
|
|
120
110
|
}
|
|
121
111
|
return null;
|
|
@@ -130,9 +120,9 @@ export async function resolvePackage(packageId) {
|
|
|
130
120
|
license: registryPackage.license,
|
|
131
121
|
connections: registryPackage.connections,
|
|
132
122
|
homepage: registryPackage.homepage,
|
|
133
|
-
// runtime: 'node',
|
|
134
123
|
isInstalled: isPackageInstalled(registryPackage.id),
|
|
135
124
|
isVerified: true,
|
|
125
|
+
client: client,
|
|
136
126
|
};
|
|
137
127
|
return resolvedPackage;
|
|
138
128
|
}
|
package/dist/utils/ui.js
CHANGED
|
@@ -3,12 +3,31 @@ import fuzzy from "fuzzy";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
export function formatPackageChoice(pkg, showInstallStatus = false) {
|
|
5
5
|
const prefix = showInstallStatus ? (pkg.isInstalled ? "✓ " : " ") : "";
|
|
6
|
+
const name = (pkg.name || "").toString();
|
|
7
|
+
const description = (pkg.description || "").toString();
|
|
8
|
+
const vendor = (pkg.vendor || "").toString();
|
|
9
|
+
const id = (pkg.id || "").toString();
|
|
10
|
+
// Adjust column widths (removed license column)
|
|
11
|
+
const nameWidth = 20;
|
|
12
|
+
const idWidth = 15;
|
|
13
|
+
const descWidth = 45; // Increased description width
|
|
14
|
+
const vendorWidth = 15;
|
|
15
|
+
// Truncation function remains the same
|
|
16
|
+
const truncateText = (text, width) => {
|
|
17
|
+
if (text.length <= width) {
|
|
18
|
+
return text.padEnd(width);
|
|
19
|
+
}
|
|
20
|
+
return `${text.slice(0, width - 3).padEnd(width - 3)}...`;
|
|
21
|
+
};
|
|
22
|
+
// Apply truncation (removed license)
|
|
23
|
+
const truncatedName = truncateText(name, nameWidth);
|
|
24
|
+
const truncatedId = truncateText(id, idWidth);
|
|
25
|
+
const truncatedDesc = truncateText(description, descWidth);
|
|
26
|
+
const truncatedVendor = truncateText(vendor, vendorWidth);
|
|
6
27
|
return {
|
|
7
|
-
name: `${prefix}${
|
|
8
|
-
? `${pkg.description.slice(0, 44)}...`
|
|
9
|
-
: pkg.description.padEnd(49)} │ ${pkg.vendor.padEnd(19)} │ ${pkg.license.padEnd(14)}`,
|
|
28
|
+
name: `${prefix}${truncatedName} │ ${truncatedId} │ ${truncatedDesc} │ ${truncatedVendor}`,
|
|
10
29
|
value: pkg,
|
|
11
|
-
short:
|
|
30
|
+
short: name,
|
|
12
31
|
};
|
|
13
32
|
}
|
|
14
33
|
export function createPackagePrompt(packages, options = {}) {
|