@deriv-com/fe-mcp-servers 0.0.9 → 0.0.11
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 +34 -5
- package/bin/fe-mcp.js +424 -0
- package/dist/maestro-ai/README.md +341 -0
- package/dist/maestro-ai/mcp-server.js +19616 -0
- package/dist/shift-ai/mcp-server.js +17216 -3998
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -31,14 +31,43 @@ mcps/
|
|
|
31
31
|
npm install -g @deriv-com/fe-mcp-servers
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
###
|
|
35
|
-
|
|
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:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
63
|
+
fe-mcp code
|
|
39
64
|
```
|
|
40
65
|
|
|
41
|
-
|
|
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
|
|
42
71
|
|
|
43
72
|
### MCP Configuration Template
|
|
44
73
|
```json
|
|
@@ -46,7 +75,7 @@ Replace `SERVER_NAME` with the specific server you want (e.g., `shift-ai`).
|
|
|
46
75
|
"mcpServers": {
|
|
47
76
|
"server-name": {
|
|
48
77
|
"command": "node",
|
|
49
|
-
"args": ["<
|
|
78
|
+
"args": ["<PATH_FROM_FE-MCP>"]
|
|
50
79
|
}
|
|
51
80
|
}
|
|
52
81
|
}
|
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
|
+
}
|