@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 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.2",
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();