@meldocio/mcp-stdio-proxy 1.0.22 → 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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/bin/cli.js +33 -1088
- package/bin/meldoc-mcp-proxy.js +134 -1264
- package/lib/cli/commands.js +277 -0
- package/lib/cli/formatters.js +137 -0
- package/lib/core/constants.js +98 -0
- package/lib/http/client.js +61 -0
- package/lib/http/error-handler.js +195 -0
- package/lib/install/config-manager.js +203 -0
- package/lib/install/config-paths.js +198 -0
- package/lib/install/installers.js +328 -0
- package/lib/install/templates.js +266 -0
- package/lib/mcp/handlers.js +185 -0
- package/lib/mcp/tools-call.js +179 -0
- package/lib/protocol/error-codes.js +143 -0
- package/lib/protocol/json-rpc.js +183 -0
- package/lib/protocol/tools-schema.js +239 -0
- package/package.json +1 -1
- package/lib/constants.js +0 -31
- /package/lib/{auth.js → core/auth.js} +0 -0
- /package/lib/{config.js → core/config.js} +0 -0
- /package/lib/{credentials.js → core/credentials.js} +0 -0
- /package/lib/{device-flow.js → core/device-flow.js} +0 -0
- /package/lib/{logger.js → core/logger.js} +0 -0
- /package/lib/{workspace.js → core/workspace.js} +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Error Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles workspace-required and authentication errors from backend.
|
|
5
|
+
* Consolidates error handling logic that was duplicated 3+ times in proxy.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { JSON_RPC_ERROR_CODES, CUSTOM_ERROR_CODES } = require('../protocol/error-codes');
|
|
9
|
+
const { sendError, writeResponse } = require('../protocol/json-rpc');
|
|
10
|
+
const { LOG_LEVELS } = require('../core/constants');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get current log level
|
|
14
|
+
*/
|
|
15
|
+
function getLogLevel() {
|
|
16
|
+
const level = (process.env.LOG_LEVEL || 'ERROR').toUpperCase();
|
|
17
|
+
return LOG_LEVELS[level] !== undefined ? LOG_LEVELS[level] : LOG_LEVELS.ERROR;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const LOG_LEVEL = getLogLevel();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Log message based on level
|
|
24
|
+
*/
|
|
25
|
+
function log(level, message) {
|
|
26
|
+
if (LOG_LEVEL >= level) {
|
|
27
|
+
const levelName = Object.keys(LOG_LEVELS)[level] || 'UNKNOWN';
|
|
28
|
+
process.stderr.write(`[${levelName}] ${message}\n`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if error indicates workspace is required
|
|
34
|
+
* @param {Object} error - Error object from response
|
|
35
|
+
* @returns {boolean} True if workspace required
|
|
36
|
+
*/
|
|
37
|
+
function isWorkspaceRequiredError(error) {
|
|
38
|
+
if (!error) return false;
|
|
39
|
+
|
|
40
|
+
const errorCode = error.code;
|
|
41
|
+
const errorMessage = String(error.message || '');
|
|
42
|
+
const errorData = error.data || {};
|
|
43
|
+
|
|
44
|
+
// Check for explicit workspace required code
|
|
45
|
+
if (errorCode === 'WORKSPACE_REQUIRED' || errorData.code === 'WORKSPACE_REQUIRED') {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for workspace message in error
|
|
50
|
+
const hasWorkspaceMessage =
|
|
51
|
+
errorMessage.includes('Multiple workspaces available') ||
|
|
52
|
+
errorMessage.includes('Specify workspace');
|
|
53
|
+
|
|
54
|
+
// Server error with workspace message
|
|
55
|
+
if (errorCode === JSON_RPC_ERROR_CODES.SERVER_ERROR && hasWorkspaceMessage) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if error indicates authentication is required
|
|
64
|
+
* @param {Object} error - Error object from response
|
|
65
|
+
* @returns {boolean} True if auth required
|
|
66
|
+
*/
|
|
67
|
+
function isAuthRequiredError(error) {
|
|
68
|
+
if (!error) return false;
|
|
69
|
+
|
|
70
|
+
const errorCode = error.code;
|
|
71
|
+
const errorData = error.data || {};
|
|
72
|
+
|
|
73
|
+
return errorCode === 'AUTH_REQUIRED' || errorData.code === 'AUTH_REQUIRED';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Handle workspace required error
|
|
78
|
+
* @param {string|number} requestId - Request ID
|
|
79
|
+
* @param {string} [toolName] - Tool name being called
|
|
80
|
+
*/
|
|
81
|
+
function handleWorkspaceRequiredError(requestId, toolName) {
|
|
82
|
+
log(LOG_LEVELS.DEBUG, `Detected WORKSPACE_REQUIRED error for tool: ${toolName || 'unknown'}`);
|
|
83
|
+
|
|
84
|
+
// Special handling for list_workspaces tool
|
|
85
|
+
if (toolName === 'list_workspaces') {
|
|
86
|
+
const message = `Backend requires workspace selection even for ${toolName}. Please set a default workspace using set_workspace tool first, or contact support if this persists.`;
|
|
87
|
+
sendError(requestId, JSON_RPC_ERROR_CODES.SERVER_ERROR, message, {
|
|
88
|
+
code: 'WORKSPACE_REQUIRED',
|
|
89
|
+
hint: 'Try setting a default workspace first using set_workspace tool, or specify workspaceAlias/workspaceId in the tool call arguments.'
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// General workspace required message
|
|
95
|
+
const message = 'Multiple workspaces available. Use list_workspaces tool to get list, then use set_workspace to set default workspace, or specify workspaceAlias or workspaceId parameter in tool call.';
|
|
96
|
+
sendError(requestId, JSON_RPC_ERROR_CODES.SERVER_ERROR, message, {
|
|
97
|
+
code: 'WORKSPACE_REQUIRED',
|
|
98
|
+
hint: 'Use list_workspaces tool to get available workspaces, then use set_workspace to set default, or specify workspaceAlias or workspaceId in tool call.'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Handle authentication required error
|
|
104
|
+
* @param {string|number} requestId - Request ID
|
|
105
|
+
*/
|
|
106
|
+
function handleAuthRequiredError(requestId) {
|
|
107
|
+
const message = 'Authentication required. Run: npx @meldocio/mcp-stdio-proxy@latest auth login';
|
|
108
|
+
sendError(requestId, CUSTOM_ERROR_CODES.AUTH_REQUIRED, message, {
|
|
109
|
+
code: 'AUTH_REQUIRED',
|
|
110
|
+
hint: 'Use auth_login_instructions tool to get login command'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Process backend response and handle special errors
|
|
116
|
+
* @param {Object} response - Axios response object
|
|
117
|
+
* @param {Object} request - Original request
|
|
118
|
+
* @returns {boolean} True if error was handled, false if should be forwarded
|
|
119
|
+
*/
|
|
120
|
+
function handleBackendResponse(response, request) {
|
|
121
|
+
const responseData = response.data || {};
|
|
122
|
+
|
|
123
|
+
// Successful HTTP response (200, 201, etc.)
|
|
124
|
+
if (response.status >= 200 && response.status < 300) {
|
|
125
|
+
// Ensure response has id
|
|
126
|
+
if (responseData && !responseData.id && request.id !== undefined) {
|
|
127
|
+
responseData.id = request.id;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check for error in response data
|
|
131
|
+
if (responseData.error) {
|
|
132
|
+
const error = responseData.error;
|
|
133
|
+
const toolName = request.params?.name;
|
|
134
|
+
|
|
135
|
+
log(LOG_LEVELS.DEBUG, `Error response: code=${error.code}, message="${error.message}", toolName=${toolName}`);
|
|
136
|
+
|
|
137
|
+
// Check for workspace required
|
|
138
|
+
if (isWorkspaceRequiredError(error)) {
|
|
139
|
+
handleWorkspaceRequiredError(request.id, toolName);
|
|
140
|
+
return true; // Handled
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check for auth required
|
|
144
|
+
if (isAuthRequiredError(error)) {
|
|
145
|
+
handleAuthRequiredError(request.id);
|
|
146
|
+
return true; // Handled
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Forward unhandled errors as-is
|
|
150
|
+
log(LOG_LEVELS.DEBUG, `Forwarding unhandled error: ${JSON.stringify(error)}`);
|
|
151
|
+
writeResponse(responseData);
|
|
152
|
+
return true; // Handled (forwarded)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Success response - forward as-is
|
|
156
|
+
writeResponse(responseData);
|
|
157
|
+
return true; // Handled
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// HTTP error status (400, 401, 404, etc.)
|
|
161
|
+
log(LOG_LEVELS.DEBUG, `HTTP error status ${response.status}`);
|
|
162
|
+
|
|
163
|
+
const errorMessage = responseData.error?.message ||
|
|
164
|
+
responseData.message ||
|
|
165
|
+
`HTTP ${response.status}: ${response.statusText}`;
|
|
166
|
+
|
|
167
|
+
// Check for workspace/auth errors in HTTP error responses
|
|
168
|
+
const error = responseData.error || responseData;
|
|
169
|
+
|
|
170
|
+
if (isWorkspaceRequiredError(error)) {
|
|
171
|
+
const toolName = request.params?.name;
|
|
172
|
+
handleWorkspaceRequiredError(request.id, toolName);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isAuthRequiredError(error)) {
|
|
177
|
+
handleAuthRequiredError(request.id);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Forward other HTTP errors
|
|
182
|
+
sendError(request.id, JSON_RPC_ERROR_CODES.SERVER_ERROR, errorMessage, {
|
|
183
|
+
httpStatus: response.status,
|
|
184
|
+
code: error.code || 'HTTP_ERROR'
|
|
185
|
+
});
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
isWorkspaceRequiredError,
|
|
191
|
+
isAuthRequiredError,
|
|
192
|
+
handleWorkspaceRequiredError,
|
|
193
|
+
handleAuthRequiredError,
|
|
194
|
+
handleBackendResponse
|
|
195
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration File Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles reading, writing, and validating MCP configuration files.
|
|
5
|
+
* Provides safe file operations with error handling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if a file exists
|
|
13
|
+
* @param {string} filePath - Path to file
|
|
14
|
+
* @returns {boolean} True if file exists
|
|
15
|
+
*/
|
|
16
|
+
function fileExists(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return fs.existsSync(filePath);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read and parse JSON configuration file
|
|
26
|
+
* @param {string} filePath - Path to config file
|
|
27
|
+
* @returns {Object|null} Parsed configuration object, or null if file doesn't exist or parse fails
|
|
28
|
+
*/
|
|
29
|
+
function readConfig(filePath) {
|
|
30
|
+
try {
|
|
31
|
+
if (!fileExists(filePath)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
36
|
+
return JSON.parse(content);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// Return null on any error (file not found, parse error, etc.)
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Write configuration object to JSON file
|
|
45
|
+
* Creates parent directories if they don't exist
|
|
46
|
+
* @param {string} filePath - Path to config file
|
|
47
|
+
* @param {Object} config - Configuration object to write
|
|
48
|
+
* @throws {Error} If write fails
|
|
49
|
+
*/
|
|
50
|
+
function writeConfig(filePath, config) {
|
|
51
|
+
const dir = path.dirname(filePath);
|
|
52
|
+
|
|
53
|
+
// Create directory if it doesn't exist
|
|
54
|
+
if (!fileExists(dir)) {
|
|
55
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Write config file with pretty formatting
|
|
59
|
+
const content = JSON.stringify(config, null, 2);
|
|
60
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Safely read configuration with fallback to empty object
|
|
65
|
+
* @param {string} filePath - Path to config file
|
|
66
|
+
* @returns {Object} Configuration object (empty if file doesn't exist or is invalid)
|
|
67
|
+
*/
|
|
68
|
+
function readConfigSafe(filePath) {
|
|
69
|
+
const config = readConfig(filePath);
|
|
70
|
+
return config || {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Backup configuration file before modification
|
|
75
|
+
* @param {string} filePath - Path to config file
|
|
76
|
+
* @returns {string|null} Path to backup file, or null if backup failed
|
|
77
|
+
*/
|
|
78
|
+
function backupConfig(filePath) {
|
|
79
|
+
try {
|
|
80
|
+
if (!fileExists(filePath)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
85
|
+
const backupPath = `${filePath}.backup-${timestamp}`;
|
|
86
|
+
|
|
87
|
+
fs.copyFileSync(filePath, backupPath);
|
|
88
|
+
return backupPath;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validate that config is a valid JSON object
|
|
96
|
+
* @param {any} config - Configuration to validate
|
|
97
|
+
* @returns {boolean} True if valid
|
|
98
|
+
*/
|
|
99
|
+
function isValidConfig(config) {
|
|
100
|
+
return config !== null && typeof config === 'object' && !Array.isArray(config);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get file modification time
|
|
105
|
+
* @param {string} filePath - Path to file
|
|
106
|
+
* @returns {Date|null} Modification time, or null if file doesn't exist
|
|
107
|
+
*/
|
|
108
|
+
function getFileModTime(filePath) {
|
|
109
|
+
try {
|
|
110
|
+
if (!fileExists(filePath)) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const stats = fs.statSync(filePath);
|
|
114
|
+
return stats.mtime;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Ensure parent directory exists for a file path
|
|
122
|
+
* @param {string} filePath - Path to file
|
|
123
|
+
*/
|
|
124
|
+
function ensureDirectoryExists(filePath) {
|
|
125
|
+
const dir = path.dirname(filePath);
|
|
126
|
+
if (!fileExists(dir)) {
|
|
127
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Delete configuration file
|
|
133
|
+
* @param {string} filePath - Path to config file
|
|
134
|
+
* @returns {boolean} True if file was deleted, false if it didn't exist
|
|
135
|
+
*/
|
|
136
|
+
function deleteConfig(filePath) {
|
|
137
|
+
try {
|
|
138
|
+
if (!fileExists(filePath)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
fs.unlinkSync(filePath);
|
|
142
|
+
return true;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new Error(`Failed to delete config file: ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Read, modify, and write config in a single operation
|
|
150
|
+
* @param {string} filePath - Path to config file
|
|
151
|
+
* @param {Function} modifier - Function that takes config and returns modified config
|
|
152
|
+
* @param {boolean} [createBackup=false] - Whether to create backup before writing
|
|
153
|
+
* @returns {Object} { success: boolean, backupPath?: string, error?: string }
|
|
154
|
+
*/
|
|
155
|
+
function modifyConfig(filePath, modifier, createBackup = false) {
|
|
156
|
+
try {
|
|
157
|
+
// Read existing config
|
|
158
|
+
const config = readConfigSafe(filePath);
|
|
159
|
+
|
|
160
|
+
// Apply modifications
|
|
161
|
+
const modifiedConfig = modifier(config);
|
|
162
|
+
|
|
163
|
+
// Validate result
|
|
164
|
+
if (!isValidConfig(modifiedConfig)) {
|
|
165
|
+
return {
|
|
166
|
+
success: false,
|
|
167
|
+
error: 'Modified configuration is not a valid object'
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Create backup if requested
|
|
172
|
+
let backupPath = null;
|
|
173
|
+
if (createBackup && fileExists(filePath)) {
|
|
174
|
+
backupPath = backupConfig(filePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Write modified config
|
|
178
|
+
writeConfig(filePath, modifiedConfig);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
success: true,
|
|
182
|
+
backupPath
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
error: error.message
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
fileExists,
|
|
194
|
+
readConfig,
|
|
195
|
+
writeConfig,
|
|
196
|
+
readConfigSafe,
|
|
197
|
+
backupConfig,
|
|
198
|
+
isValidConfig,
|
|
199
|
+
getFileModTime,
|
|
200
|
+
ensureDirectoryExists,
|
|
201
|
+
deleteConfig,
|
|
202
|
+
modifyConfig
|
|
203
|
+
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Path Management
|
|
3
|
+
*
|
|
4
|
+
* Centralized configuration file path management for all MCP clients.
|
|
5
|
+
* Supports Claude Desktop, Cursor, Claude Code, and local installations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Platform detection utilities
|
|
13
|
+
*/
|
|
14
|
+
const PLATFORMS = {
|
|
15
|
+
MAC: 'darwin',
|
|
16
|
+
WINDOWS: 'win32',
|
|
17
|
+
LINUX: 'linux'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function getPlatform() {
|
|
21
|
+
return os.platform();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isMac() {
|
|
25
|
+
return getPlatform() === PLATFORMS.MAC;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isWindows() {
|
|
29
|
+
return getPlatform() === PLATFORMS.WINDOWS;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isLinux() {
|
|
33
|
+
return getPlatform() === PLATFORMS.LINUX;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get home directory
|
|
38
|
+
*/
|
|
39
|
+
function getHomeDir() {
|
|
40
|
+
return os.homedir();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get current working directory
|
|
45
|
+
*/
|
|
46
|
+
function getCwd() {
|
|
47
|
+
return process.cwd();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Client configuration path resolvers
|
|
52
|
+
* Each function returns the appropriate config file path for the target client
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get Claude Desktop config file path (platform-specific)
|
|
57
|
+
* @returns {string} Path to claude_desktop_config.json
|
|
58
|
+
*/
|
|
59
|
+
function getClaudeDesktopConfigPath() {
|
|
60
|
+
const homeDir = getHomeDir();
|
|
61
|
+
|
|
62
|
+
if (isMac()) {
|
|
63
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isWindows()) {
|
|
67
|
+
const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming');
|
|
68
|
+
return path.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Linux and others
|
|
72
|
+
return path.join(homeDir, '.config', 'Claude', 'claude_desktop_config.json');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get Cursor project config file path (.cursor/mcp.json in current directory)
|
|
77
|
+
* @returns {string} Path to project-specific Cursor config
|
|
78
|
+
*/
|
|
79
|
+
function getCursorProjectConfigPath() {
|
|
80
|
+
return path.join(getCwd(), '.cursor', 'mcp.json');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get Cursor global config file path (~/.cursor/mcp.json)
|
|
85
|
+
* @returns {string} Path to global Cursor config
|
|
86
|
+
*/
|
|
87
|
+
function getCursorGlobalConfigPath() {
|
|
88
|
+
return path.join(getHomeDir(), '.cursor', 'mcp.json');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get Claude Code project config path (.mcp.json in current directory)
|
|
93
|
+
* @returns {string} Path to project-specific Claude Code config
|
|
94
|
+
*/
|
|
95
|
+
function getClaudeCodeProjectConfigPath() {
|
|
96
|
+
return path.join(getCwd(), '.mcp.json');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get Claude Code user config path (~/.claude.json)
|
|
101
|
+
* @returns {string} Path to user-level Claude Code config
|
|
102
|
+
*/
|
|
103
|
+
function getClaudeCodeUserConfigPath() {
|
|
104
|
+
return path.join(getHomeDir(), '.claude.json');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get Claude Code local config path (same as user, but with project context)
|
|
109
|
+
* Note: Local scope uses ~/.claude.json but stores project-specific paths
|
|
110
|
+
* @returns {string} Path to local Claude Code config
|
|
111
|
+
*/
|
|
112
|
+
function getClaudeCodeLocalConfigPath() {
|
|
113
|
+
return path.join(getHomeDir(), '.claude.json');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get local mcp.json path (in current directory)
|
|
118
|
+
* @returns {string} Path to generic local MCP config
|
|
119
|
+
*/
|
|
120
|
+
function getLocalMcpJsonPath() {
|
|
121
|
+
return path.join(getCwd(), 'mcp.json');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get config path by client type and scope
|
|
126
|
+
* @param {string} client - Client type: 'claude-desktop', 'cursor', 'claude-code', 'local'
|
|
127
|
+
* @param {string} [scope] - Installation scope: 'global', 'project', 'user', 'local'
|
|
128
|
+
* @returns {string} Config file path
|
|
129
|
+
* @throws {Error} If client type is unknown or scope is invalid
|
|
130
|
+
*/
|
|
131
|
+
function getConfigPath(client, scope = 'project') {
|
|
132
|
+
switch (client) {
|
|
133
|
+
case 'claude-desktop':
|
|
134
|
+
return getClaudeDesktopConfigPath();
|
|
135
|
+
|
|
136
|
+
case 'cursor':
|
|
137
|
+
if (scope === 'global') {
|
|
138
|
+
return getCursorGlobalConfigPath();
|
|
139
|
+
}
|
|
140
|
+
return getCursorProjectConfigPath();
|
|
141
|
+
|
|
142
|
+
case 'claude-code':
|
|
143
|
+
if (scope === 'user') {
|
|
144
|
+
return getClaudeCodeUserConfigPath();
|
|
145
|
+
}
|
|
146
|
+
if (scope === 'local') {
|
|
147
|
+
return getClaudeCodeLocalConfigPath();
|
|
148
|
+
}
|
|
149
|
+
return getClaudeCodeProjectConfigPath();
|
|
150
|
+
|
|
151
|
+
case 'local':
|
|
152
|
+
return getLocalMcpJsonPath();
|
|
153
|
+
|
|
154
|
+
default:
|
|
155
|
+
throw new Error(`Unknown client type: ${client}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get friendly display name for client
|
|
161
|
+
* @param {string} client - Client type
|
|
162
|
+
* @returns {string} Display name
|
|
163
|
+
*/
|
|
164
|
+
function getClientDisplayName(client) {
|
|
165
|
+
const names = {
|
|
166
|
+
'claude-desktop': 'Claude Desktop',
|
|
167
|
+
'cursor': 'Cursor',
|
|
168
|
+
'claude-code': 'Claude Code',
|
|
169
|
+
'local': 'Local MCP'
|
|
170
|
+
};
|
|
171
|
+
return names[client] || client;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
// Platform utilities
|
|
176
|
+
getPlatform,
|
|
177
|
+
isMac,
|
|
178
|
+
isWindows,
|
|
179
|
+
isLinux,
|
|
180
|
+
getHomeDir,
|
|
181
|
+
getCwd,
|
|
182
|
+
|
|
183
|
+
// Individual path getters (for backward compatibility)
|
|
184
|
+
getClaudeDesktopConfigPath,
|
|
185
|
+
getCursorProjectConfigPath,
|
|
186
|
+
getCursorGlobalConfigPath,
|
|
187
|
+
getClaudeCodeProjectConfigPath,
|
|
188
|
+
getClaudeCodeUserConfigPath,
|
|
189
|
+
getClaudeCodeLocalConfigPath,
|
|
190
|
+
getLocalMcpJsonPath,
|
|
191
|
+
|
|
192
|
+
// Unified interface
|
|
193
|
+
getConfigPath,
|
|
194
|
+
getClientDisplayName,
|
|
195
|
+
|
|
196
|
+
// Constants
|
|
197
|
+
PLATFORMS
|
|
198
|
+
};
|