imcp 0.0.3 → 0.0.4

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
@@ -1,15 +1,15 @@
1
1
  # IMCP
2
2
 
3
- IMCP (Install Model Context Protocol) is a unified MCP management project that provides the ability to list, install, and manage MCP servers based on unified feeds.
3
+ IMCP is a single-line pull ann push experience that makes onboarding MCP servers easy.
4
4
 
5
5
  ## Overview
6
6
 
7
- IMCP is a command-line tool that simplifies the management of Model Context Protocol (MCP) servers. It allows you to:
7
+ IMCP allows you to:
8
8
 
9
- - Discover available MCP servers from configurable feeds
10
- - Install servers with specific configurations
9
+ - Discover available MCP servers
11
10
  - Manage server installations
12
- - Run a local web interface to manage your MCP servers
11
+ - Run a local web interface with simple click experience
12
+ - (in progress) Distribute your own MCP servers to others
13
13
 
14
14
  ## Installation
15
15
 
@@ -47,6 +47,13 @@ export class ClientInstaller {
47
47
  // Windows-specific command check
48
48
  await execAsync(`where ${command}`);
49
49
  }
50
+ else if (process.platform === 'darwin' && (command === 'code' || command === 'code-insiders')) {
51
+ // macOS-specific VS Code check
52
+ const vscodePath = command === 'code' ?
53
+ '/Applications/Visual Studio Code.app' :
54
+ '/Applications/Visual Studio Code - Insiders.app';
55
+ await execAsync(`test -d "${vscodePath}"`);
56
+ }
50
57
  else {
51
58
  // Unix-like systems
52
59
  await execAsync(`which ${command}`);
@@ -54,6 +61,20 @@ export class ClientInstaller {
54
61
  return true;
55
62
  }
56
63
  catch (error) {
64
+ if (process.platform === 'darwin' && (command === 'code' || command === 'code-insiders')) {
65
+ // Try checking in ~/Applications as well for macOS
66
+ try {
67
+ const homedir = process.env.HOME;
68
+ const vscodePath = command === 'code' ?
69
+ `${homedir}/Applications/Visual Studio Code.app` :
70
+ `${homedir}/Applications/Visual Studio Code - Insiders.app`;
71
+ await execAsync(`test -d "${vscodePath}"`);
72
+ return true;
73
+ }
74
+ catch (error) {
75
+ return false;
76
+ }
77
+ }
57
78
  return false;
58
79
  }
59
80
  }
@@ -389,6 +410,10 @@ export class ClientInstaller {
389
410
  const npmPath = await this.getNpmPath();
390
411
  serverConfig.env['APPDATA'] = npmPath;
391
412
  }
413
+ // Convert backslashes to forward slashes in args paths
414
+ if (serverConfig.args) {
415
+ serverConfig.args = serverConfig.args.map((arg) => typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg);
416
+ }
392
417
  // Add or update the server configuration
