mcp-doctor 0.1.0

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/cli/commands/check.d.ts +5 -0
  4. package/dist/cli/commands/check.js +67 -0
  5. package/dist/cli/commands/check.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.js +24 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/config/locations.d.ts +3 -0
  10. package/dist/config/locations.js +138 -0
  11. package/dist/config/locations.js.map +1 -0
  12. package/dist/config/types.d.ts +37 -0
  13. package/dist/config/types.js +2 -0
  14. package/dist/config/types.js.map +1 -0
  15. package/dist/utils/output.d.ts +6 -0
  16. package/dist/utils/output.js +102 -0
  17. package/dist/utils/output.js.map +1 -0
  18. package/dist/validators/env-vars.d.ts +2 -0
  19. package/dist/validators/env-vars.js +70 -0
  20. package/dist/validators/env-vars.js.map +1 -0
  21. package/dist/validators/index.d.ts +4 -0
  22. package/dist/validators/index.js +5 -0
  23. package/dist/validators/index.js.map +1 -0
  24. package/dist/validators/json-syntax.d.ts +6 -0
  25. package/dist/validators/json-syntax.js +77 -0
  26. package/dist/validators/json-syntax.js.map +1 -0
  27. package/dist/validators/paths.d.ts +2 -0
  28. package/dist/validators/paths.js +125 -0
  29. package/dist/validators/paths.js.map +1 -0
  30. package/dist/validators/server-health.d.ts +2 -0
  31. package/dist/validators/server-health.js +135 -0
  32. package/dist/validators/server-health.js.map +1 -0
  33. package/package.json +45 -0
  34. package/src/cli/commands/check.ts +103 -0
  35. package/src/cli/index.ts +29 -0
  36. package/src/config/locations.ts +141 -0
  37. package/src/config/types.ts +45 -0
  38. package/src/utils/output.ts +128 -0
  39. package/src/validators/env-vars.ts +85 -0
  40. package/src/validators/index.ts +4 -0
  41. package/src/validators/json-syntax.ts +83 -0
  42. package/src/validators/paths.ts +140 -0
  43. package/src/validators/server-health.ts +165 -0
  44. package/tsconfig.json +18 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # mcp-doctor
