@deriv-com/fe-mcp-servers 0.0.8 → 0.0.10

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 CHANGED
@@ -4,6 +4,7 @@ A collection of Front-End Model Context Protocol (MCP) servers for reusability a
4
4
 
5
5
  ## šŸ—ļø Project Structure
6
6
 
7
+ ### Source Structure (Development)
7
8
  ```
8
9
  mcps/
9
10
  ā”œā”€ā”€ shift-ai/ # Individual MCP server
@@ -30,14 +31,43 @@ mcps/
30
31
  npm install -g @deriv-com/fe-mcp-servers
31
32
  ```
32
33
 
33
- ### Get Your Configuration Path
34
- Copy and run this command to get the exact path for your MCP configuration:
34
+ ### CLI Commands
35
+
36
+ After installation, use the `fe-mcp` CLI to manage MCP servers:
37
+
38
+ ```bash
39
+ # List all available MCP servers
40
+ fe-mcp list
41
+
42
+ # Interactive config generator - creates file & opens it for copy-paste
43
+ fe-mcp code
44
+
45
+ # Show detailed info about a specific server
46
+ fe-mcp info shift-ai
47
+
48
+ # Output MCP client configuration JSON
49
+ fe-mcp config shift-ai
50
+
51
+ # Show help
52
+ fe-mcp help
53
+
54
+ # Show version
55
+ fe-mcp --version
56
+ ```
57
+
58
+ ### Quick Setup (Recommended)
59
+
60
+ The easiest way to get your MCP config:
35
61
 
36
62
  ```bash
37
- echo "$(npm root -g)/@deriv-com/fe-mcp-servers/dist/SERVER_NAME/mcp-server.js"
63
+ fe-mcp code
38
64
  ```
39
65
 
40
- Replace `SERVER_NAME` with the specific server you want (e.g., `shift-ai`).
66
+ This will:
67
+ 1. Show you a list of available MCP servers
68
+ 2. Ask you to select one
69
+ 3. Generate the configuration JSON with the correct path
70
+ 4. Save it to a file and auto-open it for easy copy-paste
41
71
 
42
72
  ### MCP Configuration Template
43
73
  ```json
@@ -45,7 +75,7 @@ Replace `SERVER_NAME` with the specific server you want (e.g., `shift-ai`).
45
75
  "mcpServers": {
46
76
  "server-name": {
47
77
  "command": "node",
48
- "args": ["<PASTE_PATH_FROM_ABOVE>"]
78
+ "args": ["<PATH_FROM_FE-MCP>"]
49
79
  }
50
80
  }
51
81
  }
@@ -66,11 +96,14 @@ Replace `SERVER_NAME` with the specific server you want (e.g., `shift-ai`).
66
96
  ## šŸ› ļø Development
67
97
 
68
98
  ### Building the Package
99
+ The build process bundles all dependencies into standalone executables:
69
100
  ```bash
70
101
  cd mcps
71
102
  npm run build
72
103
  ```
73
104
 
105
+ This creates bundled files in `dist/` with all dependencies included.
106
+
74
107
  ### Running Tests
75
108
  ```bash
76
109
  npm run test
@@ -79,64 +112,35 @@ npm run test
79
112
  ### Adding New MCP Servers
80
113
 
81
114
  1. Create a new directory in `mcps/`
82
- 2. Add the required structure:
115
+ 2. Add the required **source structure**:
83
116
  ```
84
117
  your-server/
85
118
  ā”œā”€ā”€ src/
86
- │ ā”œā”€ā”€ mcp-server.js # Required: Main MCP server
87
- │ ā”œā”€ā”€ your-logic.js # Your server logic
88
- │ └── test-mcp.js # Optional: Tests
89
- └── README.md # Required: Documentation
119
+ │ ā”œā”€ā”€ mcp-server.js # Main server implementation (entry point)
120
+ │ └── mcp.js # Core functionality
121
+ └── README.md # Server documentation
90
122
  ```
91
- 3. Use standard npm imports (dependencies are resolved from mcps root)
92
- 4. The build script will automatically include it
123
+ 3. Implement your MCP server logic in the `src/` files
124
+ 4. The build process will automatically bundle everything into `dist/your-server/mcp-server.js`
125
+ 5. Users will reference the **bundled file** (not the source) in their MCP configuration
93
126
 
