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 +5 -5
- package/dist/core/installers/ClientInstaller.js +29 -0
- package/dist/utils/clientUtils.d.ts +0 -6
- package/dist/utils/clientUtils.js +3 -2
- package/dist/web/server.js +1 -1
- package/package.json +2 -1
- package/src/core/installers/ClientInstaller.ts +33 -0
- package/src/utils/clientUtils.ts +4 -2
- package/src/web/server.ts +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# IMCP
|
|
2
2
|
|
|
3
|
-
IMCP
|
|
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
|
|
7
|
+
IMCP allows you to:
|
|
8
8
|
|
|
9
|
-
- Discover available MCP servers
|
|
10
|
-
- Install servers with specific configurations
|
|
9
|
+
- Discover available MCP servers
|
|
11
10
|
- Manage server installations
|
|
12
|
-
- Run a local web interface
|
|
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) {
|
package/dist/web/server.js
CHANGED
|
@@ -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
|
+
"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
|
}
|
package/src/utils/clientUtils.ts
CHANGED
|
@@ -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
|