@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 +49 -0
- package/dist/index.js +120 -0
- package/dist/utils.js +134 -0
- package/package.json +28 -0
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
|
+
}
|