94
- ## Available MCP Servers
95
-
96
- ### šŸš€ shift-ai
97
- AI code generation and analysis server that provides intelligent code suggestions, analysis, and automated code generation capabilities.
98
-
99
- **Status**: āœ… Active
100
- **Location**: `shift-ai/`
101
-
102
- ### šŸ™ github-mcp
103
- GitHub integration server for repository management, issue tracking, and pull request automation.
104
-
105
- **Status**: 🚧 Planned
106
- **Location**: `github-mcp/`
107
-
108
- ### šŸ’¬ slack-mcp
109
- Slack workspace automation server for communication workflows and bot integrations.
110
-
111
- **Status**: 🚧 Planned
112
- **Location**: `slack-mcp/`
113
-
114
- ### šŸ—„ļø database-mcp
115
- Database query and schema management server supporting multiple database systems.
116
-
117
- **Status**: 🚧 Planned
118
- **Location**: `database-mcp/`
119
-
120
- ## šŸ“¦ Distribution
121
-
122
- This package is published to npm as part of the @deriv/ai-tools collection with all dependencies bundled for easy installation and usage.
123
-
124
- ## šŸ”§ Standards
127
+ ### Requirements for New Servers
125
128
 
126
129
  Each MCP server must:
127
- - Have `src/mcp-server.js` as the main entry point
128
- - Use standard npm package imports
129
- - Include comprehensive `README.md` documentation
130
- - Follow consistent coding patterns
131
- - Include test coverage where applicable
130
+ - Have `src/mcp-server.js` as the main entry point (source file)
131
+ - Follow the MCP protocol specification
132
+ - Include comprehensive documentation in README.md
133
+ - The build process will bundle everything into a single executable `dist/server-name/mcp-server.js` file
132
134
 
133
135
  ## šŸ—ļø Architecture
134
136
 
