@insforge/install 0.0.2

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 ADDED
@@ -0,0 +1,49 @@
1
+ # @insforge/install
2
+
3
+ CLI tool for installing Insforge MCP servers across different AI clients.
4
+
5
+ ## Installation & Usage
6
+
7
+ ```bash
8
+ # Interactive mode (will prompt for API key)
9
+ npx @insforge/install
10
+
11
+ # Quick install with required API key
12
+ npx @insforge/install --client claude --api-key YOUR_API_KEY
13
+
14
+ # With custom base URL (API key still required)
15
+ npx @insforge/install --client cursor --api-key YOUR_API_KEY --base-url http://localhost:7130
16
+
17
+ # Uninstall
18
+ npx @insforge/install uninstall --client claude
19
+ ```
20
+
21
+ ## Required Configuration
22
+
23
+ - **API Key** (required): Your Insforge API key for authentication
24
+ - **Base URL** (optional): Defaults to `http://localhost:7130`
25
+
26
+ ## Supported Clients
27
+
28
+ - **claude** - Claude Desktop
29
+ - **cursor** - Cursor IDE
30
+ - **windsurf** - Windsurf IDE
31
+ - **cline** - Cline VS Code Extension
32
+ - **roocode** - Roo-Code VS Code Extension (Cline fork)
33
+
34
+ ## Options
35
+
36
+ ```
37
+ --client <client> Target AI client
38
+ --api-key <key> Insforge API key
39
+ --base-url <url> Insforge base URL (default: http://localhost:7130)
40
+ --force Overwrite existing configuration
41
+ ```
42
+
43
+ ## Examples
44
+
45
+ See the [examples](./examples) directory for programmatic usage.
46
+
47
+ ## License
48
+
49
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ clientNames,
4
+ logger,
5
+ readConfig,
6
+ writeConfig
7
+ } from "./utils.js";
8
+ import { execSync } from 'child_process';
9
+
10
+ import yargs from "yargs";
11
+ import { hideBin } from "yargs/helpers";
12
+
13
+ import pc from "picocolors";
14
+ var { green, red } = pc;
15
+ function builder(yargs2) {
16
+ return yargs2.option("client", {
17
+ type: "string",
18
+ description: "Client to use for installation",
19
+ demandOption: true,
20
+ choices: clientNames
21
+ }).option("env", {
22
+ type: "string",
23
+ description: "Environment variables as key=value pairs (can be used multiple times). API_KEY is required.",
24
+ array: true
25
+ });
26
+ }
27
+ async function handler(argv) {
28
+ if (!argv.client || !clientNames.includes(argv.client)) {
29
+ logger.error(`Invalid client: ${argv.client}. Available clients: ${clientNames.join(", ")}`);
30
+ return;
31
+ }
32
+ const envVars = {};
33
+ if (argv.env && argv.env.length > 0) {
34
+ for (const envVar of argv.env) {
35
+ const [key, ...valueParts] = envVar.split("=");
36
+ if (key && valueParts.length > 0) {
37
+ envVars[key] = valueParts.join("=");
38
+ } else {
39
+ logger.warn(`Invalid environment variable format: ${envVar}. Expected KEY=VALUE format.`);
40
+ }
41
+ }
42
+ }
43
+ if (!envVars.API_KEY) {
44
+ logger.error("API_KEY environment variable is required. Use --env API_KEY=your_key");
45
+ return;
46
+ }
47
+ // Add default base URL if not provided
48
+ if (!envVars.API_BASE_URL) {
49
+ envVars.API_BASE_URL = "http://localhost:7130";
50
+ }
51
+ const name = "insforge";
52
+ const ready = true; // Auto-confirm for simplified installation
53
+ if (ready) {
54
+ try {
55
+ const config = readConfig(argv.client);
56
+
57
+ // Handle different config structures for different clients
58
+ if (argv.client === "claude-code") {
59
+ // Use Claude CLI for Claude Code
60
+ const envArgs = Object.entries(envVars)
61
+ .filter(([key]) => key !== 'CLIENT_NAME')
62
+ .map(([key, value]) => `-e ${key}=${value}`)
63
+ .join(' ');
64
+
65
+ try {
66
+ execSync(`claude mcp add ${name} ${envArgs} -- npx -y @insforge/insforge-mcp@latest`, {
67
+ stdio: 'inherit'
68
+ });
69
+ } catch (error) {
70
+ throw new Error(`Failed to add MCP server via Claude CLI: ${error.message}`);
71
+ }
72
+ } else if (argv.client === "cursor") {
73
+ // Cursor uses mcpServers at root level
74
+ if (!config.mcpServers) config.mcpServers = {};
75
+ config.mcpServers[name] = {
76
+ command: "npx",
77
+ args: ["-y", "@insforge/insforge-mcp@latest"],
78
+ env: {
79
+ API_KEY: envVars.API_KEY,
80
+ API_BASE_URL: envVars.API_BASE_URL
81
+ }
82
+ };
83
+ writeConfig(config, argv.client);
84
+ } else if (argv.client === "windsurf") {
85
+ if (!config.mcpServers) config.mcpServers = {};
86
+ config.mcpServers[name] = {
87
+ command: "npx",
88
+ args: ["-y", "@insforge/insforge-mcp@latest"],
89
+ env: {
90
+ API_KEY: envVars.API_KEY,
91
+ API_BASE_URL: envVars.API_BASE_URL
92
+ }
93
+ };
94
+ writeConfig(config, argv.client);
95
+ } else if (argv.client === "cline" || argv.client === "roocode") {
96
+ if (!config.mcpServers) config.mcpServers = {};
97
+ config.mcpServers[name] = {
98
+ command: "npx",
99
+ args: ["-y", "@insforge/insforge-mcp@latest"],
100
+ env: {
101
+ API_KEY: envVars.API_KEY,
102
+ API_BASE_URL: envVars.API_BASE_URL
103
+ }
104
+ };
105
+ writeConfig(config, argv.client);
106
+ }
107
+ logger.box(green(`Successfully installed Insforge MCP server in ${argv.client}.`));
108
+ } catch (e) {
109
+ logger.error(red(e.message));
110
+ }
111
+ }
112
+ }
113
+
114
+ var parser = yargs(hideBin(process.argv)).scriptName("@insforge/install").command("install", "Install Insforge MCP server", builder, handler).help().alias("h", "help").version().alias("v", "version");
115
+ if (!process.argv.slice(2).length || process.argv[2].startsWith("--")) {
116
+ parser.parse(["install", ...process.argv.slice(2)]);
117
+ } else {
118
+ parser.parse();
119
+ }
120
+
package/dist/utils.js ADDED
@@ -0,0 +1,134 @@
1
+ // src/client-config.ts
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import process from "process";
6
+
7
+ // src/logger.ts
8
+ import { createConsola } from "consola";
9
+ var logger = createConsola({});
10
+ var verbose = (msg) => logger.verbose(msg);
11
+
12
+ // src/client-config.ts
13
+ var homeDir = os.homedir();
14
+ var platformPaths = {
15
+ win32: {
16
+ baseDir: process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
17
+ vscodePath: path.join("Code", "User", "globalStorage")
18
+ },
19
+ darwin: {
20
+ baseDir: path.join(homeDir, "Library", "Application Support"),
21
+ vscodePath: path.join("Code", "User", "globalStorage")
22
+ },
23
+ linux: {
24
+ baseDir: process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
25
+ vscodePath: path.join("Code/User/globalStorage")
26
+ }
27
+ };
28
+ var platform = process.platform;
29
+ var { baseDir, vscodePath } = platformPaths[platform];
30
+ var clientPaths = {
31
+ "claude-code": {
32
+ type: "file",
33
+ path: path.join(homeDir, ".claude", "claude_mcp_config.json")
34
+ },
35
+ cursor: {
36
+ type: "file",
37
+ path: path.join(homeDir, ".cursor", "mcp.json"),
38
+ localPath: path.join(process.cwd(), ".cursor", "mcp.json")
39
+ },
40
+ windsurf: {
41
+ type: "file",
42
+ path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json")
43
+ },
44
+ cline: {
45
+ type: "file",
46
+ path: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
47
+ },
48
+ roocode: {
49
+ type: "file",
50
+ path: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
51
+ }
52
+ };
53
+ var clientNames = Object.keys(clientPaths);
54
+ function getConfigPath(client, local) {
55
+ const normalizedClient = client?.toLowerCase() || "claude-code";
56
+ verbose(`Getting config path for client: ${normalizedClient}${local ? " (local)" : ""}`);
57
+ const configTarget = clientPaths[normalizedClient];
58
+ if (!configTarget) {
59
+ throw new Error(`Unsupported client: ${client}. Supported clients: ${clientNames.join(", ")}`);
60
+ }
61
+ if (local && configTarget.localPath) {
62
+ verbose(`Using local config path for ${normalizedClient}: ${configTarget.localPath}`);
63
+ return { ...configTarget, path: configTarget.localPath };
64
+ }
65
+ verbose(`Using default config path for ${normalizedClient}: ${configTarget.path}`);
66
+ return configTarget;
67
+ }
68
+ function readConfig(client, local) {
69
+ verbose(`Reading config for client: ${client}${local ? " (local)" : ""}`);
70
+ try {
71
+ const configPath = getConfigPath(client, local);
72
+ verbose(`Checking if config file exists at: ${configPath.path}`);
73
+ if (!fs.existsSync(configPath.path)) {
74
+ verbose("Config file not found, returning default empty config");
75
+ // Return appropriate default structure for each client
76
+ return { mcpServers: {} };
77
+ }
78
+ verbose("Reading config file content");
79
+ const rawConfig = JSON.parse(fs.readFileSync(configPath.path, "utf8"));
80
+ verbose(`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`);
81
+ // Return the raw config to preserve existing structure
82
+ return rawConfig;
83
+ } catch (error) {
84
+ verbose(`Error reading config: ${error instanceof Error ? error.stack : JSON.stringify(error)}`);
85
+ return { mcpServers: {} };
86
+ }
87
+ }
88
+ function writeConfig(config, client, local) {
89
+ verbose(`Writing config for client: ${client || "default"}${local ? " (local)" : ""}`);
90
+ verbose(`Config data: ${JSON.stringify(config, null, 2)}`);
91
+
92
+ // All clients use mcpServers
93
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
94
+ verbose("Invalid mcpServers structure in config");
95
+ throw new Error("Invalid mcpServers structure");
96
+ }
97
+
98
+ const configPath = getConfigPath(client, local);
99
+ writeConfigFile(config, configPath);
100
+ }
101
+ function writeConfigFile(config, target) {
102
+ const configDir = path.dirname(target.path);
103
+ verbose(`Ensuring config directory exists: ${configDir}`);
104
+ if (!fs.existsSync(configDir)) {
105
+ verbose(`Creating directory: ${configDir}`);
106
+ fs.mkdirSync(configDir, { recursive: true });
107
+ }
108
+ let existingConfig = { mcpServers: {} };
109
+ try {
110
+ if (fs.existsSync(target.path)) {
111
+ verbose("Reading existing config file for merging");
112
+ existingConfig = JSON.parse(fs.readFileSync(target.path, "utf8"));
113
+ verbose(`Existing config loaded: ${JSON.stringify(existingConfig, null, 2)}`);
114
+ }
115
+ } catch (error) {
116
+ verbose(`Error reading existing config for merge: ${error instanceof Error ? error.message : String(error)}`);
117
+ }
118
+ verbose("Merging configs");
119
+ const mergedConfig = {
120
+ ...existingConfig,
121
+ ...config
122
+ };
123
+ verbose(`Merged config: ${JSON.stringify(mergedConfig, null, 2)}`);
124
+ verbose(`Writing config to file: ${target.path}`);
125
+ fs.writeFileSync(target.path, JSON.stringify(mergedConfig, null, 2));
126
+ verbose("Config successfully written");
127
+ }
128
+ export {
129
+ logger,
130
+ clientNames,
131
+ getConfigPath,
132
+ readConfig,
133
+ writeConfig
134
+ };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@insforge/install",
3
+ "version": "0.0.2",
4
+ "description": "CLI tool for installing Insforge MCP servers across different AI clients",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "insforge-install": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node dist/index.js"
12
+ },
13
+ "dependencies": {
14
+ "yargs": "^17.7.2",
15
+ "picocolors": "^1.0.0",
16
+ "consola": "^3.2.3"
17
+ },
18
+ "keywords": [
19
+ "insforge",
20
+ "mcp",
21
+ "model-context-protocol"
22
+ ],
23
+ "author": "Insforge",
24
+ "license": "MIT",
25
+ "publishConfig": {
26
+ "access": "public"
27
+ }
28
+ }