@metalabdesign/mcp-client 1.0.2 → 1.0.3
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/CHANGELOG.md +5 -0
- package/package.json +6 -1
- package/src/bin/cli.ts +0 -242
- package/src/bin/metalab-mcp-config.ts +0 -262
- package/src/bin/metalab-mcp.ts +0 -284
- package/src/config/aws-sso.ts +0 -78
- package/src/config/defaults.ts +0 -26
- package/src/config/index.ts +0 -8
- package/src/index.ts +0 -54
- package/src/oauth.ts +0 -540
- package/src/proxy.ts +0 -274
- package/src/storage.ts +0 -81
- package/src/types.ts +0 -79
- package/src/utils.ts +0 -115
- package/tsconfig.json +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ All notable changes to `@metalabdesign/mcp-client` will be documented in this fi
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.3] - 2026-01-28
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Added `files` field to package.json so README.md and CHANGELOG.md are included in npm package
|
|
12
|
+
|
|
8
13
|
## [1.0.2] - 2026-01-28
|
|
9
14
|
|
|
10
15
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metalabdesign/mcp-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "MCP client for connecting to Metalab's MCP Gateway with OAuth 2.1 authentication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"CHANGELOG.md"
|
|
26
|
+
],
|
|
22
27
|
"keywords": [
|
|
23
28
|
"mcp",
|
|
24
29
|
"oauth",
|
package/src/bin/cli.ts
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* MCP Local Proxy CLI
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* mcp-local-proxy --remote-url <url> --callback-url <url> [options]
|
|
7
|
-
*
|
|
8
|
-
* Example:
|
|
9
|
-
* mcp-local-proxy \
|
|
10
|
-
* --remote-url https://mcp.example.com/mcp \
|
|
11
|
-
* --callback-url http://localhost:9876/callback
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { createProxy } from '../index.js';
|
|
15
|
-
|
|
16
|
-
interface CliArgs {
|
|
17
|
-
remoteUrl?: string;
|
|
18
|
-
callbackUrl?: string;
|
|
19
|
-
tokenStorageDir?: string;
|
|
20
|
-
timeout?: number;
|
|
21
|
-
openBrowser?: boolean;
|
|
22
|
-
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
23
|
-
serviceTokens?: Record<string, string>;
|
|
24
|
-
help?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function printHelp(): void {
|
|
28
|
-
console.error(`
|
|
29
|
-
MCP Local Proxy - Connect to remote OAuth-protected MCP servers
|
|
30
|
-
|
|
31
|
-
USAGE:
|
|
32
|
-
mcp-local-proxy --remote-url <url> --callback-url <url> [options]
|
|
33
|
-
|
|
34
|
-
REQUIRED:
|
|
35
|
-
--remote-url <url> Remote MCP server URL
|
|
36
|
-
--callback-url <url> OAuth callback URL with port (e.g., http://localhost:9876/callback)
|
|
37
|
-
|
|
38
|
-
OPTIONS:
|
|
39
|
-
--token-dir <dir> Directory for token storage (default: ~/.metalab/mcp-local-proxy/tokens)
|
|
40
|
-
--timeout <ms> Request timeout in milliseconds (default: 30000)
|
|
41
|
-
--no-browser Don't auto-open browser for auth
|
|
42
|
-
--log-level <level> Log level: debug, info, warn, error (default: info)
|
|
43
|
-
--figma-token <token> Figma access token (alternative: FIGMA_ACCESS_TOKEN env var)
|
|
44
|
-
--notion-token <token> Notion access token (alternative: NOTION_ACCESS_TOKEN env var)
|
|
45
|
-
--help, -h Show this help message
|
|
46
|
-
|
|
47
|
-
EXAMPLES:
|
|
48
|
-
# Basic usage
|
|
49
|
-
mcp-local-proxy \\
|
|
50
|
-
--remote-url https://mcp.example.com/mcp \\
|
|
51
|
-
--callback-url http://localhost:9876/callback
|
|
52
|
-
|
|
53
|
-
# With custom callback port (specify in URL)
|
|
54
|
-
mcp-local-proxy \\
|
|
55
|
-
--remote-url https://mcp.example.com/mcp \\
|
|
56
|
-
--callback-url http://localhost:3000/oauth/callback
|
|
57
|
-
|
|
58
|
-
# Debug mode
|
|
59
|
-
mcp-local-proxy \\
|
|
60
|
-
--remote-url https://mcp.example.com/mcp \\
|
|
61
|
-
--callback-url http://localhost:9876/callback \\
|
|
62
|
-
--log-level debug
|
|
63
|
-
|
|
64
|
-
# With Figma token
|
|
65
|
-
mcp-local-proxy \\
|
|
66
|
-
--remote-url https://mcp.example.com/mcp \\
|
|
67
|
-
--callback-url http://localhost:9876/callback \\
|
|
68
|
-
--figma-token figd_xxxxx
|
|
69
|
-
|
|
70
|
-
# Multiple service tokens
|
|
71
|
-
mcp-local-proxy \\
|
|
72
|
-
--remote-url https://mcp.example.com/mcp \\
|
|
73
|
-
--callback-url http://localhost:9876/callback \\
|
|
74
|
-
--figma-token figd_xxxxx \\
|
|
75
|
-
--notion-token secret_xxxxx
|
|
76
|
-
|
|
77
|
-
CLAUDE DESKTOP CONFIGURATION:
|
|
78
|
-
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
|
|
79
|
-
|
|
80
|
-
# Basic configuration
|
|
81
|
-
{
|
|
82
|
-
"mcpServers": {
|
|
83
|
-
"my-remote-server": {
|
|
84
|
-
"command": "npx",
|
|
85
|
-
"args": [
|
|
86
|
-
"@metalab/mcp-local-proxy",
|
|
87
|
-
"--remote-url", "https://mcp.example.com/mcp",
|
|
88
|
-
"--callback-url", "http://localhost:9876/callback"
|
|
89
|
-
]
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
# With service tokens (CLI args)
|
|
95
|
-
{
|
|
96
|
-
"mcpServers": {
|
|
97
|
-
"metalab-mcp": {
|
|
98
|
-
"command": "npx",
|
|
99
|
-
"args": [
|
|
100
|
-
"@metalab/mcp-local-proxy",
|
|
101
|
-
"--remote-url", "https://mcp.example.com/mcp",
|
|
102
|
-
"--callback-url", "http://localhost:9876/callback",
|
|
103
|
-
"--figma-token", "figd_xxxxx"
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# With service tokens (environment variables)
|
|
110
|
-
{
|
|
111
|
-
"mcpServers": {
|
|
112
|
-
"metalab-mcp": {
|
|
113
|
-
"command": "npx",
|
|
114
|
-
"args": [
|
|
115
|
-
"@metalab/mcp-local-proxy",
|
|
116
|
-
"--remote-url", "https://mcp.example.com/mcp",
|
|
117
|
-
"--callback-url", "http://localhost:9876/callback"
|
|
118
|
-
],
|
|
119
|
-
"env": {
|
|
120
|
-
"FIGMA_ACCESS_TOKEN": "figd_xxxxx"
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function parseArgs(argv: string[]): CliArgs {
|
|
129
|
-
const args: CliArgs = {
|
|
130
|
-
serviceTokens: {},
|
|
131
|
-
};
|
|
132
|
-
let i = 2; // Skip 'node' and script path
|
|
133
|
-
|
|
134
|
-
while (i < argv.length) {
|
|
135
|
-
const arg = argv[i];
|
|
136
|
-
|
|
137
|
-
switch (arg) {
|
|
138
|
-
case '--remote-url':
|
|
139
|
-
args.remoteUrl = argv[++i];
|
|
140
|
-
break;
|
|
141
|
-
case '--callback-url':
|
|
142
|
-
args.callbackUrl = argv[++i];
|
|
143
|
-
break;
|
|
144
|
-
case '--token-dir':
|
|
145
|
-
args.tokenStorageDir = argv[++i];
|
|
146
|
-
break;
|
|
147
|
-
case '--timeout':
|
|
148
|
-
args.timeout = Number.parseInt(argv[++i] ?? '', 10);
|
|
149
|
-
break;
|
|
150
|
-
case '--no-browser':
|
|
151
|
-
args.openBrowser = false;
|
|
152
|
-
break;
|
|
153
|
-
case '--log-level':
|
|
154
|
-
args.logLevel = argv[++i] as CliArgs['logLevel'];
|
|
155
|
-
break;
|
|
156
|
-
case '--figma-token':
|
|
157
|
-
case '--figma':
|
|
158
|
-
args.serviceTokens!.figma = argv[++i] ?? '';
|
|
159
|
-
break;
|
|
160
|
-
case '--notion-token':
|
|
161
|
-
case '--notion':
|
|
162
|
-
args.serviceTokens!.notion = argv[++i] ?? '';
|
|
163
|
-
break;
|
|
164
|
-
case '--help':
|
|
165
|
-
case '-h':
|
|
166
|
-
args.help = true;
|
|
167
|
-
break;
|
|
168
|
-
default:
|
|
169
|
-
if (arg?.startsWith('-')) {
|
|
170
|
-
console.error(`Unknown option: ${arg}`);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
i++;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Load service tokens from environment variables (fallback)
|
|
178
|
-
if (process.env.FIGMA_ACCESS_TOKEN) {
|
|
179
|
-
args.serviceTokens!.figma = args.serviceTokens!.figma || process.env.FIGMA_ACCESS_TOKEN;
|
|
180
|
-
}
|
|
181
|
-
if (process.env.NOTION_ACCESS_TOKEN) {
|
|
182
|
-
args.serviceTokens!.notion = args.serviceTokens!.notion || process.env.NOTION_ACCESS_TOKEN;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Clean up empty serviceTokens object
|
|
186
|
-
if (Object.keys(args.serviceTokens!).length === 0) {
|
|
187
|
-
delete args.serviceTokens;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return args;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async function main(): Promise<void> {
|
|
194
|
-
const args = parseArgs(process.argv);
|
|
195
|
-
|
|
196
|
-
if (args.help) {
|
|
197
|
-
printHelp();
|
|
198
|
-
process.exit(0);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (!args.remoteUrl) {
|
|
202
|
-
console.error('Error: --remote-url is required');
|
|
203
|
-
console.error('Run with --help for usage information');
|
|
204
|
-
process.exit(1);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (!args.callbackUrl) {
|
|
208
|
-
console.error('Error: --callback-url is required');
|
|
209
|
-
console.error('Run with --help for usage information');
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
const proxy = await createProxy({
|
|
215
|
-
remoteUrl: args.remoteUrl,
|
|
216
|
-
callbackUrl: args.callbackUrl,
|
|
217
|
-
tokenStorageDir: args.tokenStorageDir,
|
|
218
|
-
timeout: args.timeout,
|
|
219
|
-
openBrowser: args.openBrowser ?? true,
|
|
220
|
-
logLevel: args.logLevel ?? 'info',
|
|
221
|
-
serviceTokens: args.serviceTokens,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Handle shutdown gracefully
|
|
225
|
-
const shutdown = async (): Promise<void> => {
|
|
226
|
-
console.error('Shutting down...');
|
|
227
|
-
await proxy.disconnect();
|
|
228
|
-
process.exit(0);
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
process.on('SIGINT', shutdown);
|
|
232
|
-
process.on('SIGTERM', shutdown);
|
|
233
|
-
|
|
234
|
-
// Start the proxy
|
|
235
|
-
await proxy.start();
|
|
236
|
-
} catch (error) {
|
|
237
|
-
console.error('Failed to start proxy:', error);
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
main();
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Metalab MCP Config Generator
|
|
4
|
-
*
|
|
5
|
-
* Generates JSON configuration for various MCP clients like Claude Desktop,
|
|
6
|
-
* Windsurf, Cursor, and VS Code.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as fs from 'node:fs';
|
|
10
|
-
import * as path from 'node:path';
|
|
11
|
-
import * as os from 'node:os';
|
|
12
|
-
import * as readline from 'node:readline';
|
|
13
|
-
|
|
14
|
-
interface ClientConfig {
|
|
15
|
-
name: string;
|
|
16
|
-
configPath: string;
|
|
17
|
-
description: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const CLIENTS: Record<string, ClientConfig> = {
|
|
21
|
-
claude: {
|
|
22
|
-
name: 'Claude Desktop',
|
|
23
|
-
configPath:
|
|
24
|
-
process.platform === 'darwin'
|
|
25
|
-
? path.join(os.homedir(), 'Library/Application Support/Claude/claude_desktop_config.json')
|
|
26
|
-
: process.platform === 'win32'
|
|
27
|
-
? path.join(process.env.APPDATA || '', 'Claude/claude_desktop_config.json')
|
|
28
|
-
: path.join(os.homedir(), '.config/Claude/claude_desktop_config.json'),
|
|
29
|
-
description: 'Anthropic Claude Desktop application',
|
|
30
|
-
},
|
|
31
|
-
windsurf: {
|
|
32
|
-
name: 'Windsurf',
|
|
33
|
-
configPath: path.join(os.homedir(), '.codeium/windsurf/mcp_config.json'),
|
|
34
|
-
description: 'Codeium Windsurf IDE',
|
|
35
|
-
},
|
|
36
|
-
cursor: {
|
|
37
|
-
name: 'Cursor',
|
|
38
|
-
configPath: path.join(os.homedir(), '.cursor/mcp.json'),
|
|
39
|
-
description: 'Cursor IDE',
|
|
40
|
-
},
|
|
41
|
-
cline: {
|
|
42
|
-
name: 'VS Code (Cline)',
|
|
43
|
-
configPath: path.join(process.cwd(), '.vscode/cline_mcp_settings.json'),
|
|
44
|
-
description: 'VS Code with Cline extension (project-local)',
|
|
45
|
-
},
|
|
46
|
-
vscode: {
|
|
47
|
-
name: 'VS Code (Cline)',
|
|
48
|
-
configPath: path.join(process.cwd(), '.vscode/cline_mcp_settings.json'),
|
|
49
|
-
description: 'Alias for cline',
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
interface CliArgs {
|
|
54
|
-
client?: string;
|
|
55
|
-
figmaToken?: string;
|
|
56
|
-
notionToken?: string;
|
|
57
|
-
output?: boolean;
|
|
58
|
-
help?: boolean;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function printHelp(): void {
|
|
62
|
-
console.error(`
|
|
63
|
-
Metalab MCP Config Generator
|
|
64
|
-
|
|
65
|
-
USAGE:
|
|
66
|
-
metalab-mcp-config [options]
|
|
67
|
-
|
|
68
|
-
OPTIONS:
|
|
69
|
-
--client <name> Generate config for specific client
|
|
70
|
-
Options: claude, windsurf, cursor, cline, vscode
|
|
71
|
-
--figma-token <token> Include Figma token in config
|
|
72
|
-
--notion-token <token> Include Notion token in config
|
|
73
|
-
--output Write config directly to client's config file
|
|
74
|
-
--help, -h Show this help message
|
|
75
|
-
|
|
76
|
-
EXAMPLES:
|
|
77
|
-
# Interactive mode (prompts for client)
|
|
78
|
-
metalab-mcp-config
|
|
79
|
-
|
|
80
|
-
# Generate Claude Desktop config
|
|
81
|
-
metalab-mcp-config --client claude
|
|
82
|
-
|
|
83
|
-
# Generate config with tokens
|
|
84
|
-
metalab-mcp-config --client claude --figma-token figd_xxxxx
|
|
85
|
-
|
|
86
|
-
# Write directly to config file
|
|
87
|
-
metalab-mcp-config --client claude --output
|
|
88
|
-
|
|
89
|
-
SUPPORTED CLIENTS:
|
|
90
|
-
claude - Claude Desktop (macOS/Windows/Linux)
|
|
91
|
-
windsurf - Codeium Windsurf IDE
|
|
92
|
-
cursor - Cursor IDE
|
|
93
|
-
cline - VS Code with Cline extension
|
|
94
|
-
vscode - Alias for cline
|
|
95
|
-
`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function parseArgs(argv: string[]): CliArgs {
|
|
99
|
-
const args: CliArgs = {};
|
|
100
|
-
let i = 2;
|
|
101
|
-
|
|
102
|
-
while (i < argv.length) {
|
|
103
|
-
const arg = argv[i];
|
|
104
|
-
|
|
105
|
-
switch (arg) {
|
|
106
|
-
case '--client':
|
|
107
|
-
args.client = argv[++i];
|
|
108
|
-
break;
|
|
109
|
-
case '--figma-token':
|
|
110
|
-
args.figmaToken = argv[++i];
|
|
111
|
-
break;
|
|
112
|
-
case '--notion-token':
|
|
113
|
-
args.notionToken = argv[++i];
|
|
114
|
-
break;
|
|
115
|
-
case '--output':
|
|
116
|
-
args.output = true;
|
|
117
|
-
break;
|
|
118
|
-
case '--help':
|
|
119
|
-
case '-h':
|
|
120
|
-
args.help = true;
|
|
121
|
-
break;
|
|
122
|
-
default:
|
|
123
|
-
if (arg?.startsWith('-')) {
|
|
124
|
-
console.error(`Unknown option: ${arg}`);
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
i++;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Load tokens from env if not provided
|
|
132
|
-
if (!args.figmaToken && process.env.FIGMA_ACCESS_TOKEN) {
|
|
133
|
-
args.figmaToken = process.env.FIGMA_ACCESS_TOKEN;
|
|
134
|
-
}
|
|
135
|
-
if (!args.notionToken && process.env.NOTION_ACCESS_TOKEN) {
|
|
136
|
-
args.notionToken = process.env.NOTION_ACCESS_TOKEN;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return args;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function generateConfig(args: CliArgs): object {
|
|
143
|
-
const env: Record<string, string> = {};
|
|
144
|
-
|
|
145
|
-
if (args.figmaToken) {
|
|
146
|
-
env.FIGMA_ACCESS_TOKEN = args.figmaToken;
|
|
147
|
-
}
|
|
148
|
-
if (args.notionToken) {
|
|
149
|
-
env.NOTION_ACCESS_TOKEN = args.notionToken;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const serverConfig: Record<string, unknown> = {
|
|
153
|
-
command: 'npx',
|
|
154
|
-
args: ['@metalabdesign/mcp-client'],
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
if (Object.keys(env).length > 0) {
|
|
158
|
-
serverConfig.env = env;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
mcpServers: {
|
|
163
|
-
metalab: serverConfig,
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async function promptForClient(): Promise<string> {
|
|
169
|
-
const rl = readline.createInterface({
|
|
170
|
-
input: process.stdin,
|
|
171
|
-
output: process.stderr,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
console.error('\nSelect an MCP client:\n');
|
|
175
|
-
const clientList: Array<keyof typeof CLIENTS> = ['claude', 'windsurf', 'cursor', 'cline'];
|
|
176
|
-
clientList.forEach((key, i) => {
|
|
177
|
-
const client = CLIENTS[key]!;
|
|
178
|
-
console.error(` ${i + 1}. ${client.name} - ${client.description}`);
|
|
179
|
-
});
|
|
180
|
-
console.error('');
|
|
181
|
-
|
|
182
|
-
return new Promise((resolve) => {
|
|
183
|
-
rl.question('Enter number (1-4): ', (answer) => {
|
|
184
|
-
rl.close();
|
|
185
|
-
const index = parseInt(answer, 10) - 1;
|
|
186
|
-
if (index >= 0 && index < clientList.length) {
|
|
187
|
-
resolve(clientList[index] as string);
|
|
188
|
-
} else {
|
|
189
|
-
console.error('Invalid selection, using claude');
|
|
190
|
-
resolve('claude');
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function mergeConfig(existingPath: string, newConfig: object): object {
|
|
197
|
-
try {
|
|
198
|
-
if (fs.existsSync(existingPath)) {
|
|
199
|
-
const existing = JSON.parse(fs.readFileSync(existingPath, 'utf-8'));
|
|
200
|
-
// Merge mcpServers
|
|
201
|
-
return {
|
|
202
|
-
...existing,
|
|
203
|
-
mcpServers: {
|
|
204
|
-
...(existing.mcpServers || {}),
|
|
205
|
-
...(newConfig as { mcpServers: object }).mcpServers,
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
} catch {
|
|
210
|
-
// If we can't read existing, just use new config
|
|
211
|
-
}
|
|
212
|
-
return newConfig;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
async function main(): Promise<void> {
|
|
216
|
-
const args = parseArgs(process.argv);
|
|
217
|
-
|
|
218
|
-
if (args.help) {
|
|
219
|
-
printHelp();
|
|
220
|
-
process.exit(0);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
let clientKey = args.client;
|
|
224
|
-
|
|
225
|
-
// Interactive mode if no client specified
|
|
226
|
-
if (!clientKey) {
|
|
227
|
-
clientKey = await promptForClient();
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Validate client and get config
|
|
231
|
-
const client = CLIENTS[clientKey];
|
|
232
|
-
if (!client) {
|
|
233
|
-
console.error(`Unknown client: ${clientKey}`);
|
|
234
|
-
console.error(`Valid options: ${Object.keys(CLIENTS).join(', ')}`);
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const config = generateConfig(args);
|
|
239
|
-
|
|
240
|
-
if (args.output) {
|
|
241
|
-
// Write to file
|
|
242
|
-
const configDir = path.dirname(client.configPath);
|
|
243
|
-
if (!fs.existsSync(configDir)) {
|
|
244
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const mergedConfig = mergeConfig(client.configPath, config);
|
|
248
|
-
fs.writeFileSync(client.configPath, JSON.stringify(mergedConfig, null, 2) + '\n');
|
|
249
|
-
|
|
250
|
-
console.error(`\n✓ Configuration written to: ${client.configPath}`);
|
|
251
|
-
console.error(`\nRestart ${client.name} to apply changes.`);
|
|
252
|
-
} else {
|
|
253
|
-
// Print to stdout
|
|
254
|
-
console.error(`\n# Configuration for ${client.name}`);
|
|
255
|
-
console.error(`# File: ${client.configPath}\n`);
|
|
256
|
-
console.log(JSON.stringify(config, null, 2));
|
|
257
|
-
console.error(`\n# Copy this to your ${client.name} config file`);
|
|
258
|
-
console.error(`# Or run with --output to write directly`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
main();
|