@maplezzk/mcps 1.0.18 → 1.0.24
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/dist/commands/daemon.js +70 -26
- package/dist/commands/server.js +52 -9
- package/dist/core/pool.js +25 -6
- package/dist/index.js +0 -0
- package/package.json +1 -1
package/dist/commands/daemon.js
CHANGED
|
@@ -24,8 +24,8 @@ function isPortInUse(port) {
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
// Action functions for daemon commands
|
|
27
|
-
const startAction = async (options
|
|
28
|
-
const port = parseInt(options.port || DAEMON_PORT);
|
|
27
|
+
const startAction = async (options) => {
|
|
28
|
+
const port = parseInt(options.port || process.env.MCPS_PORT || DAEMON_PORT);
|
|
29
29
|
// Check if port is in use (more reliable than HTTP check)
|
|
30
30
|
const portInUse = await isPortInUse(port);
|
|
31
31
|
if (portInUse) {
|
|
@@ -51,7 +51,7 @@ const startAction = async (options, parentCmd) => {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
// Otherwise, spawn a detached process
|
|
54
|
-
console.log(chalk.
|
|
54
|
+
console.log(chalk.cyan('Starting daemon in background...'));
|
|
55
55
|
let childFailed = false;
|
|
56
56
|
const subprocess = spawn(process.execPath, [process.argv[1], 'daemon', 'start'], {
|
|
57
57
|
detached: true,
|
|
@@ -59,23 +59,27 @@ const startAction = async (options, parentCmd) => {
|
|
|
59
59
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
60
|
env: {
|
|
61
61
|
...process.env,
|
|
62
|
-
MCPS_DAEMON_DETACHED: 'true'
|
|
62
|
+
MCPS_DAEMON_DETACHED: 'true',
|
|
63
|
+
MCPS_VERBOSE: options.verbose ? 'true' : 'false'
|
|
63
64
|
}
|
|
64
65
|
});
|
|
65
66
|
// Stream logs to current console while waiting for ready
|
|
66
67
|
if (subprocess.stdout) {
|
|
67
68
|
subprocess.stdout.on('data', (data) => {
|
|
68
|
-
process.stdout.write(
|
|
69
|
+
process.stdout.write(`${data}`);
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
if (subprocess.stderr) {
|
|
72
73
|
subprocess.stderr.on('data', (data) => {
|
|
73
74
|
const msg = data.toString();
|
|
74
|
-
process.stderr.write(chalk.red(`[Daemon] ${msg}`));
|
|
75
75
|
// Detect port conflict in child process
|
|
76
76
|
if (msg.includes('Port') && msg.includes('is already in use')) {
|
|
77
77
|
childFailed = true;
|
|
78
78
|
}
|
|
79
|
+
// Only show error output if it contains critical errors
|
|
80
|
+
if (msg.includes('Error') || msg.includes('EADDRINUSE')) {
|
|
81
|
+
process.stderr.write(chalk.red(`[Daemon] ${msg}`));
|
|
82
|
+
}
|
|
79
83
|
});
|
|
80
84
|
}
|
|
81
85
|
subprocess.unref();
|
|
@@ -83,7 +87,7 @@ const startAction = async (options, parentCmd) => {
|
|
|
83
87
|
// We can poll status for a second
|
|
84
88
|
const start = Date.now();
|
|
85
89
|
// Increased timeout to allow for connection initialization
|
|
86
|
-
while (Date.now() - start <
|
|
90
|
+
while (Date.now() - start < 30000) {
|
|
87
91
|
// If child reported port conflict, check if daemon is actually running
|
|
88
92
|
if (childFailed) {
|
|
89
93
|
const stillRunning = await isPortInUse(port);
|
|
@@ -110,9 +114,9 @@ const startAction = async (options, parentCmd) => {
|
|
|
110
114
|
console.log(chalk.yellow('Daemon started (async check timeout, but likely running).'));
|
|
111
115
|
process.exit(0);
|
|
112
116
|
};
|
|
113
|
-
const stopAction = async (
|
|
117
|
+
const stopAction = async (options) => {
|
|
114
118
|
try {
|
|
115
|
-
const port = parseInt(
|
|
119
|
+
const port = parseInt(options?.port || process.env.MCPS_PORT || DAEMON_PORT);
|
|
116
120
|
await fetch(`http://localhost:${port}/stop`, { method: 'POST' });
|
|
117
121
|
console.log(chalk.green('Daemon stopped successfully.'));
|
|
118
122
|
}
|
|
@@ -120,31 +124,70 @@ const stopAction = async (parentCmd) => {
|
|
|
120
124
|
console.error(chalk.red('Failed to stop daemon. Is it running?'));
|
|
121
125
|
}
|
|
122
126
|
};
|
|
123
|
-
const statusAction = async (
|
|
127
|
+
const statusAction = async (options) => {
|
|
124
128
|
try {
|
|
125
|
-
const port = parseInt(
|
|
129
|
+
const port = parseInt(options?.port || process.env.MCPS_PORT || DAEMON_PORT);
|
|
126
130
|
const res = await fetch(`http://localhost:${port}/status`);
|
|
127
131
|
const data = await res.json();
|
|
132
|
+
console.log('');
|
|
128
133
|
console.log(chalk.green(`Daemon is running (v${data.version})`));
|
|
129
134
|
if (data.connections && data.connections.length > 0) {
|
|
135
|
+
// Helper function to calculate display width (Chinese chars count as 2)
|
|
136
|
+
const getDisplayWidth = (str) => {
|
|
137
|
+
let width = 0;
|
|
138
|
+
for (const char of str) {
|
|
139
|
+
if (char.charCodeAt(0) > 127) {
|
|
140
|
+
width += 2;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
width += 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return width;
|
|
147
|
+
};
|
|
148
|
+
// Helper function to pad string considering Chinese characters
|
|
149
|
+
const padEndWidth = (str, targetWidth) => {
|
|
150
|
+
const displayWidth = getDisplayWidth(str);
|
|
151
|
+
const padding = Math.max(0, targetWidth - displayWidth);
|
|
152
|
+
return str + ' '.repeat(padding);
|
|
153
|
+
};
|
|
154
|
+
// Build table rows
|
|
155
|
+
const rows = data.connections.map((conn) => {
|
|
156
|
+
const statusText = conn.status === 'error' ? 'Error' : 'Connected';
|
|
157
|
+
const statusColor = conn.status === 'error' ? chalk.red : chalk.green;
|
|
158
|
+
const toolsCount = conn.toolsCount !== null ? conn.toolsCount : '-';
|
|
159
|
+
return {
|
|
160
|
+
name: conn.name,
|
|
161
|
+
status: statusColor(statusText),
|
|
162
|
+
tools: toolsCount
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
// Calculate column widths
|
|
166
|
+
const nameWidth = Math.max(4, ...rows.map((r) => getDisplayWidth(r.name)));
|
|
167
|
+
const statusWidth = 10;
|
|
168
|
+
const toolsWidth = 6;
|
|
169
|
+
// Print table header
|
|
130
170
|
console.log(chalk.bold('\nActive Connections:'));
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
171
|
+
console.log(chalk.bold(`${'NAME'.padEnd(nameWidth)} ${'STATUS'.padEnd(statusWidth)} ${'TOOLS'}`));
|
|
172
|
+
console.log(chalk.cyan('─'.repeat(nameWidth) + ' ' + '─'.repeat(statusWidth) + ' ' + '─'.repeat(toolsWidth)));
|
|
173
|
+
// Print table rows
|
|
174
|
+
rows.forEach((row) => {
|
|
175
|
+
console.log(`${padEndWidth(row.name, nameWidth)} ${String(row.status).padEnd(statusWidth)} ${String(row.tools)}`);
|
|
135
176
|
});
|
|
177
|
+
console.log(chalk.cyan(`Total: ${data.connections.length} connection(s)`));
|
|
136
178
|
}
|
|
137
179
|
else {
|
|
138
|
-
console.log(chalk.
|
|
180
|
+
console.log(chalk.yellow('\nNo active connections.'));
|
|
139
181
|
}
|
|
182
|
+
console.log('');
|
|
140
183
|
}
|
|
141
184
|
catch (e) {
|
|
142
185
|
console.error(chalk.red('Daemon is not running.'));
|
|
143
186
|
}
|
|
144
187
|
};
|
|
145
|
-
const restartAction = async (serverName,
|
|
188
|
+
const restartAction = async (serverName, options) => {
|
|
146
189
|
try {
|
|
147
|
-
const port = parseInt(
|
|
190
|
+
const port = parseInt(options?.port || process.env.MCPS_PORT || DAEMON_PORT);
|
|
148
191
|
const res = await fetch(`http://localhost:${port}/restart`, {
|
|
149
192
|
method: 'POST',
|
|
150
193
|
body: JSON.stringify({ server: serverName })
|
|
@@ -161,19 +204,20 @@ export const registerDaemonCommand = (program) => {
|
|
|
161
204
|
program.command('start')
|
|
162
205
|
.description('Start the daemon')
|
|
163
206
|
.option('-p, --port <number>', 'Daemon port', String(DAEMON_PORT))
|
|
164
|
-
.
|
|
207
|
+
.option('-v, --verbose', 'Show detailed logs')
|
|
208
|
+
.action((options) => startAction(options));
|
|
165
209
|
program.command('stop')
|
|
166
210
|
.description('Stop the daemon')
|
|
167
211
|
.option('-p, --port <number>', 'Daemon port', String(DAEMON_PORT))
|
|
168
|
-
.action(() => stopAction(
|
|
212
|
+
.action((options) => stopAction(options));
|
|
169
213
|
program.command('status')
|
|
170
214
|
.description('Check daemon status')
|
|
171
215
|
.option('-p, --port <number>', 'Daemon port', String(DAEMON_PORT))
|
|
172
|
-
.action(() => statusAction(
|
|
216
|
+
.action((options) => statusAction(options));
|
|
173
217
|
program.command('restart [server]')
|
|
174
218
|
.description('Restart the daemon or a specific server connection')
|
|
175
219
|
.option('-p, --port <number>', 'Daemon port', String(DAEMON_PORT))
|
|
176
|
-
.action((serverName) => restartAction(serverName,
|
|
220
|
+
.action((serverName, options) => restartAction(serverName, options));
|
|
177
221
|
// ===== Legacy daemon subcommands (for backward compatibility) =====
|
|
178
222
|
const daemonCmd = program.command('daemon')
|
|
179
223
|
.description('Manage the mcps daemon (legacy, use top-level commands)')
|
|
@@ -181,16 +225,16 @@ export const registerDaemonCommand = (program) => {
|
|
|
181
225
|
.option('-p, --port <number>', 'Daemon port', String(DAEMON_PORT));
|
|
182
226
|
daemonCmd.command('start', { isDefault: true, hidden: true })
|
|
183
227
|
.description('Start the daemon (default)')
|
|
184
|
-
.action((options) => startAction(options
|
|
228
|
+
.action((options) => startAction(options));
|
|
185
229
|
daemonCmd.command('stop')
|
|
186
230
|
.description('Stop the running daemon')
|
|
187
|
-
.action(() => stopAction(
|
|
231
|
+
.action((options) => stopAction(options));
|
|
188
232
|
daemonCmd.command('status')
|
|
189
233
|
.description('Check daemon status')
|
|
190
|
-
.action(() => statusAction(
|
|
234
|
+
.action((options) => statusAction(options));
|
|
191
235
|
daemonCmd.command('restart [server]')
|
|
192
236
|
.description('Restart the daemon or a specific server connection')
|
|
193
|
-
.action((serverName) => restartAction(serverName,
|
|
237
|
+
.action((serverName, options) => restartAction(serverName, options));
|
|
194
238
|
};
|
|
195
239
|
const startDaemon = (port) => {
|
|
196
240
|
const server = http.createServer(async (req, res) => {
|
package/dist/commands/server.js
CHANGED
|
@@ -8,19 +8,62 @@ export const registerServerCommands = (program) => {
|
|
|
8
8
|
console.log(chalk.yellow('No servers configured.'));
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
// Helper function to calculate display width (Chinese chars count as 2)
|
|
12
|
+
const getDisplayWidth = (str) => {
|
|
13
|
+
let width = 0;
|
|
14
|
+
for (const char of str) {
|
|
15
|
+
if (char.charCodeAt(0) > 127) {
|
|
16
|
+
width += 2;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
width += 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return width;
|
|
23
|
+
};
|
|
24
|
+
// Helper function to pad string considering Chinese characters
|
|
25
|
+
const padEndWidth = (str, targetWidth) => {
|
|
26
|
+
const displayWidth = getDisplayWidth(str);
|
|
27
|
+
const padding = Math.max(0, targetWidth - displayWidth);
|
|
28
|
+
return str + ' '.repeat(padding);
|
|
29
|
+
};
|
|
30
|
+
// Build table rows
|
|
31
|
+
const rows = servers.map(server => {
|
|
32
|
+
const disabled = server.disabled === true;
|
|
33
|
+
const typeColor = server.type === 'stdio' ? chalk.cyan : chalk.yellow;
|
|
34
|
+
const enabledMark = disabled ? chalk.red('✗') : chalk.green('✓');
|
|
35
|
+
// Build command/URL string
|
|
36
|
+
let command = '';
|
|
37
|
+
if (server.type === 'stdio') {
|
|
38
|
+
command = `${server.command} ${server.args.join(' ')}`;
|
|
18
39
|
}
|
|
19
40
|
else {
|
|
20
|
-
|
|
41
|
+
command = server.url || '';
|
|
21
42
|
}
|
|
22
|
-
|
|
43
|
+
return {
|
|
44
|
+
name: server.name,
|
|
45
|
+
type: typeColor(server.type),
|
|
46
|
+
enabled: enabledMark,
|
|
47
|
+
command: command,
|
|
48
|
+
disabled
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
// Calculate column widths
|
|
52
|
+
const nameWidth = Math.max(4, ...rows.map(r => getDisplayWidth(r.name)));
|
|
53
|
+
const typeWidth = 6;
|
|
54
|
+
const enabledWidth = 7;
|
|
55
|
+
const commandWidth = Math.max(7, ...rows.map(r => getDisplayWidth(r.command)));
|
|
56
|
+
// Print table header
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log(chalk.bold(`${'NAME'.padEnd(nameWidth)} ${'TYPE'.padEnd(typeWidth)} ${'ENABLED'.padEnd(enabledWidth)} ${'COMMAND/URL'}`));
|
|
59
|
+
console.log(chalk.gray('─'.repeat(nameWidth) + ' ' + '─'.repeat(typeWidth) + ' ' + '─'.repeat(enabledWidth) + ' ' + '─'.repeat(commandWidth)));
|
|
60
|
+
// Print table rows
|
|
61
|
+
rows.forEach(row => {
|
|
62
|
+
console.log(`${padEndWidth(row.name, nameWidth)} ${String(row.type).padEnd(typeWidth)} ${String(row.enabled).padEnd(enabledWidth)} ${row.command}`);
|
|
23
63
|
});
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(chalk.cyan(`Total: ${servers.length} server(s)`));
|
|
66
|
+
console.log('');
|
|
24
67
|
};
|
|
25
68
|
const addServerAction = (name, options) => {
|
|
26
69
|
try {
|
package/dist/core/pool.js
CHANGED
|
@@ -56,10 +56,11 @@ export class ConnectionPool {
|
|
|
56
56
|
const servers = configManager.listServers();
|
|
57
57
|
this.initializing = true;
|
|
58
58
|
this.initialized = false;
|
|
59
|
+
const verbose = process.env.MCPS_VERBOSE === 'true';
|
|
59
60
|
// 过滤掉 disabled 的服务器
|
|
60
61
|
const enabledServers = servers.filter(server => {
|
|
61
62
|
const disabled = server.disabled === true;
|
|
62
|
-
if (disabled) {
|
|
63
|
+
if (verbose && disabled) {
|
|
63
64
|
console.log(`[Daemon] Skipping disabled server: ${server.name}`);
|
|
64
65
|
}
|
|
65
66
|
return !disabled;
|
|
@@ -70,20 +71,38 @@ export class ConnectionPool {
|
|
|
70
71
|
this.initialized = true;
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
|
-
console.log(`[Daemon]
|
|
74
|
+
console.log(`[Daemon] Connecting to ${enabledServers.length} server(s)...`);
|
|
75
|
+
const results = [];
|
|
74
76
|
for (const server of enabledServers) {
|
|
77
|
+
if (verbose) {
|
|
78
|
+
process.stdout.write(`[Daemon] - ${server.name}... `);
|
|
79
|
+
}
|
|
75
80
|
try {
|
|
76
|
-
console.log(`[Daemon] Connecting to server: ${server.name}...`);
|
|
77
81
|
await this.getClient(server.name, { timeoutMs: 8000 });
|
|
78
|
-
|
|
82
|
+
results.push({ name: server.name, success: true });
|
|
83
|
+
if (verbose) {
|
|
84
|
+
console.log('✓');
|
|
85
|
+
}
|
|
79
86
|
}
|
|
80
87
|
catch (error) {
|
|
81
|
-
|
|
88
|
+
results.push({ name: server.name, success: false, error: error.message });
|
|
89
|
+
if (verbose) {
|
|
90
|
+
console.log('✗');
|
|
91
|
+
console.error(`[Daemon] Error: ${error.message}`);
|
|
92
|
+
}
|
|
82
93
|
}
|
|
83
94
|
}
|
|
95
|
+
// Print summary
|
|
96
|
+
const successCount = results.filter(r => r.success).length;
|
|
97
|
+
const failed = results.filter(r => !r.success);
|
|
98
|
+
console.log(`[Daemon] Connected: ${successCount}/${enabledServers.length}`);
|
|
99
|
+
if (!verbose && failed.length > 0) {
|
|
100
|
+
failed.forEach(f => {
|
|
101
|
+
console.error(`[Daemon] ✗ ${f.name}: ${f.error}`);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
84
104
|
this.initializing = false;
|
|
85
105
|
this.initialized = true;
|
|
86
|
-
console.log('[Daemon] Initialization complete.');
|
|
87
106
|
}
|
|
88
107
|
getInitStatus() {
|
|
89
108
|
return { initializing: this.initializing, initialized: this.initialized };
|
package/dist/index.js
CHANGED
|
File without changes
|