file-share-mcp 1.0.7 → 1.0.8

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/gui.js CHANGED
@@ -5,13 +5,13 @@ import path from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import fs from 'fs';
7
7
  import os from 'os';
8
- import axios from 'axios';
9
- import { setupLogging } from './utils/logger.js';
8
+ import { setupLogging, createLogger, ProxyClient } from '@imenam/mcp-gui-interface';
10
9
  import { getConfig, saveConfig } from './utils/config-manager.js';
11
10
  import { getSharedFiles, validateSafePath, deleteFile, createDirectory, moveFile, deleteDirectory } from './utils/file-manager.js';
12
11
  const __filename = fileURLToPath(import.meta.url);
13
12
  const __dirname = path.dirname(__filename);
14
- setupLogging('GUI');
13
+ setupLogging({ processLabel: 'GUI', logDir: path.join(os.homedir(), '.file-share-mcp', 'logs') });
14
+ const logger = createLogger('GUI');
15
15
  const app = express();
16
16
  app.use(cors());
17
17
  app.use(express.json());
@@ -34,9 +34,9 @@ app.get('/api/download', async (req, res) => {
34
34
  const relativePath = req.query.path || '';
35
35
  try {
36
36
  const safePath = validateSafePath(relativePath);
37
- console.log(`[download] path="${relativePath}" → safePath="${safePath}"`);
37
+ logger.info(`[download] path="${relativePath}" → safePath="${safePath}"`);
38
38
  if (!fs.existsSync(safePath) || !fs.statSync(safePath).isFile()) {
39
- console.log(`[download] Not found or not a file: "${safePath}"`);
39
+ logger.info(`[download] Not found or not a file: "${safePath}"`);
40
40
  res.status(404).send(`File not found: ${relativePath}`);
41
41
  return;
42
42
  }
@@ -47,18 +47,18 @@ app.get('/api/download', async (req, res) => {
47
47
  res.setHeader('Content-Length', size);
48
48
  const stream = fs.createReadStream(safePath);
49
49
  stream.on('error', (err) => {
50
- console.error(`[download] Stream error for "${safePath}": ${err.message}`);
50
+ logger.error(`[download] Stream error for "${safePath}": ${err.message}`);
51
51
  if (!res.headersSent) {
52
52
  res.status(500).send(`Stream error: ${err.message}`);
53
53
  }
54
54
  });
55
55
  stream.on('end', () => {
56
- console.log(`[download] Completed: "${safePath}"`);
56
+ logger.info(`[download] Completed: "${safePath}"`);
57
57
  });
58
58
  stream.pipe(res);
59
59
  }
60
60
  catch (error) {
61
- console.error(`[download] Validation error for "${relativePath}": ${error.message}`);
61
+ logger.error(`[download] Validation error for "${relativePath}": ${error.message}`);
62
62
  res.status(400).send(`Invalid path: ${error.message}`);
63
63
  }
64
64
  });
@@ -203,84 +203,49 @@ app.get(/^(?!\/api\/|\/proxy\/)/, (req, res) => {
203
203
  });
204
204
  // Proxy registration & server startup
205
205
  const config = getConfig();
206
- async function registerWithProxy() {
207
- if (!config.PROXY_URL || !config.PROXY_APP_PATH)
208
- return null;
209
- try {
210
- await axios.get(`${config.PROXY_URL}/proxy/health`, { timeout: 2000 });
211
- const response = await axios.post(`${config.PROXY_URL}/proxy/register`, {
212
- path: config.PROXY_APP_PATH,
213
- name: config.PROXY_APP_NAME
214
- }, { timeout: 2000 });
215
- const assignedPort = response.data.port;
216
- console.log(`Successfully registered with proxy at ${config.PROXY_URL}${config.PROXY_APP_PATH} (port ${assignedPort})`);
217
- return assignedPort;
218
- }
219
- catch (error) {
220
- console.error(`Failed to register with proxy: ${error.message}`);
221
- return null;
222
- }
223
- }
224
- async function unregisterFromProxy() {
225
- if (!config.PROXY_URL || !config.PROXY_APP_PATH)
226
- return;
227
- try {
228
- await axios.delete(`${config.PROXY_URL}/proxy/unregister`, {
229
- data: { path: config.PROXY_APP_PATH },
230
- timeout: 2000
231
- });
232
- console.log(`Unregistered from proxy`);
233
- }
234
- catch (error) {
235
- // Ignore error on shutdown
236
- }
237
- }
206
+ const proxyClient = config.PROXY_URL ? new ProxyClient(config.PROXY_URL) : null;
238
207
  let server;
239
208
  async function startServer() {
240
- let port;
241
- if (config.PROXY_URL) {
242
- const assignedPort = await registerWithProxy();
243
- if (assignedPort) {
244
- port = assignedPort;
209
+ let port = config.GUI_PORT;
210
+ if (proxyClient && config.PROXY_APP_PATH) {
211
+ const result = await proxyClient.register({ path: config.PROXY_APP_PATH, name: config.PROXY_APP_NAME }, config.GUI_PORT);
212
+ port = result.port;
213
+ if (!result.success) {
214
+ logger.warn(`Could not register with proxy. Falling back to standalone mode on port ${port}.`);
245
215
  }
246
- else {
247
- console.error(`Could not register with proxy. Falling back to standalone mode on port ${config.GUI_PORT}.`);
248
- port = config.GUI_PORT;
249
- }
250
- }
251
- else {
252
- port = config.GUI_PORT;
253
216
  }
254
217
  server = app.listen(port, () => {
255
- console.log(`GUI API server listening on port ${port}`);
218
+ logger.info(`GUI API server listening on port ${port}`);
256
219
  });
257
220
  server.on('error', (err) => {
258
221
  if (err.code === 'EADDRINUSE') {
259
- console.error(`PORT CONFLICT: Port ${port} is already in use.`);
222
+ logger.error(`PORT CONFLICT: Port ${port} is already in use. Another GUI instance is running.`);
223
+ process.exit(0);
260
224
  }
261
225
  });
262
226
  }
263
227
  startServer();
264
228
  // IPC and signals
265
229
  process.on('message', async (msg) => {
266
- if (msg === 'STOP') {
267
- console.log('GUI received STOP signal');
268
- await unregisterFromProxy();
230
+ const type = typeof msg === 'string' ? msg : msg?.type;
231
+ if (type === 'STOP') {
232
+ logger.info('GUI received STOP signal');
233
+ await proxyClient?.unregister();
269
234
  server?.close(() => process.exit(0));
270
235
  }
271
236
  });
272
237
  process.on('error', (err) => {
273
238
  if (err.code === 'ERR_IPC_CHANNEL_CLOSED') {
274
- console.error('[IPC] Channel closed (expected on disconnect).');
239
+ logger.error('[IPC] Channel closed (expected on disconnect).');
275
240
  return;
276
241
  }
277
- console.error(`[Process] Error: ${err.message}`);
242
+ logger.error(`[Process] Error: ${err.message}`);
278
243
  });
279
244
  process.on('disconnect', () => {
280
- console.error('Disconnected from parent, exiting.');
245
+ logger.error('Disconnected from parent, exiting.');
281
246
  process.exit(0);
282
247
  });
283
248
  process.on('SIGTERM', async () => {
284
- await unregisterFromProxy();
249
+ await proxyClient?.unregister();
285
250
  process.exit(0);
286
251
  });
@@ -3,17 +3,17 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { z } from 'zod';
6
- import { fork } from 'child_process';
7
6
  import path from 'path';
8
7
  import { fileURLToPath } from 'url';
9
8
  import fs from 'fs';
10
- import { setupLogging } from './utils/logger.js';
9
+ import { GuiLauncher, setupLogging, createLogger } from '@imenam/mcp-gui-interface';
11
10
  import { getConfig } from './utils/config-manager.js';
12
11
  import { getSharedFiles, validateSafePath, writeSharedFile, copyToSharedFolder, createDirectory, deleteDirectory, moveFile, } from './utils/file-manager.js';
13
12
  import { VERSION } from './version.js';
14
13
  const __filename = fileURLToPath(import.meta.url);
15
14
  const __dirname = path.dirname(__filename);
16
- setupLogging('MASTER');
15
+ setupLogging({ processLabel: 'MASTER' });
16
+ const logger = createLogger('MASTER');
17
17
  const server = new Server({
18
18
  name: 'file-share-mcp',
19
19
  version: VERSION,
@@ -22,68 +22,18 @@ const server = new Server({
22
22
  tools: {},
23
23
  },
24
24
  });
25
- let guiProcess = null;
26
- /**
27
- * Envoi IPC robuste — vérifie que le canal est connecté avant d'envoyer
28
- */
29
- function safeSend(proc, message) {
30
- if (proc && typeof proc.send === 'function' && proc.connected) {
31
- try {
32
- return proc.send(message);
25
+ const gui = new GuiLauncher({
26
+ guiPath: path.join(__dirname, 'gui.js'),
27
+ onMessage: (msg) => {
28
+ if (msg.type === 'RESTART') {
29
+ logger.info('GUI requested restart...');
30
+ gui.start();
33
31
  }
34
- catch (err) {
35
- console.error(`[IPC] Failed to send message: ${err.message}`);
36
- return false;
32
+ else if (msg.type === 'STOP') {
33
+ logger.info('GUI requested stop...');
34
+ gui.cleanup().then(() => process.exit(0));
37
35
  }
38
- }
39
- return false;
40
- }
41
- function startGui() {
42
- if (guiProcess) {
43
- guiProcess.kill();
44
- }
45
- const guiPath = path.join(__dirname, 'gui.js');
46
- guiProcess = fork(guiPath, [], {
47
- stdio: ['ignore', 'pipe', 'inherit', 'ipc'],
48
- env: { ...process.env }
49
- });
50
- // Redirect GUI stdout to stderr to protect JSON-RPC
51
- guiProcess.stdout?.on('data', (data) => {
52
- process.stderr.write(data);
53
- });
54
- guiProcess.on('message', (message) => {
55
- if (message === 'RESTART') {
56
- console.log('GUI requested restart...');
57
- startGui();
58
- }
59
- else if (message === 'STOP') {
60
- console.log('GUI requested stop...');
61
- cleanup().then(() => process.exit(0));
62
- }
63
- });
64
- guiProcess.on('exit', (code) => {
65
- if (code !== 0 && code !== null) {
66
- console.error(`GUI process exited with code ${code}. Restarting...`);
67
- setTimeout(startGui, 2000);
68
- }
69
- });
70
- }
71
- async function cleanup() {
72
- console.log('Cleaning up...');
73
- if (guiProcess) {
74
- safeSend(guiProcess, 'STOP');
75
- // Give it a moment to stop gracefully
76
- await new Promise(resolve => setTimeout(resolve, 1000));
77
- guiProcess.kill();
78
- }
79
- }
80
- process.on('SIGINT', async () => {
81
- await cleanup();
82
- process.exit(0);
83
- });
84
- process.on('SIGTERM', async () => {
85
- await cleanup();
86
- process.exit(0);
36
+ },
87
37
  });
88
38
  // Tool Definitions
89
39
  server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -349,8 +299,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
349
299
  async function main() {
350
300
  const transport = new StdioServerTransport();
351
301
  await server.connect(transport);
352
- console.log('File Share MCP Server started');
353
- startGui();
302
+ logger.info('File Share MCP Server started');
303
+ gui.start();
354
304
  }
355
305
  main().catch((error) => {
356
306
  console.error('Fatal error in main():', error);
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '1.0.7';
1
+ export const VERSION = '1.0.8';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "file-share-mcp",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "MCP server for bidirectional file sharing between AI agents and users, with a built-in web GUI",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -37,6 +37,7 @@
37
37
  "test": "echo \"Error: no test specified\" && exit 1"
38
38
  },
39
39
  "dependencies": {
40
+ "@imenam/mcp-gui-interface": "file:../mcp-gui-interface",
40
41
  "@modelcontextprotocol/sdk": "^1.26.0",
41
42
  "axios": "^1.13.5",
42
43
  "cors": "^2.8.6",