2
+
3
+ Diagnose and fix MCP configuration issues across Claude Desktop, Cursor, VS Code, Claude Code, and more.
4
+
5
+ ## The Problem
6
+
7
+ MCP configuration is fragile:
8
+
9
+ - **Trailing commas** break JSON parsing silently
10
+ - **Wrong paths** cause "server not found" errors
11
+ - **Missing environment variables** lead to auth failures
12
+ - **Server crashes** happen with unhelpful error messages
13
+
14
+ You end up spending hours debugging issues that should take seconds to find.
15
+
16
+ ## The Solution
17
+
18
+ ```bash
19
+ npm install -g mcp-doctor
20
+ mcp-doctor
21
+ ```
22
+
23
+ That's it. It scans your configs and tells you exactly what's wrong:
24
+
25
+ ```
26
+ 🩺 MCP Doctor
27
+
28
+ Found 2 config files:
29
+
30
+ Claude Desktop
31
+ ~/Library/Application Support/Claude/claude_desktop_config.json
32
+
33
+ Cursor (project)
34
+ ./.cursor/mcp.json
35
+
36
+ āŒ 1 Error
37
+
38
+ ~/.cursor/mcp.json:12
39
+ Trailing comma detected (not allowed in JSON)
40
+ šŸ’” Remove the comma before the closing bracket/brace
41
+
42
+ āš ļø 2 Warnings
43
+
44
+ ~/.cursor/mcp.json
45
+ Server "github": Environment variable GITHUB_TOKEN is not set
46
+ šŸ’” Set it with: export GITHUB_TOKEN="your-value"
47
+
48
+ ~/.cursor/mcp.json
49
+ Server "myserver": Using relative path "./server.js"
50
+ šŸ’” Consider using absolute path: /Users/you/project/server.js
51
+
52
+ šŸ”Œ Server Health: 3 healthy, 1 failed
53
+
54
+ āœ“ filesystem (120ms)
55
+ āœ“ fetch (89ms)
56
+ āœ“ github (340ms)
57
+ āœ— broken-server
58
+ Timeout after 10000ms
59
+
60
+ ──────────────────────────────────────────────────
61
+
62
+ āŒ Found 1 error that needs fixing
63
+ āš ļø 3/4 servers responding
64
+ ```
65
+
66
+ ## What It Checks
67
+
68
+ | Check | Description |
69
+ | ------------------------- | ------------------------------------------------------- |
70
+ | **JSON Syntax** | Catches trailing commas, missing brackets, invalid JSON |
71
+ | **Path Validation** | Verifies command paths exist and are executable |
72
+ | **Environment Variables** | Detects missing or empty env vars |
73
+ | **Hardcoded Secrets** | Warns about API keys in config files |
74
+ | **Server Health** | Actually starts each server and tests MCP handshake |
75
+
76
+ ## Supported Clients
77
+
78
+ - āœ… Claude Desktop (macOS, Windows, Linux)
79
+ - āœ… Cursor
80
+ - āœ… VS Code (with MCP extension)
81
+ - āœ… Claude Code
82
+ - āœ… Windsurf
83
+
84
+ ## Usage
85
+
86
+ ```bash
87
+ # Check all configs (default)
88
+ mcp-doctor
89
+
90
+ # Same as above
91
+ mcp-doctor check
92
+
93
+ # Skip server health tests (faster)
94
+ mcp-doctor check --skip-health
95
+ ```
96
+
97
+ ## Installation
98
+
99
+ Requires Node.js 18 or later.
100
+
101
+ ```bash
102
+ # npm
103
+ npm install -g mcp-doctor
104
+
105
+ # Or run directly with npx
106
+ npx mcp-doctor
107
+ ```
108
+
109
+ ## FAQ
110
+
111
+ **Q: It says "No MCP configuration files found"**
112
+
113
+ Make sure you have at least one MCP client configured. The tool looks in standard locations for each client.
114
+
115
+ **Q: Server health check is timing out**
116
+
117
+ Some servers take longer to start. The default timeout is 10 seconds. If your server needs more time, it might indicate a performance issue.
118
+
119
+ **Q: Can it auto-fix issues?**
120
+
121
+ Not yet. For now it tells you what's wrong and how to fix it. Auto-fix for simple issues (like trailing commas) is planned.
122
+
123
+ ## Contributing
124
+
125
+ Issues and PRs welcome! https://github.com/Crooj026/mcp-doctor
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,5 @@
1
+ interface CheckOptions {
2
+ skipHealth?: boolean;
3
+ }
4
+ export declare function checkCommand(options?: CheckOptions): Promise<void>;
5
+ export {};
@@ -0,0 +1,67 @@
1
+ import ora from 'ora';
2
+ import { getConfigLocations } from '../../config/locations.js';
3
+ import { validateJsonSyntax, validatePaths, validateEnvVars, testServerHealth, } from '../../validators/index.js';
4
+ import { printHeader, printConfigsFound, printValidationResults, printServerResults, printSummary, } from '../../utils/output.js';
5
+ export async function checkCommand(options = {}) {
6
+ printHeader('🩺 MCP Doctor');
7
+ const spinner = ora('Scanning for MCP configurations...').start();
8
+ // Find all config files
9
+ const locations = getConfigLocations();
10
+ const foundConfigs = locations.filter((l) => l.exists);
11
+ spinner.stop();
12
+ // Show what we found
13
+ printConfigsFound(locations);
14
+ if (foundConfigs.length === 0) {
15
+ process.exitCode = 0;
16
+ return;
17
+ }
18
+ // Run validators on each config
19
+ const allResults = [];
20
+ const allServerResults = [];
21
+ const configsWithServers = [];
22
+ for (const configLoc of foundConfigs) {
23
+ // JSON syntax check
24
+ const { valid, results: syntaxResults, config } = validateJsonSyntax(configLoc.path);
25
+ allResults.push(...syntaxResults);
26
+ // If JSON is invalid, skip other validators for this file
27
+ if (!valid || !config) {
28
+ continue;
29
+ }
30
+ // Store for server health testing
31
+ configsWithServers.push({ path: configLoc.path, config });
32
+ // Path validation
33
+ allResults.push(...validatePaths(config, configLoc.path));
34
+ // Environment variable validation
35
+ allResults.push(...validateEnvVars(config, configLoc.path));
36
+ }
37
+ // Print validation results
38
+ printValidationResults(allResults);
39
+ // Server health tests (unless skipped)
40
+ if (!options.skipHealth && configsWithServers.length > 0) {
41
+ const healthSpinner = ora('Testing server connectivity...').start();
42
+ for (const { config } of configsWithServers) {
43
+ const results = await testServerHealth(config);
44
+ allServerResults.push(...results);
45
+ }
46
+ healthSpinner.stop();
47
+ // Deduplicate servers by name (same server might be in multiple configs)
48
+ const uniqueServers = new Map();
49
+ for (const result of allServerResults) {
50
+ // Keep the healthy result if we have conflicting results
51
+ if (!uniqueServers.has(result.name) || result.healthy) {
52
+ uniqueServers.set(result.name, result);
53
+ }
54
+ }
55
+ printServerResults(Array.from(uniqueServers.values()));
56
+ }
57
+ // Summary
58
+ const errors = allResults.filter((r) => r.level === 'error').length;
59
+ const warnings = allResults.filter((r) => r.level === 'warning').length;
60
+ const healthyServers = allServerResults.filter((s) => s.healthy).length;
61
+ printSummary(errors, warnings, healthyServers, allServerResults.length);
62
+ // Set exit code
63
+ if (errors > 0) {
64
+ process.exitCode = 1;
65
+ }
66
+ }
67
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { checkCommand } from './commands/check.js';
4
+ const program = new Command();
5
+ program
6
+ .name('mcp-doctor')
7
+ .description('Diagnose and fix MCP configuration issues')
8
+ .version('0.1.0');
9
+ program
10
+ .command('check')
11
+ .description('Validate all MCP configuration files')
12
+ .option('--skip-health', 'Skip server health checks')
13
+ .action(async (options) => {
14
+ await checkCommand({
15
+ skipHealth: options.skipHealth,
16
+ });
17
+ });
18
+ // Default command (no subcommand) runs check
19
+ program
20
+ .action(async () => {
21
+ await checkCommand();
22
+ });
23
+ program.parse();
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,sCAAsC,CAAC;KACnD,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC;KACpD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,YAAY,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,6CAA6C;AAC7C,OAAO;KACJ,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ConfigLocation } from './types.js';
2
+ export declare function getConfigLocations(projectDir?: string): ConfigLocation[];
3
+ export declare function getServersFromConfig(config: Record<string, unknown>): Record<string, unknown> | null;
@@ -0,0 +1,138 @@
1
+ import { homedir, platform } from 'os';
2
+ import { join } from 'path';
3
+ import { existsSync } from 'fs';
4
+ export function getConfigLocations(projectDir) {
5
+ const home = homedir();
6
+ const os = platform();
7
+ const cwd = projectDir || process.cwd();
8
+ const locations = [];
9
+ // ============================================
10
+ // Claude Desktop
11
+ // ============================================
12
+ if (os === 'darwin') {
13
+ locations.push({
14
+ client: 'Claude Desktop',
15
+ scope: 'global',
16
+ path: join(home, 'Library/Application Support/Claude/claude_desktop_config.json'),
17
+ });
18
+ }
19
+ else if (os === 'win32') {
20
+ locations.push({
21
+ client: 'Claude Desktop',
22
+ scope: 'global',
23
+ path: join(process.env.APPDATA || join(home, 'AppData/Roaming'), 'Claude/claude_desktop_config.json'),
24
+ });
25
+ }
26
+ else {
27
+ // Linux
28
+ locations.push({
29
+ client: 'Claude Desktop',
30
+ scope: 'global',
31
+ path: join(home, '.config/claude/claude_desktop_config.json'),
32
+ });
33
+ }
34
+ // ============================================
35
+ // Claude Code
36
+ // ============================================
37
+ locations.push({
38
+ client: 'Claude Code',
39
+ scope: 'global',
40
+ path: join(home, '.claude.json'),
41
+ });
42
+ locations.push({
43
+ client: 'Claude Code',
44
+ scope: 'project',
45
+ path: join(cwd, '.mcp.json'),
46
+ });
47
+ // ============================================
48
+ // Cursor
49
+ // ============================================
50
+ if (os === 'darwin') {
51
+ locations.push({
52
+ client: 'Cursor',
53
+ scope: 'global',
54
+ path: join(home, 'Library/Application Support/Cursor/User/globalStorage/cursor.mcp/mcp.json'),
55
+ });
56
+ }
57
+ else if (os === 'win32') {
58
+ locations.push({
59
+ client: 'Cursor',
60
+ scope: 'global',
61
+ path: join(process.env.APPDATA || join(home, 'AppData/Roaming'), 'Cursor/User/globalStorage/cursor.mcp/mcp.json'),
62
+ });
63
+ }
64
+ else {
65
+ locations.push({
66
+ client: 'Cursor',
67
+ scope: 'global',
68
+ path: join(home, '.config/Cursor/User/globalStorage/cursor.mcp/mcp.json'),
69
+ });
70
+ }
71
+ // Also check older Cursor location
72
+ locations.push({
73
+ client: 'Cursor',
74
+ scope: 'global',
75
+ path: join(home, '.cursor/mcp.json'),
76
+ });
77
+ locations.push({
78
+ client: 'Cursor',
79
+ scope: 'project',
80
+ path: join(cwd, '.cursor/mcp.json'),
81
+ });
82
+ // ============================================
83
+ // VS Code (MCP extension)
84
+ // ============================================
85
+ if (os === 'darwin') {
86
+ locations.push({
87
+ client: 'VS Code',
88
+ scope: 'global',
89
+ path: join(home, 'Library/Application Support/Code/User/settings.json'),
90
+ });
91
+ }
92
+ else if (os === 'win32') {
93
+ locations.push({
94
+ client: 'VS Code',
95
+ scope: 'global',
96
+ path: join(process.env.APPDATA || join(home, 'AppData/Roaming'), 'Code/User/settings.json'),
97
+ });
98
+ }
99
+ else {
100
+ locations.push({
101
+ client: 'VS Code',
102
+ scope: 'global',
103
+ path: join(home, '.config/Code/User/settings.json'),
104
+ });
105
+ }
106
+ locations.push({
107
+ client: 'VS Code',
108
+ scope: 'project',
109
+ path: join(cwd, '.vscode/settings.json'),
110
+ });
111
+ // ============================================
112
+ // Windsurf
113
+ // ============================================
114
+ locations.push({
115
+ client: 'Windsurf',
116
+ scope: 'global',
117
+ path: join(home, '.codeium/windsurf/mcp_config.json'),
118
+ });
119
+ // Check existence and return
120
+ return locations.map((loc) => ({
121
+ ...loc,
122
+ exists: existsSync(loc.path),
123
+ }));
124
+ }
125
+ export function getServersFromConfig(config) {
126
+ // Try different config formats
127
+ if (config.mcpServers && typeof config.mcpServers === 'object') {
128
+ return config.mcpServers;
129
+ }
130
+ if (config['mcp.servers'] && typeof config['mcp.servers'] === 'object') {
131
+ return config['mcp.servers'];
132
+ }
133
+ if (config.servers && typeof config.servers === 'object') {
134
+ return config.servers;
135
+ }
136
+ return null;
137
+ }
138
+ //# sourceMappingURL=locations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locations.js","sourceRoot":"","sources":["../../src/config/locations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAGhC,MAAM,UAAU,kBAAkB,CAAC,UAAmB;IACpD,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAExC,MAAM,SAAS,GAA0C,EAAE,CAAC;IAE5D,+CAA+C;IAC/C,iBAAiB;IACjB,+CAA+C;IAC/C,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpB,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,gBAAgB;YACxB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,+DAA+D,CAAC;SAClF,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,gBAAgB;YACxB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,mCAAmC,CAAC;SACtG,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,QAAQ;QACR,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,gBAAgB;YACxB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,2CAA2C,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;IAED,+CAA+C;IAC/C,cAAc;IACd,+CAA+C;IAC/C,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;KACjC,CAAC,CAAC;IACH,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC;KAC7B,CAAC,CAAC;IAEH,+CAA+C;IAC/C,SAAS;IACT,+CAA+C;IAC/C,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpB,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,2EAA2E,CAAC;SAC9F,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,+CAA+C,CAAC;SAClH,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,uDAAuD,CAAC;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,mCAAmC;IACnC,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC;KACrC,CAAC,CAAC;IACH,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC;KACpC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,0BAA0B;IAC1B,+CAA+C;IAC/C,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACpB,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,qDAAqD,CAAC;SACxE,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;SAC5F,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,IAAI,CAAC;YACb,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,iCAAiC,CAAC;SACpD,CAAC,CAAC;IACL,CAAC;IACD,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC;KACzC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,WAAW;IACX,+CAA+C;IAC/C,SAAS,CAAC,IAAI,CAAC;QACb,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,mCAAmC,CAAC;KACtD,CAAC,CAAC;IAEH,6BAA6B;IAC7B,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC7B,GAAG,GAAG;QACN,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;KAC7B,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAA+B;IAClE,+BAA+B;IAC/B,IAAI,MAAM,CAAC,UAAU,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC/D,OAAO,MAAM,CAAC,UAAqC,CAAC;IACtD,CAAC;IACD,IAAI,MAAM,CAAC,aAAa,CAAC,IAAI,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,QAAQ,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,aAAa,CAA4B,CAAC;IAC1D,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzD,OAAO,MAAM,CAAC,OAAkC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,37 @@
1
+ export interface MCPServerConfig {
2
+ command?: string;
3
+ args?: string[];
4
+ env?: Record<string, string>;
5
+ cwd?: string;
6
+ url?: string;
7
+ }
8
+ export interface MCPConfig {
9
+ mcpServers?: Record<string, MCPServerConfig>;
10
+ 'mcp.servers'?: Record<string, MCPServerConfig>;
11
+ servers?: Record<string, MCPServerConfig>;
12
+ }
13
+ export interface ConfigLocation {
14
+ client: string;
15
+ scope: 'global' | 'project';
16
+ path: string;
17
+ exists: boolean;
18
+ }
19
+ export interface ValidationResult {
20
+ level: 'error' | 'warning' | 'info';
21
+ code: string;
22
+ message: string;
23
+ file: string;
24
+ line?: number;
25
+ suggestion?: string;
26
+ }
27
+ export interface ServerTestResult {
28
+ name: string;
29
+ healthy: boolean;
30
+ responseTime?: number;
31
+ error?: string;
32
+ }
33
+ export interface CheckResults {
34
+ configsFound: ConfigLocation[];
35
+ validationResults: ValidationResult[];
36
+ serverResults: ServerTestResult[];
37
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { ValidationResult, ServerTestResult, ConfigLocation } from '../config/types.js';
2
+ export declare function printHeader(text: string): void;
3
+ export declare function printConfigsFound(configs: ConfigLocation[]): void;
4
+ export declare function printValidationResults(results: ValidationResult[]): void;
5
+ export declare function printServerResults(results: ServerTestResult[]): void;
6
+ export declare function printSummary(errors: number, warnings: number, healthyServers: number, totalServers: number): void;
@@ -0,0 +1,102 @@
1
+ import chalk from 'chalk';
2
+ export function printHeader(text) {
3
+ console.log(chalk.bold(`\n${text}\n`));
4
+ }
5
+ export function printConfigsFound(configs) {
6
+ const found = configs.filter((c) => c.exists);
7
+ const notFound = configs.filter((c) => !c.exists);
8
+ if (found.length === 0) {
9
+ console.log(chalk.yellow('No MCP configuration files found.\n'));
10
+ console.log(chalk.gray('Looked in:'));
11
+ notFound.slice(0, 5).forEach((c) => {
12
+ console.log(chalk.gray(` ${c.client}: ${c.path}`));
13
+ });
14
+ if (notFound.length > 5) {
15
+ console.log(chalk.gray(` ... and ${notFound.length - 5} more locations`));
16
+ }
17
+ return;
18
+ }
19
+ console.log(chalk.green(`Found ${found.length} config file${found.length > 1 ? 's' : ''}:\n`));
20
+ found.forEach((c) => {
21
+ const scope = c.scope === 'project' ? chalk.gray(' (project)') : '';
22
+ console.log(` ${chalk.cyan(c.client)}${scope}`);
23
+ console.log(chalk.gray(` ${c.path}\n`));
24
+ });
25
+ }
26
+ export function printValidationResults(results) {
27
+ const errors = results.filter((r) => r.level === 'error');
28
+ const warnings = results.filter((r) => r.level === 'warning');
29
+ if (errors.length > 0) {
30
+ console.log(chalk.red(`āŒ ${errors.length} Error${errors.length > 1 ? 's' : ''}\n`));
31
+ errors.forEach((e) => {
32
+ const location = e.line ? `:${e.line}` : '';
33
+ console.log(chalk.red(` ${shortPath(e.file)}${location}`));
34
+ console.log(chalk.red(` ${e.message}`));
35
+ if (e.suggestion) {
36
+ console.log(chalk.gray(` šŸ’” ${e.suggestion}`));
37
+ }
38
+ console.log('');
39
+ });
40
+ }
41
+ if (warnings.length > 0) {
42
+ console.log(chalk.yellow(`āš ļø ${warnings.length} Warning${warnings.length > 1 ? 's' : ''}\n`));
43
+ warnings.forEach((w) => {
44
+ const location = w.line ? `:${w.line}` : '';
45
+ console.log(chalk.yellow(` ${shortPath(w.file)}${location}`));
46
+ console.log(chalk.yellow(` ${w.message}`));
47
+ if (w.suggestion) {
48
+ console.log(chalk.gray(` šŸ’” ${w.suggestion}`));
49
+ }
50
+ console.log('');
51
+ });
52
+ }
53
+ }
54
+ export function printServerResults(results) {
55
+ if (results.length === 0) {
56
+ console.log(chalk.gray('No servers to test.\n'));
57
+ return;
58
+ }
59
+ const healthy = results.filter((s) => s.healthy);
60
+ const unhealthy = results.filter((s) => !s.healthy);
61
+ console.log(`šŸ”Œ Server Health: ${chalk.green(healthy.length)} healthy, ${unhealthy.length > 0 ? chalk.red(unhealthy.length + ' failed') : chalk.gray('0 failed')}\n`);
62
+ healthy.forEach((s) => {
63
+ const time = s.responseTime ? chalk.gray(` (${s.responseTime}ms)`) : '';
64
+ console.log(chalk.green(` āœ“ ${s.name}${time}`));
65
+ });
66
+ unhealthy.forEach((s) => {
67
+ console.log(chalk.red(` āœ— ${s.name}`));
68
+ if (s.error) {
69
+ console.log(chalk.gray(` ${s.error}`));
70
+ }
71
+ });
72
+ console.log('');
73
+ }
74
+ export function printSummary(errors, warnings, healthyServers, totalServers) {
75
+ console.log(chalk.gray('─'.repeat(50)));
76
+ if (errors === 0 && warnings === 0) {
77
+ console.log(chalk.green('\nāœ“ All configurations valid'));
78
+ }
79
+ else if (errors === 0) {
80
+ console.log(chalk.yellow(`\nāš ļø Configuration valid with ${warnings} warning${warnings > 1 ? 's' : ''}`));
81
+ }
82
+ else {
83
+ console.log(chalk.red(`\nāŒ Found ${errors} error${errors > 1 ? 's' : ''} that need${errors === 1 ? 's' : ''} fixing`));
84
+ }
85
+ if (totalServers > 0) {
86
+ if (healthyServers === totalServers) {
87
+ console.log(chalk.green(`āœ“ All ${totalServers} server${totalServers > 1 ? 's' : ''} responding`));
88
+ }
89
+ else {
90
+ console.log(chalk.yellow(`āš ļø ${healthyServers}/${totalServers} servers responding`));
91
+ }
92
+ }
93
+ console.log('');
94
+ }
95
+ function shortPath(path) {
96
+ const home = process.env.HOME || process.env.USERPROFILE || '';
97
+ if (home && path.startsWith(home)) {
98
+ return '~' + path.slice(home.length);
99
+ }
100
+ return path;
101
+ }
102
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { ValidationResult } from '../config/types.js';
2
+ export declare function validateEnvVars(config: Record<string, unknown>, configPath: string): ValidationResult[];
@@ -0,0 +1,70 @@
1
+ import { getServersFromConfig } from '../config/locations.js';
2
+ export function validateEnvVars(config, configPath) {
3
+ const results = [];
4
+ const servers = getServersFromConfig(config);
5
+ if (!servers) {
6
+ return results;
7
+ }
8
+ for (const [name, serverRaw] of Object.entries(servers)) {
9
+ const server = serverRaw;
10
+ if (!server.env)
11
+ continue;
12
+ for (const [key, value] of Object.entries(server.env)) {
13
+ if (typeof value !== 'string')
14
+ continue;
15
+ // Check for ${VAR} or $VAR references
16
+ const varRefs = value.match(/\$\{([A-Z_][A-Z0-9_]*)\}|\$([A-Z_][A-Z0-9_]*)/g) || [];
17
+ for (const ref of varRefs) {
18
+ const varName = ref.replace(/^\$\{?|\}?$/g, '');
19
+ if (!process.env[varName]) {
20
+ results.push({
21
+ level: 'warning',
22
+ code: 'ENV_VAR_MISSING',
23
+ message: `Server "${name}": Environment variable ${varName} is not set`,
24
+ file: configPath,
25
+ suggestion: `Set it with: export ${varName}="your-value"`,
26
+ });
27
+ }
28
+ }
29
+ // Check for empty values that look like they should be set
30
+ if (value === '' || value === '""' || value === "''") {
31
+ results.push({
32
+ level: 'warning',
33
+ code: 'ENV_VAR_EMPTY',
34
+ message: `Server "${name}": Environment variable ${key} is empty`,
35
+ file: configPath,
36
+ suggestion: 'This may cause authentication or configuration issues',
37
+ });
38
+ }
39
+ // Warn about hardcoded secrets
40
+ const secretKeyPatterns = [
41
+ /^(api[_-]?key|secret|token|password|pwd|auth|credential)/i,
42
+ /_(api[_-]?key|secret|token|password|pwd|auth|credential)$/i,
43
+ ];
44
+ const secretValuePatterns = [
45
+ /^sk[-_][a-zA-Z0-9]{20,}$/, // OpenAI-style keys
46
+ /^[a-f0-9]{32,}$/i, // Hex strings (32+ chars)
47
+ /^eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/, // JWT tokens
48
+ /^ghp_[a-zA-Z0-9]{36}$/, // GitHub tokens
49
+ /^github_pat_[a-zA-Z0-9_]{22,}/, // GitHub PAT
50
+ /^xoxb-[0-9]{10,}/, // Slack bot tokens
51
+ /^xoxp-[0-9]{10,}/, // Slack user tokens
52
+ /^AKIA[A-Z0-9]{16}$/, // AWS access keys
53
+ ];
54
+ const isSecretKey = secretKeyPatterns.some((p) => p.test(key));
55
+ const looksLikeSecret = secretValuePatterns.some((p) => p.test(value));
56
+ // Only warn if it's not a variable reference
57
+ if ((isSecretKey || looksLikeSecret) && !value.includes('$')) {
58
+ results.push({
59
+ level: 'warning',
60
+ code: 'HARDCODED_SECRET',
61
+ message: `Server "${name}": Possible hardcoded secret in ${key}`,
62
+ file: configPath,
63
+ suggestion: 'Use an environment variable reference like ${' + key.toUpperCase() + '} instead',
64
+ });
65
+ }
66
+ }
67
+ }
68
+ return results;
69
+ }
70
+ //# sourceMappingURL=env-vars.js.map