393
418
  settings.mcpServers[serverName] = {
394
419
  command: serverConfig.command,
@@ -414,6 +439,10 @@ export class ClientInstaller {
414
439
  if (!settings.mcp.servers) {
415
440
  settings.mcp.servers = {};
416
441
  }
442
+ // Convert backslashes to forward slashes in args paths
443
+ if (installConfig.args) {
444
+ installConfig.args = installConfig.args.map((arg) => typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg);
445
+ }
417
446
  // Add or update the server configuration
418
447
  settings.mcp.servers[serverName] = {
419
448
  command: installConfig.command,
@@ -14,12 +14,6 @@ export declare function extractZipFile(zipPath: string, options: {
14
14
  * @returns The resolved path
15
15
  */
16
16
  export declare function resolveNpmModulePath(pathString: string): string;
17
- /**
18
- * Reads a JSON file and parses its content
19
- * @param filePath Path to the JSON file
20
- * @param createIfNotExist Whether to create the file if it doesn't exist
21
- * @returns The parsed JSON content
22
- */
23
17
  export declare function readJsonFile(filePath: string, createIfNotExist?: boolean): Promise<any>;
24
18
  /**
25
19
  * Writes content to a JSON file
@@ -77,6 +77,7 @@ export function resolveNpmModulePath(pathString) {
77
77
  * @param createIfNotExist Whether to create the file if it doesn't exist
78
78
  * @returns The parsed JSON content
79
79
  */
80
+ import stripJsonComments from 'strip-json-comments';
80
81
  export async function readJsonFile(filePath, createIfNotExist = false) {
81
82
  try {
82
83
  // Ensure directory exists
@@ -84,8 +85,8 @@ export async function readJsonFile(filePath, createIfNotExist = false) {
84
85
  await fs.mkdir(path.dirname(filePath), { recursive: true });
85
86
  }
86
87
  const content = await fs.readFile(filePath, 'utf8');
87
- // Remove trailing commas from JSON content
88
- const sanitizedContent = content.replace(/,(\s*[}\]])/g, '$1');
88
+ // Remove comments and trailing commas from JSON content
89
+ const sanitizedContent = stripJsonComments(content).replace(/,(\s*[}\]])/g, '$1');
89
90
  return JSON.parse(sanitizedContent);
90
91
  }
91
92
  catch (error) {
@@ -9,7 +9,7 @@ import { configProvider } from '../core/ConfigurationProvider.js';
9
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  const app = express();
11
11
  // Middleware
12
- app.use(express.static(path.join(__dirname, 'public')));
12
+ app.use('/', express.static(path.join(__dirname, '..', '..', 'src', 'web', 'public')));
13
13
  app.use(express.json());
14
14
  // Get available targets
15
15
  app.get('/api/targets', async (req, res) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imcp",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Node.js SDK for Model Context Protocol (MCP)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,6 +30,7 @@
30
30
  "commander": "^11.1.0",
31
31
  "express": "^4.18.2",
32
32
  "extract-zip": "^2.0.1",
33
+ "strip-json-comments": "^5.0.1",
33
34
  "unzipper": "^0.10.14"
34
35
  },
35
36
  "devDependencies": {
@@ -58,12 +58,31 @@ export class ClientInstaller {
58
58
  if (process.platform === 'win32') {
59
59
  // Windows-specific command check
60
60
  await execAsync(`where ${command}`);
61
+ } else if (process.platform === 'darwin' && (command === 'code' || command === 'code-insiders')) {
62
+ // macOS-specific VS Code check
63
+ const vscodePath = command === 'code' ?
64
+ '/Applications/Visual Studio Code.app' :
65
+ '/Applications/Visual Studio Code - Insiders.app';
66
+ await execAsync(`test -d "${vscodePath}"`);
61
67
  } else {
62
68
  // Unix-like systems
63
69
  await execAsync(`which ${command}`);
64
70
  }
65
71
  return true;
66
72
  } catch (error) {
73
+ if (process.platform === 'darwin' && (command === 'code' || command === 'code-insiders')) {
74
+ // Try checking in ~/Applications as well for macOS
75
+ try {
76
+ const homedir = process.env.HOME;
77
+ const vscodePath = command === 'code' ?
78
+ `${homedir}/Applications/Visual Studio Code.app` :
79
+ `${homedir}/Applications/Visual Studio Code - Insiders.app`;
80
+ await execAsync(`test -d "${vscodePath}"`);
81
+ return true;
82
+ } catch (error) {
83
+ return false;
84
+ }
85
+ }
67
86
  return false;
68
87
  }
69
88
  }
@@ -490,6 +509,12 @@ export class ClientInstaller {
490
509
  const npmPath = await this.getNpmPath();
491
510
  serverConfig.env['APPDATA'] = npmPath;
492
511
  }
512
+ // Convert backslashes to forward slashes in args paths
513
+ if (serverConfig.args) {
514
+ serverConfig.args = serverConfig.args.map((arg: string) =>
515
+ typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg
516
+ );
517
+ }
493
518
 
494
519
  // Add or update the server configuration
495
520
  settings.mcpServers[serverName] = {
@@ -525,6 +550,13 @@ export class ClientInstaller {
525
550
  settings.mcp.servers = {};
526
551
  }
527
552
 
553
+ // Convert backslashes to forward slashes in args paths
554
+ if (installConfig.args) {
555
+ installConfig.args = installConfig.args.map((arg: string) =>
556
+ typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg
557
+ );
558
+ }
559
+
528
560
  // Add or update the server configuration
529
561
  settings.mcp.servers[serverName] = {
530
562
  command: installConfig.command,
@@ -532,6 +564,7 @@ export class ClientInstaller {
532
564
  env: installConfig.env
533
565
  };
534
566
 
567
+
535
568
  // Write the updated settings back to the file
536
569
  await writeJsonFile(settingPath, settings);
537
570
  }
@@ -84,6 +84,8 @@ export function resolveNpmModulePath(pathString: string): string {
84
84
  * @param createIfNotExist Whether to create the file if it doesn't exist
85
85
  * @returns The parsed JSON content
86
86
  */
87
+ import stripJsonComments from 'strip-json-comments';
88
+
87
89
  export async function readJsonFile(filePath: string, createIfNotExist = false): Promise<any> {
88
90
  try {
89
91
  // Ensure directory exists
@@ -92,8 +94,8 @@ export async function readJsonFile(filePath: string, createIfNotExist = false):
92
94
  }
93
95
 
94
96
  const content = await fs.readFile(filePath, 'utf8');
95
- // Remove trailing commas from JSON content
96
- const sanitizedContent = content.replace(/,(\s*[}\]])/g, '$1');
97
+ // Remove comments and trailing commas from JSON content
98
+ const sanitizedContent = stripJsonComments(content).replace(/,(\s*[}\]])/g, '$1');
97
99
  return JSON.parse(sanitizedContent);
98
100
  } catch (error) {
99
101
  if ((error as NodeJS.ErrnoException).code === 'ENOENT' && createIfNotExist) {
package/src/web/server.ts CHANGED
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  const app = express();
13
13
 
14
14
  // Middleware
15
- app.use(express.static(path.join(__dirname, 'public')));
15
+ app.use('/', express.static(path.join(__dirname, '..', '..', 'src', 'web', 'public')));
16
16
  app.use(express.json());
17
17
 
18
18
  // API Routes