@gatewaze/connect 0.1.0

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,79 @@
1
+ # @gatewaze/connect
2
+
3
+ One command to connect your AI clients to a Gatewaze MCP server.
4
+
5
+ ```
6
+ npx @gatewaze/connect
7
+ ```
8
+
9
+ The tool finds the AI clients installed on your machine, asks which ones you want to connect, and writes the right connector entry for each. No secrets are stored: each client opens your normal sign-in page in the browser the first time you use the connector.
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ npx @gatewaze/connect [options]
15
+
16
+ Options:
17
+ --server <url> MCP server URL (default: https://mcp.aaif.live/)
18
+ --name <label> Connector name (default: derived from the server hostname, e.g. "aaif")
19
+ --all Configure every detected client, no prompts
20
+ --client <id> Configure a specific client (repeatable):
21
+ claude-desktop, claude-code, goose, chatgpt
22
+ --force Overwrite an existing entry with the same name
23
+ --dry-run Show what would change without writing anything
24
+ -h, --help Show help
25
+ ```
26
+
27
+ Examples:
28
+
29
+ ```
30
+ # Interactive: detect clients and choose which to connect
31
+ npx @gatewaze/connect
32
+
33
+ # Connect everything found, non-interactive
34
+ npx @gatewaze/connect --all
35
+
36
+ # A different server, specific clients, preview only
37
+ npx @gatewaze/connect --server https://mcp.example.org --client claude-desktop --client goose --dry-run
38
+ ```
39
+
40
+ ## What it does per client
41
+
42
+ **Claude Desktop** — adds an entry to `claude_desktop_config.json` (`~/Library/Application Support/Claude/` on macOS, `%APPDATA%\Claude\` on Windows, `~/.config/Claude/` on Linux):
43
+
44
+ ```json
45
+ "mcpServers": {
46
+ "aaif": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.aaif.live/"] }
47
+ }
48
+ ```
49
+
50
+ Claude Desktop's config cannot take a bare remote URL, so the entry goes through [mcp-remote](https://www.npmjs.com/package/mcp-remote), which opens the OAuth sign-in in your browser. All existing config content is kept, the file is backed up first, and an existing entry with the same name is never overwritten without confirmation (or `--force`). Restart Claude Desktop after connecting.
51
+
52
+ **Claude Code** — runs `claude mcp add --scope user --transport http <name> <url>`. If the command cannot run, the tool prints it so you can run it yourself.
53
+
54
+ **Goose** (CLI and Desktop share one config) — appends an entry to the `extensions:` map in `~/.config/goose/config.yaml`:
55
+
56
+ ```yaml
57
+ extensions:
58
+ aaif:
59
+ enabled: true
60
+ name: aaif
61
+ type: streamable_http
62
+ uri: https://mcp.aaif.live/
63
+ timeout: 300
64
+ ```
65
+
66
+ The rest of the file, including comments, is preserved, and a backup is written to `config.yaml.bak-<timestamp>` first. The tool also prints a `goose://extension?...` deep link as a one-click alternative for Goose Desktop.
67
+
68
+ **ChatGPT** — connectors are managed in the ChatGPT web app, so the tool prints the steps: Settings -> Connectors -> Add, paste the server URL, sign in.
69
+
70
+ ## Development
71
+
72
+ ```
73
+ npm install
74
+ npm run build # tsc -> dist/
75
+ npm run dev # run from source via tsx
76
+ npm test # unit tests for the config mutators
77
+ ```
78
+
79
+ Requires Node 18 or newer.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ChatGPT keeps its connectors in the web app — there is no local config file
3
+ * to edit, so all we can do is walk the user through it.
4
+ */
5
+ export function chatgptInstructions(name, serverUrl) {
6
+ return [
7
+ 'ChatGPT connectors are managed in the web app; add this one by hand:',
8
+ ' 1. Open ChatGPT and go to Settings -> Connectors.',
9
+ ' 2. Click "Add" (Create a custom connector).',
10
+ ` 3. Name it "${name}" and paste the server URL: ${serverUrl}`,
11
+ ' 4. Save, then sign in when the browser window opens.',
12
+ ];
13
+ }
14
+ //# sourceMappingURL=chatgpt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatgpt.js","sourceRoot":"","sources":["../../src/clients/chatgpt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,SAAiB;IACjE,OAAO;QACL,sEAAsE;QACtE,qDAAqD;QACrD,+CAA+C;QAC/C,iBAAiB,IAAI,+BAA+B,SAAS,EAAE;QAC/D,wDAAwD;KACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { findOnPath } from '../util.js';
3
+ export function claudeBinaryPath() {
4
+ return findOnPath('claude');
5
+ }
6
+ /** The command we run (user scope, HTTP transport — Claude Code speaks remote MCP natively). */
7
+ export function claudeCodeAddCommand(name, serverUrl) {
8
+ return ['claude', 'mcp', 'add', '--scope', 'user', '--transport', 'http', name, serverUrl];
9
+ }
10
+ export function formatCommand(argv) {
11
+ return argv.map((a) => (/[^A-Za-z0-9_@%+=:,./-]/.test(a) ? `'${a.replace(/'/g, `'\\''`)}'` : a)).join(' ');
12
+ }
13
+ export function runClaudeCodeAdd(name, serverUrl) {
14
+ const argv = claudeCodeAddCommand(name, serverUrl);
15
+ const command = formatCommand(argv);
16
+ const res = spawnSync(argv[0], argv.slice(1), { encoding: 'utf8', timeout: 30_000 });
17
+ if (res.error) {
18
+ return { ok: false, message: `Could not run claude: ${res.error.message}`, command };
19
+ }
20
+ if (res.status !== 0) {
21
+ const detail = (res.stderr || res.stdout || '').trim();
22
+ return {
23
+ ok: false,
24
+ message: `claude mcp add exited with code ${res.status}${detail ? `: ${detail}` : ''}`,
25
+ command,
26
+ };
27
+ }
28
+ const out = (res.stdout || '').trim();
29
+ return { ok: true, message: out || `Registered "${name}" with Claude Code (user scope).`, command };
30
+ }
31
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/clients/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,gBAAgB;IAC9B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,SAAiB;IAClE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7G,CAAC;AASD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,SAAiB;IAC9D,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrF,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,yBAAyB,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,mCAAmC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACtF,OAAO;SACR,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,eAAe,IAAI,kCAAkC,EAAE,OAAO,EAAE,CAAC;AACtG,CAAC"}
@@ -0,0 +1,97 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { backupFile, deepEqual, ensureParentDir } from '../util.js';
5
+ /** Platform-specific location of claude_desktop_config.json. */
6
+ export function claudeDesktopConfigPath() {
7
+ if (process.platform === 'win32') {
8
+ const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
9
+ return path.join(appData, 'Claude', 'claude_desktop_config.json');
10
+ }
11
+ if (process.platform === 'darwin') {
12
+ return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
13
+ }
14
+ const configHome = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
15
+ return path.join(configHome, 'Claude', 'claude_desktop_config.json');
16
+ }
17
+ /**
18
+ * The entry we write. Claude Desktop's config cannot take a bare remote URL,
19
+ * so we go through mcp-remote, which drives the OAuth sign-in in the browser.
20
+ */
21
+ export function desktopServerEntry(serverUrl) {
22
+ return { command: 'npx', args: ['-y', 'mcp-remote', serverUrl] };
23
+ }
24
+ /**
25
+ * Add (or update) an mcpServers entry in claude_desktop_config.json.
26
+ * All existing JSON content is preserved; the file is backed up before writing.
27
+ */
28
+ export function applyClaudeDesktopConfig(configPath, name, serverUrl, opts = {}) {
29
+ const desired = desktopServerEntry(serverUrl);
30
+ let config = {};
31
+ const exists = fs.existsSync(configPath);
32
+ if (exists) {
33
+ const raw = fs.readFileSync(configPath, 'utf8');
34
+ if (raw.trim() !== '') {
35
+ try {
36
+ const parsed = JSON.parse(raw);
37
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
38
+ return {
39
+ status: 'error',
40
+ message: `${configPath} does not contain a JSON object; refusing to modify it.`,
41
+ };
42
+ }
43
+ config = parsed;
44
+ }
45
+ catch (err) {
46
+ return {
47
+ status: 'error',
48
+ message: `Could not parse ${configPath} (${err.message}); refusing to modify it.`,
49
+ };
50
+ }
51
+ }
52
+ }
53
+ const servers = (config.mcpServers ?? {});
54
+ if (typeof servers !== 'object' || servers === null || Array.isArray(servers)) {
55
+ return {
56
+ status: 'error',
57
+ message: `"mcpServers" in ${configPath} is not an object; refusing to modify it.`,
58
+ };
59
+ }
60
+ const existing = servers[name];
61
+ const isUpdate = existing !== undefined;
62
+ if (isUpdate && deepEqual(existing, desired)) {
63
+ return { status: 'unchanged', message: `"${name}" is already configured for this server.` };
64
+ }
65
+ if (isUpdate && !opts.overwrite && !opts.dryRun) {
66
+ return {
67
+ status: 'conflict',
68
+ message: `An mcpServers entry named "${name}" already exists in ${configPath} with different settings ` +
69
+ `(currently: ${JSON.stringify(existing)}).`,
70
+ };
71
+ }
72
+ servers[name] = desired;
73
+ config.mcpServers = servers;
74
+ const output = JSON.stringify(config, null, 2) + '\n';
75
+ if (opts.dryRun) {
76
+ return {
77
+ status: isUpdate ? 'would-update' : 'would-add',
78
+ message: `${isUpdate ? 'Would update' : 'Would add'} mcpServers.${name} in ${configPath}:\n` +
79
+ ` ${JSON.stringify({ [name]: desired })}`,
80
+ plannedContent: output,
81
+ };
82
+ }
83
+ let backupPath;
84
+ if (exists) {
85
+ backupPath = backupFile(configPath, opts.now);
86
+ }
87
+ else {
88
+ ensureParentDir(configPath);
89
+ }
90
+ fs.writeFileSync(configPath, output, 'utf8');
91
+ return {
92
+ status: isUpdate ? 'updated' : 'added',
93
+ message: `${isUpdate ? 'Updated' : 'Added'} mcpServers.${name} in ${configPath}`,
94
+ backupPath,
95
+ };
96
+ }
97
+ //# sourceMappingURL=claude-desktop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-desktop.js","sourceRoot":"","sources":["../../src/clients/claude-desktop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { Document, parseDocument } from 'yaml';
5
+ import { backupFile, deepEqual, ensureParentDir } from '../util.js';
6
+ /** Goose CLI and Goose Desktop share one config file. */
7
+ export function gooseConfigPath() {
8
+ if (process.platform === 'win32') {
9
+ const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming');
10
+ return path.join(appData, 'Block', 'goose', 'config', 'config.yaml');
11
+ }
12
+ const configHome = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
13
+ return path.join(configHome, 'goose', 'config.yaml');
14
+ }
15
+ export function gooseExtensionEntry(name, serverUrl) {
16
+ return { enabled: true, name, type: 'streamable_http', uri: serverUrl, timeout: 300 };
17
+ }
18
+ /** Desktop one-click alternative to editing the config file. */
19
+ export function gooseDeepLink(name, serverUrl) {
20
+ return `goose://extension?url=${encodeURIComponent(serverUrl)}&name=${encodeURIComponent(name)}`;
21
+ }
22
+ /**
23
+ * Add (or update) an entry in the `extensions:` map of Goose's config.yaml.
24
+ * Parses with the yaml Document API so existing content, comments and
25
+ * formatting are preserved as far as the library allows. The file is backed
26
+ * up to config.yaml.bak-<timestamp> before writing.
27
+ */
28
+ export function applyGooseConfig(configPath, name, serverUrl, opts = {}) {
29
+ const desired = gooseExtensionEntry(name, serverUrl);
30
+ const exists = fs.existsSync(configPath);
31
+ let doc;
32
+ if (exists) {
33
+ const raw = fs.readFileSync(configPath, 'utf8');
34
+ doc = parseDocument(raw);
35
+ if (doc.errors.length > 0) {
36
+ return {
37
+ status: 'error',
38
+ message: `Could not parse ${configPath} (${doc.errors[0].message}); refusing to modify it.`,
39
+ };
40
+ }
41
+ }
42
+ else {
43
+ doc = new Document({});
44
+ }
45
+ const existingNode = doc.getIn(['extensions', name]);
46
+ const existing = existingNode !== undefined && existingNode !== null && typeof existingNode.toJSON === 'function'
47
+ ? existingNode.toJSON()
48
+ : existingNode;
49
+ const isUpdate = existing !== undefined;
50
+ if (isUpdate && deepEqual(existing, desired)) {
51
+ return { status: 'unchanged', message: `Extension "${name}" is already configured for this server.` };
52
+ }
53
+ if (isUpdate && !opts.overwrite && !opts.dryRun) {
54
+ return {
55
+ status: 'conflict',
56
+ message: `An extension named "${name}" already exists in ${configPath} with different settings ` +
57
+ `(currently: ${JSON.stringify(existing)}).`,
58
+ };
59
+ }
60
+ doc.setIn(['extensions', name], desired);
61
+ const output = doc.toString();
62
+ if (opts.dryRun) {
63
+ return {
64
+ status: isUpdate ? 'would-update' : 'would-add',
65
+ message: `${isUpdate ? 'Would update' : 'Would add'} extensions.${name} in ${configPath}:\n` +
66
+ ` ${JSON.stringify(desired)}`,
67
+ plannedContent: output,
68
+ };
69
+ }
70
+ let backupPath;
71
+ if (exists) {
72
+ backupPath = backupFile(configPath, opts.now);
73
+ }
74
+ else {
75
+ ensureParentDir(configPath);
76
+ }
77
+ fs.writeFileSync(configPath, output, 'utf8');
78
+ return {
79
+ status: isUpdate ? 'updated' : 'added',
80
+ message: `${isUpdate ? 'Updated' : 'Added'} extensions.${name} in ${configPath}`,
81
+ backupPath,
82
+ };
83
+ }
84
+ //# sourceMappingURL=goose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goose.js","sourceRoot":"","sources":["../../src/clients/goose.ts"],"names":[],"mappings":"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"}
package/dist/detect.js ADDED
@@ -0,0 +1,45 @@
1
+ import fs from 'node:fs';
2
+ import { claudeDesktopConfigPath } from './clients/claude-desktop.js';
3
+ import { claudeBinaryPath } from './clients/claude-code.js';
4
+ import { gooseConfigPath } from './clients/goose.js';
5
+ import { findOnPath } from './util.js';
6
+ export function detectClients() {
7
+ const clients = [];
8
+ const desktopConfig = claudeDesktopConfigPath();
9
+ const desktopFound = fs.existsSync(desktopConfig);
10
+ clients.push({
11
+ id: 'claude-desktop',
12
+ label: 'Claude Desktop',
13
+ detected: desktopFound,
14
+ detail: desktopFound ? `config at ${desktopConfig}` : `no config at ${desktopConfig}`,
15
+ });
16
+ const claudeBin = claudeBinaryPath();
17
+ clients.push({
18
+ id: 'claude-code',
19
+ label: 'Claude Code',
20
+ detected: claudeBin !== null,
21
+ detail: claudeBin ? `claude binary at ${claudeBin}` : 'claude binary not found on PATH',
22
+ });
23
+ const gooseConfig = gooseConfigPath();
24
+ const gooseConfigFound = fs.existsSync(gooseConfig);
25
+ const gooseBin = findOnPath('goose');
26
+ const gooseFound = gooseConfigFound || gooseBin !== null;
27
+ const gooseBits = [
28
+ gooseBin ? `goose binary at ${gooseBin}` : null,
29
+ gooseConfigFound ? `config at ${gooseConfig}` : null,
30
+ ].filter(Boolean);
31
+ clients.push({
32
+ id: 'goose',
33
+ label: 'Goose (CLI + Desktop)',
34
+ detected: gooseFound,
35
+ detail: gooseFound ? gooseBits.join(', ') : `no goose binary on PATH and no config at ${gooseConfig}`,
36
+ });
37
+ clients.push({
38
+ id: 'chatgpt',
39
+ label: 'ChatGPT',
40
+ detected: true,
41
+ detail: 'connectors are web-managed; instructions only',
42
+ });
43
+ return clients;
44
+ }
45
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,MAAM,aAAa,GAAG,uBAAuB,EAAE,CAAC;IAChD,MAAM,YAAY,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC;QACX,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,aAAa,EAAE,CAAC,CAAC,CAAC,gBAAgB,aAAa,EAAE;KACtF,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC;QACX,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,SAAS,KAAK,IAAI;QAC5B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC,CAAC,iCAAiC;KACxF,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,gBAAgB,GAAG,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,gBAAgB,IAAI,QAAQ,KAAK,IAAI,CAAC;IACzD,MAAM,SAAS,GAAG;QAChB,QAAQ,CAAC,CAAC,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI;QAC/C,gBAAgB,CAAC,CAAC,CAAC,aAAa,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;KACrD,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,CAAC,IAAI,CAAC;QACX,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,uBAAuB;QAC9B,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,4CAA4C,WAAW,EAAE;KACtG,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,CAAC;QACX,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,+CAA+C;KACxD,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { CLIENT_IDS } from './types.js';
4
+ import { DEFAULT_SERVER_URL, deriveName, validateServerUrl } from './util.js';
5
+ import { detectClients } from './detect.js';
6
+ import { confirm, selectClients } from './prompt.js';
7
+ import { applyClaudeDesktopConfig, claudeDesktopConfigPath } from './clients/claude-desktop.js';
8
+ import { claudeCodeAddCommand, formatCommand, runClaudeCodeAdd } from './clients/claude-code.js';
9
+ import { applyGooseConfig, gooseConfigPath, gooseDeepLink } from './clients/goose.js';
10
+ import { chatgptInstructions } from './clients/chatgpt.js';
11
+ const HELP = `gatewaze-connect — connect your AI clients to a Gatewaze MCP server
12
+
13
+ Usage:
14
+ npx @gatewaze/connect [options]
15
+
16
+ Options:
17
+ --server <url> MCP server URL (default: ${DEFAULT_SERVER_URL})
18
+ --name <label> Connector name (default: derived from the server hostname)
19
+ --all Configure every detected client, no prompts
20
+ --client <id> Configure a specific client (repeatable). One of:
21
+ ${CLIENT_IDS.join(', ')}
22
+ --force Overwrite an existing entry with the same name
23
+ --dry-run Show what would change without writing anything
24
+ -h, --help Show this help
25
+
26
+ Examples:
27
+ npx @gatewaze/connect
28
+ npx @gatewaze/connect --server https://mcp.aaif.live --all
29
+ npx @gatewaze/connect --client claude-desktop --client goose --dry-run
30
+ `;
31
+ function parseCliArgs(argv) {
32
+ const { values } = parseArgs({
33
+ args: argv,
34
+ options: {
35
+ server: { type: 'string' },
36
+ name: { type: 'string' },
37
+ all: { type: 'boolean', default: false },
38
+ client: { type: 'string', multiple: true },
39
+ force: { type: 'boolean', default: false },
40
+ 'dry-run': { type: 'boolean', default: false },
41
+ help: { type: 'boolean', short: 'h', default: false },
42
+ },
43
+ });
44
+ if (values.help) {
45
+ console.log(HELP);
46
+ process.exit(0);
47
+ }
48
+ const serverUrl = validateServerUrl(values.server ?? DEFAULT_SERVER_URL);
49
+ const name = values.name?.trim() || deriveName(serverUrl);
50
+ const clients = [];
51
+ for (const raw of values.client ?? []) {
52
+ const id = raw.trim().toLowerCase();
53
+ if (!CLIENT_IDS.includes(id)) {
54
+ throw new Error(`Unknown --client "${raw}". Valid values: ${CLIENT_IDS.join(', ')}`);
55
+ }
56
+ clients.push(id);
57
+ }
58
+ return {
59
+ serverUrl,
60
+ name,
61
+ all: values.all ?? false,
62
+ clients,
63
+ force: values.force ?? false,
64
+ dryRun: values['dry-run'] ?? false,
65
+ };
66
+ }
67
+ function reportApply(result) {
68
+ const prefix = result.status === 'error' ? ' !!' : ' ->';
69
+ console.log(`${prefix} ${result.message}`);
70
+ if (result.backupPath) {
71
+ console.log(` -> Backed up previous config to ${result.backupPath}`);
72
+ }
73
+ }
74
+ async function resolveConflict(label, result, retry) {
75
+ if (result.status !== 'conflict')
76
+ return result;
77
+ console.log(` !! ${result.message}`);
78
+ if (process.stdin.isTTY && process.stdout.isTTY) {
79
+ const yes = await confirm(` Overwrite the existing "${label}" entry?`, false);
80
+ if (yes)
81
+ return retry(true);
82
+ return { status: 'unchanged', message: 'Left the existing entry untouched.' };
83
+ }
84
+ return {
85
+ status: 'unchanged',
86
+ message: 'Left the existing entry untouched (re-run with --force to overwrite).',
87
+ };
88
+ }
89
+ async function main() {
90
+ let opts;
91
+ try {
92
+ opts = parseCliArgs(process.argv.slice(2));
93
+ }
94
+ catch (err) {
95
+ console.error(`Error: ${err.message}\n`);
96
+ console.error(HELP);
97
+ process.exit(2);
98
+ }
99
+ console.log('gatewaze-connect');
100
+ console.log(` Server: ${opts.serverUrl}`);
101
+ console.log(` Connector name: ${opts.name}`);
102
+ if (opts.dryRun)
103
+ console.log(' Mode: dry-run (nothing will be written)');
104
+ const detected = detectClients();
105
+ let targets;
106
+ if (opts.clients.length > 0) {
107
+ targets = detected.filter((c) => opts.clients.includes(c.id));
108
+ }
109
+ else if (opts.all) {
110
+ targets = detected.filter((c) => c.detected);
111
+ }
112
+ else if (process.stdin.isTTY && process.stdout.isTTY) {
113
+ targets = await selectClients(detected);
114
+ }
115
+ else {
116
+ console.error('\nNo TTY available for interactive selection. Re-run with --all or one or more --client flags.');
117
+ process.exit(2);
118
+ }
119
+ if (targets.length === 0) {
120
+ console.log('\nNothing selected — no changes made.');
121
+ return;
122
+ }
123
+ console.log('');
124
+ const nextSteps = [];
125
+ let hadError = false;
126
+ for (const target of targets) {
127
+ console.log(`${target.label}:`);
128
+ if (!target.detected) {
129
+ console.log(` -> Not detected (${target.detail}) — configuring anyway as requested.`);
130
+ }
131
+ switch (target.id) {
132
+ case 'claude-desktop': {
133
+ const configPath = claudeDesktopConfigPath();
134
+ let result = applyClaudeDesktopConfig(configPath, opts.name, opts.serverUrl, {
135
+ overwrite: opts.force,
136
+ dryRun: opts.dryRun,
137
+ });
138
+ result = await resolveConflict(target.label, result, (overwrite) => applyClaudeDesktopConfig(configPath, opts.name, opts.serverUrl, {
139
+ overwrite,
140
+ dryRun: opts.dryRun,
141
+ }));
142
+ reportApply(result);
143
+ if (result.status === 'error')
144
+ hadError = true;
145
+ if (result.status === 'added' || result.status === 'updated') {
146
+ nextSteps.push('Claude Desktop: restart the app, then use the connector once — the first use opens your sign-in page in the browser (via mcp-remote).');
147
+ }
148
+ break;
149
+ }
150
+ case 'claude-code': {
151
+ if (opts.dryRun) {
152
+ console.log(` -> Would run: ${formatCommand(claudeCodeAddCommand(opts.name, opts.serverUrl))}`);
153
+ }
154
+ else {
155
+ const result = runClaudeCodeAdd(opts.name, opts.serverUrl);
156
+ if (result.ok) {
157
+ console.log(` -> ${result.message}`);
158
+ nextSteps.push('Claude Code: run /mcp in any session to check the connection — first use opens your sign-in page.');
159
+ }
160
+ else {
161
+ hadError = true;
162
+ console.log(` !! ${result.message}`);
163
+ console.log(` -> Run this yourself to register the server:\n ${result.command}`);
164
+ }
165
+ }
166
+ break;
167
+ }
168
+ case 'goose': {
169
+ const configPath = gooseConfigPath();
170
+ let result = applyGooseConfig(configPath, opts.name, opts.serverUrl, {
171
+ overwrite: opts.force,
172
+ dryRun: opts.dryRun,
173
+ });
174
+ result = await resolveConflict(target.label, result, (overwrite) => applyGooseConfig(configPath, opts.name, opts.serverUrl, { overwrite, dryRun: opts.dryRun }));
175
+ reportApply(result);
176
+ if (result.status === 'error')
177
+ hadError = true;
178
+ console.log(` -> Goose Desktop one-click alternative: ${gooseDeepLink(opts.name, opts.serverUrl)}`);
179
+ if (result.status === 'added' || result.status === 'updated') {
180
+ nextSteps.push('Goose: restart Goose (Desktop) or start a new goose session (CLI) — first use of the extension opens your sign-in page.');
181
+ }
182
+ break;
183
+ }
184
+ case 'chatgpt': {
185
+ for (const line of chatgptInstructions(opts.name, opts.serverUrl)) {
186
+ console.log(` ${line}`);
187
+ }
188
+ break;
189
+ }
190
+ }
191
+ console.log('');
192
+ }
193
+ if (nextSteps.length > 0) {
194
+ console.log('Next steps:');
195
+ for (const step of nextSteps)
196
+ console.log(` - ${step}`);
197
+ }
198
+ else if (opts.dryRun) {
199
+ console.log('Dry-run complete — nothing was written.');
200
+ }
201
+ if (hadError)
202
+ process.exitCode = 1;
203
+ }
204
+ main().catch((err) => {
205
+ console.error(`Unexpected error: ${err.stack ?? err}`);
206
+ process.exit(1);
207
+ });
208
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/prompt.js ADDED
@@ -0,0 +1,55 @@
1
+ import readline from 'node:readline/promises';
2
+ function rlInterface() {
3
+ return readline.createInterface({ input: process.stdin, output: process.stdout });
4
+ }
5
+ export async function confirm(question, defaultYes = false) {
6
+ const rl = rlInterface();
7
+ try {
8
+ const suffix = defaultYes ? '[Y/n]' : '[y/N]';
9
+ const answer = (await rl.question(`${question} ${suffix} `)).trim().toLowerCase();
10
+ if (answer === '')
11
+ return defaultYes;
12
+ return answer === 'y' || answer === 'yes';
13
+ }
14
+ finally {
15
+ rl.close();
16
+ }
17
+ }
18
+ /**
19
+ * Checkbox-style selection over detected clients: detected clients are
20
+ * pre-ticked; the user can accept the selection or type numbers to change it.
21
+ */
22
+ export async function selectClients(clients) {
23
+ console.log('\nDetected AI clients:\n');
24
+ clients.forEach((c, i) => {
25
+ const box = c.detected ? '[x]' : '[ ]';
26
+ console.log(` ${i + 1}. ${box} ${c.label} (${c.detail})`);
27
+ });
28
+ console.log('');
29
+ const rl = rlInterface();
30
+ try {
31
+ const answer = (await rl.question('Which should be connected? Enter numbers (e.g. 1,3), "all", or press Enter for all detected: '))
32
+ .trim()
33
+ .toLowerCase();
34
+ if (answer === '')
35
+ return clients.filter((c) => c.detected);
36
+ if (answer === 'all' || answer === 'a')
37
+ return clients;
38
+ if (answer === 'none' || answer === 'n')
39
+ return [];
40
+ const picked = new Set();
41
+ for (const part of answer.split(/[\s,]+/).filter(Boolean)) {
42
+ const n = Number(part);
43
+ if (!Number.isInteger(n) || n < 1 || n > clients.length) {
44
+ console.log(`Ignoring "${part}" (expected a number between 1 and ${clients.length}).`);
45
+ continue;
46
+ }
47
+ picked.add(n - 1);
48
+ }
49
+ return clients.filter((_, i) => picked.has(i));
50
+ }
51
+ finally {
52
+ rl.close();
53
+ }
54
+ }
55
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export const CLIENT_IDS = ['claude-desktop', 'claude-code', 'goose', 'chatgpt'];
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAe,CAAC,gBAAgB,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC"}
package/dist/util.js ADDED
@@ -0,0 +1,92 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export const DEFAULT_SERVER_URL = 'https://mcp.aaif.live/';
4
+ /**
5
+ * Derive a short connector name from the server URL's hostname.
6
+ * e.g. https://mcp.aaif.live/ -> "aaif", https://mcp.example.test/ -> "example".
7
+ */
8
+ export function deriveName(serverUrl) {
9
+ let hostname;
10
+ try {
11
+ hostname = new URL(serverUrl).hostname;
12
+ }
13
+ catch {
14
+ return 'gatewaze';
15
+ }
16
+ const generic = new Set(['mcp', 'www', 'api', 'connect']);
17
+ const labels = hostname.split('.').filter(Boolean);
18
+ const core = labels.find((l) => !generic.has(l.toLowerCase())) ?? labels[0] ?? 'gatewaze';
19
+ const cleaned = core.toLowerCase().replace(/[^a-z0-9_-]/g, '');
20
+ return cleaned || 'gatewaze';
21
+ }
22
+ export function validateServerUrl(raw) {
23
+ let url;
24
+ try {
25
+ url = new URL(raw);
26
+ }
27
+ catch {
28
+ throw new Error(`Invalid --server URL: ${raw}`);
29
+ }
30
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') {
31
+ throw new Error(`--server must be an http(s) URL, got: ${raw}`);
32
+ }
33
+ return url.toString();
34
+ }
35
+ /** Timestamp suitable for backup file suffixes: YYYYMMDD-HHMMSS */
36
+ export function backupTimestamp(date = new Date()) {
37
+ const p = (n, w = 2) => String(n).padStart(w, '0');
38
+ return (`${date.getFullYear()}${p(date.getMonth() + 1)}${p(date.getDate())}` +
39
+ `-${p(date.getHours())}${p(date.getMinutes())}${p(date.getSeconds())}`);
40
+ }
41
+ /** Copy `filePath` to `<filePath>.bak-<timestamp>` and return the backup path. */
42
+ export function backupFile(filePath, date = new Date()) {
43
+ const backupPath = `${filePath}.bak-${backupTimestamp(date)}`;
44
+ fs.copyFileSync(filePath, backupPath);
45
+ return backupPath;
46
+ }
47
+ export function ensureParentDir(filePath) {
48
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
49
+ }
50
+ export function deepEqual(a, b) {
51
+ if (a === b)
52
+ return true;
53
+ if (typeof a !== typeof b)
54
+ return false;
55
+ if (Array.isArray(a) || Array.isArray(b)) {
56
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
57
+ return false;
58
+ return a.every((v, i) => deepEqual(v, b[i]));
59
+ }
60
+ if (a && b && typeof a === 'object') {
61
+ const ka = Object.keys(a);
62
+ const kb = Object.keys(b);
63
+ if (ka.length !== kb.length)
64
+ return false;
65
+ return ka.every((k) => deepEqual(a[k], b[k]));
66
+ }
67
+ return false;
68
+ }
69
+ /** Locate an executable on PATH (cross-platform, no shelling out). */
70
+ export function findOnPath(binary) {
71
+ const pathVar = process.env.PATH ?? '';
72
+ const dirs = pathVar.split(path.delimiter).filter(Boolean);
73
+ const exts = process.platform === 'win32'
74
+ ? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';')
75
+ : [''];
76
+ for (const dir of dirs) {
77
+ for (const ext of exts) {
78
+ const candidate = path.join(dir, binary + ext.toLowerCase());
79
+ try {
80
+ fs.accessSync(candidate, fs.constants.X_OK);
81
+ if (fs.statSync(candidate).isFile() || fs.lstatSync(candidate).isSymbolicLink()) {
82
+ return candidate;
83
+ }
84
+ }
85
+ catch {
86
+ // keep looking
87
+ }
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@gatewaze/connect",
3
+ "version": "0.1.0",
4
+ "description": "One-command setup that connects your AI clients (Claude Desktop, Claude Code, Goose, ChatGPT) to a Gatewaze MCP server.",
5
+ "type": "module",
6
+ "bin": {
7
+ "gatewaze-connect": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "license": "Apache-2.0",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/gatewaze/gatewaze.git",
22
+ "directory": "packages/connect"
23
+ },
24
+ "scripts": {
25
+ "dev": "tsx src/index.ts",
26
+ "build": "tsc",
27
+ "test": "tsx --test test/*.test.ts",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "yaml": "^2.5.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.0.0",
35
+ "tsx": "^4.19.0",
36
+ "typescript": "^5.6.0"
37
+ }
38
+ }