135
- - **Monorepo Structure**: All servers in one package for easy distribution
136
- - **Shared Dependencies**: Common dependencies bundled at mcps level
137
- - **Automatic Discovery**: Build system automatically finds and includes servers
138
- - **Global Installation**: Zero-configuration setup via npm global install
139
- - **Standardized Structure**: Consistent patterns across all servers
140
- - **Protocol**: [Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
141
- - **Runtime**: Node.js
142
- - **Build System**: Automated packaging and distribution
137
+ ### Build Process
138
+ - **Input**: Source files in `src/` directories
139
+ - **Process**: esbuild bundles all dependencies
140
+ - **Output**: Single executable files in `dist/` directories
141
+ - **Distribution**: npm package contains only the `dist/` directory
142
+
143
+ ### Dependency Management
144
+ - All dependencies are bundled into the final executable
145
+ - No external dependency resolution required at runtime
146
+ - Standalone files that work in any Node.js environment
package/bin/fe-mcp.js ADDED
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { execSync, spawn } from 'child_process';
7
+ import readline from 'readline';
8
+ import os from 'os';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const packageRoot = path.join(__dirname, '..');
12
+ const distDir = path.join(packageRoot, 'dist');
13
+ const srcDir = packageRoot;
14
+
15
+ // Read package.json for version
16
+ const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf-8'));
17
+
18
+ // ANSI colors
19
+ const colors = {
20
+ reset: '\x1b[0m',
21
+ bright: '\x1b[1m',
22
+ dim: '\x1b[2m',
23
+ cyan: '\x1b[36m',
24
+ green: '\x1b[32m',
25
+ yellow: '\x1b[33m',
26
+ blue: '\x1b[34m',
27
+ magenta: '\x1b[35m',
28
+ red: '\x1b[31m',
29
+ };
30
+
31
+ const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
32
+
33
+ /**
34
+ * Get all available MCP servers
35
+ */
36
+ function getMcpServers() {
37
+ const servers = [];
38
+
39
+ // Check dist directory for built servers
40
+ if (fs.existsSync(distDir)) {
41
+ const entries = fs.readdirSync(distDir, { withFileTypes: true });
42
+ for (const entry of entries) {
43
+ if (entry.isDirectory()) {
44
+ const mcpServerPath = path.join(distDir, entry.name, 'mcp-server.js');
45
+ const readmePath = path.join(distDir, entry.name, 'README.md');
46
+
47
+ if (fs.existsSync(mcpServerPath)) {
48
+ const server = {
49
+ name: entry.name,
50
+ path: mcpServerPath,
51
+ hasReadme: fs.existsSync(readmePath),
52
+ readmePath: readmePath,
53
+ built: true,
54
+ };
55
+
56
+ // Try to extract description from README
57
+ if (server.hasReadme) {
58
+ const readme = fs.readFileSync(readmePath, 'utf-8');
59
+ const descMatch = readme.match(/^#[^\n]+\n+([^\n#]+)/);
60
+ if (descMatch) {
61
+ server.description = descMatch[1].trim();
62
+ }
63
+ }
64
+
65
+ servers.push(server);
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ // Also check source directories for unbuilt servers
72
+ const srcEntries = fs.readdirSync(srcDir, { withFileTypes: true });
73
+ for (const entry of srcEntries) {
74
+ if (entry.isDirectory() &&
75
+ !['dist', 'bin', 'node_modules', 'scripts'].includes(entry.name) &&
76
+ !entry.name.startsWith('.')) {
77
+ const srcMcpPath = path.join(srcDir, entry.name, 'src', 'mcp-server.js');
78
+
79
+ if (fs.existsSync(srcMcpPath)) {
80
+ // Check if already in servers list (built version)
81
+ const existing = servers.find(s => s.name === entry.name);
82
+ if (!existing) {
83
+ const readmePath = path.join(srcDir, entry.name, 'README.md');
84
+ const server = {
85
+ name: entry.name,
86
+ path: srcMcpPath,
87
+ hasReadme: fs.existsSync(readmePath),
88
+ readmePath: readmePath,
89
+ built: false,
90
+ };
91
+
92
+ if (server.hasReadme) {
93
+ const readme = fs.readFileSync(readmePath, 'utf-8');
94
+ const descMatch = readme.match(/^#[^\n]+\n+([^\n#]+)/);
95
+ if (descMatch) {
96
+ server.description = descMatch[1].trim();
97
+ }
98
+ }
99
+
100
+ servers.push(server);
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ return servers;
107
+ }
108
+
109
+ /**
110
+ * List all available MCP servers
111
+ */
112
+ function listServers() {
113
+ const servers = getMcpServers();
114
+
115
+ if (servers.length === 0) {
116
+ console.log(c('yellow', '\nāš ļø No MCP servers found.'));
117
+ console.log(c('dim', ' Run "npm run build" to build the servers first.\n'));
118
+ return;
119
+ }
120
+
121
+ console.log(c('bright', '\nšŸ“¦ Available MCP Servers\n'));
122
+ console.log(c('dim', '─'.repeat(60)));
123
+
124
+ for (const server of servers) {
125
+ const status = server.built
126
+ ? c('green', 'ā— built')
127
+ : c('yellow', 'ā—‹ source only');
128
+
129
+ console.log(`\n ${c('cyan', server.name)} ${c('dim', `(${status})`)}`);
130
+
131
+ if (server.description) {
132
+ console.log(` ${c('dim', server.description)}`);
133
+ }
134
+ }
135
+
136
+ console.log(c('dim', '\n─'.repeat(60)));
137
+ console.log(c('dim', `\nTotal: ${servers.length} server(s)`));
138
+ console.log(c('dim', `\nUse "${c('cyan', 'fe-mcp info <name>')}" for details`));
139
+ console.log(c('dim', `Use "${c('cyan', 'fe-mcp path <name>')}" to get the executable path\n`));
140
+ }
141
+
142
+ /**
143
+ * Show detailed info about a specific MCP server
144
+ */
145
+ function showInfo(serverName) {
146
+ const servers = getMcpServers();
147
+ const server = servers.find(s => s.name === serverName);
148
+
149
+ if (!server) {
150
+ console.log(c('red', `\nāŒ MCP server "${serverName}" not found.\n`));
151
+ console.log(c('dim', 'Available servers:'));
152
+ servers.forEach(s => console.log(c('dim', ` - ${s.name}`)));
153
+ console.log();
154
+ process.exit(1);
155
+ }
156
+
157
+ console.log(c('bright', `\nšŸ“¦ ${server.name}\n`));
158
+ console.log(c('dim', '─'.repeat(60)));
159
+
160
+ if (server.description) {
161
+ console.log(`\n${c('bright', 'Description:')}`);
162
+ console.log(` ${server.description}`);
163
+ }
164
+
165
+ console.log(`\n${c('bright', 'Status:')} ${server.built ? c('green', 'Built āœ“') : c('yellow', 'Not built')}`);
166
+ console.log(`${c('bright', 'Path:')} ${server.path}`);
167
+
168
+ if (server.built) {
169
+ console.log(`\n${c('bright', 'MCP Configuration:')}`);
170
+ console.log(c('dim', ' Add this to your MCP client config:\n'));
171
+
172
+ const config = {
173
+ [server.name]: {
174
+ command: 'node',
175
+ args: [server.path]
176
+ }
177
+ };
178
+
179
+ console.log(c('cyan', JSON.stringify({ mcpServers: config }, null, 2)));
180
+ }
181
+
182
+ if (server.hasReadme) {
183
+ console.log(`\n${c('bright', 'Documentation:')} ${server.readmePath}`);
184
+ }
185
+
186
+ console.log(c('dim', '\n─'.repeat(60) + '\n'));
187
+ }
188
+
189
+ /**
190
+ * Show help
191
+ */
192
+ function showHelp() {
193
+ console.log(`
194
+ ${c('bright', '@deriv-com/fe-mcp-servers CLI')}
195
+
196
+ ${c('bright', 'Usage:')}
197
+ fe-mcp <command> [options]
198
+
199
+ ${c('bright', 'Commands:')}
200
+ ${c('cyan', 'list')} List all available MCP servers
201
+ ${c('cyan', 'code')} Interactive config generator (creates file & opens it)
202
+ ${c('cyan', 'info <name>')} Show detailed info about a specific server
203
+ ${c('cyan', 'config <name>')} Output MCP client configuration JSON
204
+ ${c('cyan', 'help')} Show this help message
205
+ ${c('cyan', '-v, --version')} Show version number
206
+
207
+ ${c('bright', 'Examples:')}
208
+ fe-mcp list
209
+ fe-mcp code
210
+ fe-mcp info shift-ai
211
+ fe-mcp config shift-ai
212
+
213
+ ${c('bright', 'Global Installation:')}
214
+ npm install -g @deriv-com/fe-mcp-servers
215
+ fe-mcp list
216
+ `);
217
+ }
218
+
219
+ /**
220
+ * Output MCP config JSON for a server
221
+ */
222
+ function outputConfig(serverName) {
223
+ const servers = getMcpServers();
224
+ const server = servers.find(s => s.name === serverName);
225
+
226
+ if (!server) {
227
+ console.error(c('red', `MCP server "${serverName}" not found.`));
228
+ process.exit(1);
229
+ }
230
+
231
+ if (!server.built) {
232
+ console.error(c('red', `MCP server "${serverName}" is not built yet.`));
233
+ console.error(c('dim', 'Run "npm run build" first.'));
234
+ process.exit(1);
235
+ }
236
+
237
+ const config = {
238
+ [server.name]: {
239
+ command: 'node',
240
+ args: [server.path]
241
+ }
242
+ };
243
+
244
+ console.log(JSON.stringify(config, null, 2));
245
+ }
246
+
247
+ /**
248
+ * Get global npm root path
249
+ */
250
+ function getGlobalNpmRoot() {
251
+ try {
252
+ return execSync('npm root -g', { encoding: 'utf-8' }).trim();
253
+ } catch (error) {
254
+ return null;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Open a file with the default application
260
+ */
261
+ function openFile(filePath) {
262
+ const platform = os.platform();
263
+ let command;
264
+
265
+ if (platform === 'darwin') {
266
+ command = 'open';
267
+ } else if (platform === 'win32') {
268
+ command = 'start';
269
+ } else {
270
+ command = 'xdg-open';
271
+ }
272
+
273
+ try {
274
+ spawn(command, [filePath], { detached: true, stdio: 'ignore' }).unref();
275
+ } catch (error) {
276
+ console.log(c('yellow', `\nāš ļø Could not auto-open file. Please open manually:`));
277
+ console.log(c('cyan', ` ${filePath}\n`));
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Prompt user to select from a list
283
+ */
284
+ function promptSelect(question, options) {
285
+ return new Promise((resolve) => {
286
+ const rl = readline.createInterface({
287
+ input: process.stdin,
288
+ output: process.stdout
289
+ });
290
+
291
+ console.log(c('bright', `\n${question}\n`));
292
+
293
+ options.forEach((opt, i) => {
294
+ console.log(` ${c('cyan', `[${i + 1}]`)} ${opt.name}${opt.description ? c('dim', ` - ${opt.description.substring(0, 50)}...`) : ''}`);
295
+ });
296
+
297
+ console.log();
298
+
299
+ rl.question(c('bright', 'Enter number: '), (answer) => {
300
+ rl.close();
301
+ const index = parseInt(answer, 10) - 1;
302
+ if (index >= 0 && index < options.length) {
303
+ resolve(options[index]);
304
+ } else {
305
+ console.log(c('red', '\nāŒ Invalid selection.\n'));
306
+ process.exit(1);
307
+ }
308
+ });
309
+ });
310
+ }
311
+
312
+ /**
313
+ * Generate MCP config file and open it
314
+ */
315
+ async function generateConfigFile() {
316
+ const servers = getMcpServers().filter(s => s.built);
317
+
318
+ if (servers.length === 0) {
319
+ console.log(c('yellow', '\nāš ļø No built MCP servers found.'));
320
+ console.log(c('dim', ' Run "npm run build" to build the servers first.\n'));
321
+ process.exit(1);
322
+ }
323
+
324
+ console.log(c('bright', '\nšŸ”§ MCP Configuration Generator\n'));
325
+ console.log(c('dim', '─'.repeat(50)));
326
+
327
+ // Let user select a server
328
+ const selected = await promptSelect('Select an MCP server:', servers);
329
+
330
+ // Get the global npm path
331
+ const globalRoot = getGlobalNpmRoot();
332
+ let serverPath;
333
+
334
+ if (globalRoot) {
335
+ // Use global path for installed package
336
+ serverPath = `${globalRoot}/@deriv-com/fe-mcp-servers/dist/${selected.name}/mcp-server.js`;
337
+ } else {
338
+ // Fallback to local path
339
+ serverPath = selected.path;
340
+ }
341
+
342
+ // Generate config
343
+ const config = {
344
+ mcpServers: {
345
+ [selected.name]: {
346
+ command: 'node',
347
+ args: [serverPath]
348
+ }
349
+ }
350
+ };
351
+
352
+ const configJson = JSON.stringify(config, null, 2);
353
+
354
+ // Create temp file
355
+ const tempDir = os.tmpdir();
356
+ const fileName = 'fe-mcp-config.txt';
357
+ const filePath = path.join(tempDir, fileName);
358
+
359
+ // Write config to file
360
+ fs.writeFileSync(filePath, configJson, 'utf-8');
361
+
362
+ console.log(c('green', `\nāœ… Configuration generated for ${c('cyan', selected.name)}`));
363
+ console.log(c('dim', '─'.repeat(50)));
364
+ console.log(c('bright', '\nConfiguration:\n'));
365
+ console.log(c('cyan', configJson));
366
+ console.log(c('dim', '\n─'.repeat(50)));
367
+ console.log(c('dim', `\nFile saved to: ${filePath}`));
368
+ console.log(c('bright', '\nšŸ“‚ Opening file for copy-paste...\n'));
369
+
370
+ // Open the file
371
+ openFile(filePath);
372
+ }
373
+
374
+ // Parse command line arguments
375
+ const args = process.argv.slice(2);
376
+ const command = args[0];
377
+
378
+ switch (command) {
379
+ case 'list':
380
+ case 'ls':
381
+ case undefined:
382
+ listServers();
383
+ break;
384
+
385
+ case 'code':
386
+ generateConfigFile();
387
+ break;
388
+
389
+ case 'info':
390
+ case 'show':
391
+ if (!args[1]) {
392
+ console.log(c('red', '\nāŒ Please specify a server name.'));
393
+ console.log(c('dim', ' Example: fe-mcp info shift-ai\n'));
394
+ process.exit(1);
395
+ }
396
+ showInfo(args[1]);
397
+ break;
398
+
399
+ case 'config':
400
+ if (!args[1]) {
401
+ console.log(c('red', '\nāŒ Please specify a server name.'));
402
+ console.log(c('dim', ' Example: fe-mcp config shift-ai\n'));
403
+ process.exit(1);
404
+ }
405
+ outputConfig(args[1]);
406
+ break;
407
+
408
+ case 'help':
409
+ case '--help':
410
+ case '-h':
411
+ showHelp();
412
+ break;
413
+
414
+ case 'version':
415
+ case '--version':
416
+ case '-v':
417
+ console.log(`${packageJson.name} v${packageJson.version}`);
418
+ break;
419
+
420
+ default:
421
+ console.log(c('red', `\nāŒ Unknown command: ${command}`));
422
+ showHelp();
423
+ process.exit(1);
424
+ }