@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/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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithery/cli",
|
|
3
|
-
"version": "1.0.
|
|
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
|
}
|
package/dist/commands/inspect.js
DELETED
|
@@ -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
|
-
}
|
package/dist/commands/install.js
DELETED
|
@@ -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
|
-
}
|
package/dist/commands/view.js
DELETED
|
@@ -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"];
|
package/dist/types/registry.js
DELETED
|
@@ -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
|
-
}
|