mcp-lint 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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/cli/commands/check.d.ts +2 -0
  4. package/dist/cli/commands/check.js +89 -0
  5. package/dist/cli/commands/check.js.map +1 -0
  6. package/dist/cli/commands/fix.d.ts +2 -0
  7. package/dist/cli/commands/fix.js +60 -0
  8. package/dist/cli/commands/fix.js.map +1 -0
  9. package/dist/cli/commands/init.d.ts +2 -0
  10. package/dist/cli/commands/init.js +42 -0
  11. package/dist/cli/commands/init.js.map +1 -0
  12. package/dist/cli/formatters/json.d.ts +2 -0
  13. package/dist/cli/formatters/json.js +4 -0
  14. package/dist/cli/formatters/json.js.map +1 -0
  15. package/dist/cli/formatters/markdown.d.ts +2 -0
  16. package/dist/cli/formatters/markdown.js +44 -0
  17. package/dist/cli/formatters/markdown.js.map +1 -0
  18. package/dist/cli/formatters/terminal.d.ts +2 -0
  19. package/dist/cli/formatters/terminal.js +48 -0
  20. package/dist/cli/formatters/terminal.js.map +1 -0
  21. package/dist/config/config.d.ts +3 -0
  22. package/dist/config/config.js +38 -0
  23. package/dist/config/config.js.map +1 -0
  24. package/dist/core/diagnostic.d.ts +15 -0
  25. package/dist/core/diagnostic.js +2 -0
  26. package/dist/core/diagnostic.js.map +1 -0
  27. package/dist/core/engine.d.ts +8 -0
  28. package/dist/core/engine.js +35 -0
  29. package/dist/core/engine.js.map +1 -0
  30. package/dist/core/fixer.d.ts +3 -0
  31. package/dist/core/fixer.js +37 -0
  32. package/dist/core/fixer.js.map +1 -0
  33. package/dist/core/rule.d.ts +52 -0
  34. package/dist/core/rule.js +2 -0
  35. package/dist/core/rule.js.map +1 -0
  36. package/dist/core/severity.d.ts +1 -0
  37. package/dist/core/severity.js +2 -0
  38. package/dist/core/severity.js.map +1 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +15 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/loaders/file-loader.d.ts +2 -0
  43. package/dist/loaders/file-loader.js +30 -0
  44. package/dist/loaders/file-loader.js.map +1 -0
  45. package/dist/loaders/sse-loader.d.ts +2 -0
  46. package/dist/loaders/sse-loader.js +19 -0
  47. package/dist/loaders/sse-loader.js.map +1 -0
  48. package/dist/loaders/stdio-loader.d.ts +2 -0
  49. package/dist/loaders/stdio-loader.js +19 -0
  50. package/dist/loaders/stdio-loader.js.map +1 -0
  51. package/dist/rules/client-specific/claude.d.ts +2 -0
  52. package/dist/rules/client-specific/claude.js +30 -0
  53. package/dist/rules/client-specific/claude.js.map +1 -0
  54. package/dist/rules/client-specific/cursor.d.ts +2 -0
  55. package/dist/rules/client-specific/cursor.js +49 -0
  56. package/dist/rules/client-specific/cursor.js.map +1 -0
  57. package/dist/rules/client-specific/gemini.d.ts +3 -0
  58. package/dist/rules/client-specific/gemini.js +60 -0
  59. package/dist/rules/client-specific/gemini.js.map +1 -0
  60. package/dist/rules/client-specific/vscode.d.ts +2 -0
  61. package/dist/rules/client-specific/vscode.js +22 -0
  62. package/dist/rules/client-specific/vscode.js.map +1 -0
  63. package/dist/rules/description-exists.d.ts +2 -0
  64. package/dist/rules/description-exists.js +35 -0
  65. package/dist/rules/description-exists.js.map +1 -0
  66. package/dist/rules/index.d.ts +2 -0
  67. package/dist/rules/index.js +28 -0
  68. package/dist/rules/index.js.map +1 -0
  69. package/dist/rules/max-depth.d.ts +2 -0
  70. package/dist/rules/max-depth.js +59 -0
  71. package/dist/rules/max-depth.js.map +1 -0
  72. package/dist/rules/no-content-encoding.d.ts +2 -0
  73. package/dist/rules/no-content-encoding.js +33 -0
  74. package/dist/rules/no-content-encoding.js.map +1 -0
  75. package/dist/rules/no-empty-enum.d.ts +2 -0
  76. package/dist/rules/no-empty-enum.js +33 -0
  77. package/dist/rules/no-empty-enum.js.map +1 -0
  78. package/dist/rules/no-recursive-refs.d.ts +2 -0
  79. package/dist/rules/no-recursive-refs.js +68 -0
  80. package/dist/rules/no-recursive-refs.js.map +1 -0
  81. package/dist/rules/no-required-false.d.ts +2 -0
  82. package/dist/rules/no-required-false.js +33 -0
  83. package/dist/rules/no-required-false.js.map +1 -0
  84. package/dist/rules/no-unsupported-formats.d.ts +2 -0
  85. package/dist/rules/no-unsupported-formats.js +39 -0
  86. package/dist/rules/no-unsupported-formats.js.map +1 -0
  87. package/dist/rules/valid-json-schema-subset.d.ts +2 -0
  88. package/dist/rules/valid-json-schema-subset.js +43 -0
  89. package/dist/rules/valid-json-schema-subset.js.map +1 -0
  90. package/dist/utils/schema-walker.d.ts +3 -0
  91. package/dist/utils/schema-walker.js +52 -0
  92. package/dist/utils/schema-walker.js.map +1 -0
  93. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 robert19001-cmyk
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,283 @@
1
+ # mcp-lint
2
+
3
+ [![npm version](https://img.shields.io/npm/v/mcp-lint?color=crimson)](https://www.npmjs.com/package/mcp-lint)
4
+ [![npm downloads](https://img.shields.io/npm/dm/mcp-lint)](https://www.npmjs.com/package/mcp-lint)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Node.js ≥18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
+
8
+ **Lint MCP server tool schemas for cross-client compatibility.**
9
+
10
+ MCP servers expose tool schemas consumed by **Claude**, **Cursor**, **Gemini**, and **VS Code Copilot**. Each client has different JSON Schema support and quirks — a schema that works in Claude may silently break in Cursor. `mcp-lint` catches these issues before your users do.
11
+
12
+ ```
13
+ $ npx mcp-lint check tools.json
14
+
15
+ search-tool
16
+ ✖ Schema node at "inputSchema.properties.query" has `required: false` which is not valid JSON Schema.
17
+ inputSchema.properties.query [no-required-false]
18
+ ⚠ Parameter "limit" in tool "search-tool" is missing a description.
19
+ inputSchema.properties.limit [description-exists]
20
+
21
+ 1 error, 1 warning
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ # One-time use (no install)
30
+ npx mcp-lint check tools.json
31
+
32
+ # Global
33
+ npm install -g mcp-lint
34
+
35
+ # Per-project dev dependency
36
+ npm install --save-dev mcp-lint
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```bash
44
+ # Generate a config file
45
+ mcp-lint init
46
+
47
+ # Check a JSON or YAML file
48
+ mcp-lint check tools.json
49
+ mcp-lint check tools.yaml
50
+
51
+ # Check a live MCP server (stdio)
52
+ mcp-lint check --server stdio -- node my-server.js
53
+
54
+ # Check a live MCP server (SSE)
55
+ mcp-lint check --server sse --url http://localhost:3000/sse
56
+
57
+ # Auto-fix what can be fixed
58
+ mcp-lint fix tools.json --in-place
59
+ ```
60
+
61
+ ---
62
+
63
+ ## CLI Reference
64
+
65
+ ### `mcp-lint check [input]`
66
+
67
+ ```bash
68
+ mcp-lint check tools.json
69
+
70
+ # Output formats
71
+ mcp-lint check tools.json --format terminal # default, colored output
72
+ mcp-lint check tools.json --format json # machine-readable (CI/CD)
73
+ mcp-lint check tools.json --format markdown # for PRs and GitHub summaries
74
+
75
+ # Filters
76
+ mcp-lint check tools.json --clients claude,cursor # only these clients' rules
77
+ mcp-lint check tools.json --severity error # errors only
78
+ mcp-lint check tools.json --quiet # same as --severity error
79
+ mcp-lint check tools.json --ignore "debug-tool,internal-tool"
80
+
81
+ # Config
82
+ mcp-lint check tools.json --config path/to/.mcplintrc.json
83
+ mcp-lint check tools.json --no-color
84
+
85
+ # Live servers
86
+ mcp-lint check --server stdio -- node my-server.js
87
+ mcp-lint check --server stdio -- python my_server.py arg1 arg2
88
+ mcp-lint check --server sse --url http://localhost:3000/sse
89
+ ```
90
+
91
+ **Exit codes:**
92
+
93
+ | Code | Meaning |
94
+ |------|---------|
95
+ | `0` | No errors (warnings don't count) |
96
+ | `1` | One or more errors found |
97
+ | `2` | Invalid input or configuration error |
98
+
99
+ ---
100
+
101
+ ### `mcp-lint fix [input]`
102
+
103
+ Auto-fixes issues that are safe to fix automatically (no semantic changes).
104
+
105
+ ```bash
106
+ mcp-lint fix tools.json # fixed output to stdout
107
+ mcp-lint fix tools.json --output fixed-tools.json # write to file
108
+ mcp-lint fix tools.json --in-place # overwrite original
109
+ mcp-lint fix tools.json --dry-run # show what would be fixed
110
+ mcp-lint fix tools.json --rules no-required-false,no-empty-enum # specific rules only
111
+ ```
112
+
113
+ ---
114
+
115
+ ### `mcp-lint init`
116
+
117
+ Creates a default `.mcplintrc.json` in the current directory.
118
+
119
+ ```bash
120
+ mcp-lint init # fails if file already exists
121
+ mcp-lint init --force # overwrite existing
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Rules
127
+
128
+ ### Universal (all clients)
129
+
130
+ | Rule | Severity | Fixable | Description |
131
+ |------|----------|---------|-------------|
132
+ | `no-required-false` | 🔴 error | ✅ | `required: false` on properties is not valid JSON Schema |
133
+ | `no-content-encoding` | 🔴 error | ✅ | `contentEncoding` is not supported by MCP clients |
134
+ | `no-empty-enum` | 🔴 error | ✅ | Empty `enum: []` makes a parameter impossible to satisfy |
135
+ | `no-recursive-refs` | 🔴 error | ❌ | Circular `$ref` causes crashes in most clients |
136
+ | `valid-json-schema-subset` | 🔴 error | ❌ | Unsupported keywords: `oneOf`, `anyOf`, `allOf`, `if/then/else`, `not`, `patternProperties`… |
137
+ | `description-exists` | 🟡 warning | ❌ | Tools and parameters should have descriptions |
138
+ | `max-depth` | 🟡 warning | ❌ | Schema nesting deeper than 5 levels hurts LLM comprehension |
139
+ | `no-unsupported-formats` | 🟡 warning | ✅ | Obscure `format` values (`iri`, `json-pointer`, `regex`…) not widely supported |
140
+
141
+ ### Claude-specific
142
+
143
+ | Rule | Severity | Fixable | Description |
144
+ |------|----------|---------|-------------|
145
+ | `claude/no-type-array` | 🟡 warning | ✅ | `"type": ["string", "null"]` array syntax may not work correctly in Claude |
146
+
147
+ ### Cursor-specific
148
+
149
+ | Rule | Severity | Fixable | Description |
150
+ |------|----------|---------|-------------|
151
+ | `cursor/no-default-without-type` | 🔴 error | ✅ | Cursor requires explicit `type` when a `default` value is present |
152
+
153
+ ### Gemini-specific
154
+
155
+ | Rule | Severity | Fixable | Description |
156
+ |------|----------|---------|-------------|
157
+ | `gemini/no-optional-without-default` | 🟡 warning | ❌ | Gemini handles optional params better when `default` is explicit |
158
+ | `gemini/no-nested-objects` | 🟡 warning | ❌ | Gemini has limited support for object properties nested more than 2 levels |
159
+
160
+ ### VS Code Copilot-specific
161
+
162
+ | Rule | Severity | Fixable | Description |
163
+ |------|----------|---------|-------------|
164
+ | `vscode/max-params` | 🟡 warning | ❌ | VS Code Copilot performance degrades with more than 15 parameters |
165
+
166
+ ---
167
+
168
+ ## Configuration
169
+
170
+ `mcp-lint` auto-discovers `.mcplintrc.json` starting from the current directory up to your home folder. Override with `--config`.
171
+
172
+ ```json
173
+ {
174
+ "$schema": "https://raw.githubusercontent.com/robert19001-cmyk/mcp-lint/main/config-schema.json",
175
+ "rules": {
176
+ "no-required-false": "error",
177
+ "description-exists": "warning",
178
+ "max-depth": "off",
179
+ "claude/no-type-array": "error"
180
+ },
181
+ "clients": ["claude", "cursor", "gemini", "vscode"],
182
+ "ignore": ["internal-debug-tool"],
183
+ "maxDepth": 5
184
+ }
185
+ ```
186
+
187
+ Rule severities: `"error"` | `"warning"` | `"info"` | `"off"`
188
+
189
+ ---
190
+
191
+ ## CI/CD — GitHub Actions
192
+
193
+ ```yaml
194
+ name: MCP Schema Lint
195
+ on: [push, pull_request]
196
+
197
+ jobs:
198
+ lint:
199
+ runs-on: ubuntu-latest
200
+ steps:
201
+ - uses: actions/checkout@v4
202
+ - uses: actions/setup-node@v4
203
+ with: { node-version: 20 }
204
+
205
+ # Fail the build on errors
206
+ - run: npx mcp-lint check ./src/tools.json
207
+
208
+ # Post a summary to the PR
209
+ - run: npx mcp-lint check ./src/tools.json --format markdown >> $GITHUB_STEP_SUMMARY
210
+ if: always()
211
+
212
+ # Save JSON results as artifact
213
+ - run: npx mcp-lint check ./src/tools.json --format json > lint-results.json || true
214
+ - uses: actions/upload-artifact@v4
215
+ if: always()
216
+ with:
217
+ name: mcp-lint-results
218
+ path: lint-results.json
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Programmatic API
224
+
225
+ ```typescript
226
+ import { LintEngine } from 'mcp-lint';
227
+ import { allRules } from 'mcp-lint/rules';
228
+ import { loadFile } from 'mcp-lint/loaders';
229
+ import { applyFixes } from 'mcp-lint/fixer';
230
+
231
+ // Check
232
+ const tools = await loadFile('tools.json');
233
+ const engine = new LintEngine(allRules, {
234
+ clients: ['claude', 'cursor'],
235
+ ignore: ['debug-tool'],
236
+ });
237
+ const diagnostics = engine.lint(tools);
238
+
239
+ // Fix
240
+ const fixed = applyFixes(tools, diagnostics);
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Adding a Custom Rule
246
+
247
+ Create `src/rules/my-rule.ts`:
248
+
249
+ ```typescript
250
+ import type { Rule } from 'mcp-lint/core/rule';
251
+
252
+ export const myRule: Rule = {
253
+ id: 'my-rule',
254
+ severity: 'warning',
255
+ description: 'What this rule checks',
256
+ clients: ['claude', 'cursor', 'gemini', 'vscode'],
257
+
258
+ check(tool, context) {
259
+ const diagnostics = [];
260
+
261
+ if (someCondition(tool.inputSchema)) {
262
+ diagnostics.push({
263
+ ruleId: 'my-rule',
264
+ severity: 'warning',
265
+ message: 'Human-readable explanation',
266
+ toolName: tool.name,
267
+ path: 'inputSchema.properties.something',
268
+ clients: this.clients,
269
+ });
270
+ }
271
+
272
+ return diagnostics;
273
+ },
274
+ };
275
+ ```
276
+
277
+ Register it in `src/rules/index.ts` and add tests in `tests/rules/my-rule.test.ts`.
278
+
279
+ ---
280
+
281
+ ## License
282
+
283
+ MIT © Robert
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCheckCommand(program: Command): void;
@@ -0,0 +1,89 @@
1
+ import { loadFile } from '../../loaders/file-loader.js';
2
+ import { loadStdio } from '../../loaders/stdio-loader.js';
3
+ import { loadSSE } from '../../loaders/sse-loader.js';
4
+ import { LintEngine } from '../../core/engine.js';
5
+ import { allRules } from '../../rules/index.js';
6
+ import { formatTerminal } from '../formatters/terminal.js';
7
+ import { formatJson } from '../formatters/json.js';
8
+ import { formatMarkdown } from '../formatters/markdown.js';
9
+ import { loadConfig } from '../../config/config.js';
10
+ const SEVERITY_ORDER = { error: 3, warning: 2, info: 1 };
11
+ function filterBySeverity(diagnostics, min) {
12
+ return diagnostics.filter((d) => SEVERITY_ORDER[d.severity] >= SEVERITY_ORDER[min]);
13
+ }
14
+ export function registerCheckCommand(program) {
15
+ program
16
+ .command('check [input]')
17
+ .description('Check MCP tool schemas for compatibility issues')
18
+ .option('--format <format>', 'Output format: terminal|json|markdown', 'terminal')
19
+ .option('--no-color', 'Disable terminal colors')
20
+ .option('--config <path>', 'Path to .mcplintrc.json config file')
21
+ .option('--severity <level>', 'Minimum severity to report: error|warning|info')
22
+ .option('--clients <clients>', 'Comma-separated client filter: claude,cursor,gemini,vscode')
23
+ .option('--ignore <tools>', 'Comma-separated tool names to skip')
24
+ .option('--quiet', 'Only output errors (no warnings/info)')
25
+ .option('--server <type>', 'Server transport: stdio|sse')
26
+ .option('--url <url>', 'SSE server URL (use with --server sse)')
27
+ .allowExcessArguments(true)
28
+ .action(async (input, options) => {
29
+ try {
30
+ const fileConfig = await loadConfig(options.config);
31
+ const cliClients = options.clients
32
+ ? options.clients.split(',').map((s) => s.trim())
33
+ : undefined;
34
+ const config = {
35
+ ...fileConfig,
36
+ ...(cliClients ? { clients: cliClients } : {}),
37
+ ignore: [
38
+ ...(fileConfig.ignore ?? []),
39
+ ...(options.ignore ? options.ignore.split(',').map((s) => s.trim()) : []),
40
+ ],
41
+ };
42
+ let tools;
43
+ if (options.server === 'stdio') {
44
+ // Get everything after '--' in process.argv
45
+ const rawArgv = process.argv;
46
+ const sepIdx = rawArgv.indexOf('--');
47
+ if (sepIdx === -1 || sepIdx >= rawArgv.length - 1) {
48
+ throw new Error('--server stdio requires a command after --, e.g.: mcp-lint check --server stdio -- node server.js');
49
+ }
50
+ const [cmd, ...cmdArgs] = rawArgv.slice(sepIdx + 1);
51
+ tools = await loadStdio(cmd, cmdArgs);
52
+ }
53
+ else if (options.server === 'sse') {
54
+ if (!options.url)
55
+ throw new Error('--server sse requires --url <url>');
56
+ tools = await loadSSE(options.url);
57
+ }
58
+ else {
59
+ if (!input)
60
+ throw new Error('Specify an input file or use --server stdio|sse');
61
+ tools = await loadFile(input);
62
+ }
63
+ const engine = new LintEngine(allRules, config);
64
+ let diagnostics = engine.lint(tools);
65
+ const minSeverity = options.quiet
66
+ ? 'error'
67
+ : options.severity ?? 'info';
68
+ diagnostics = filterBySeverity(diagnostics, minSeverity);
69
+ let output;
70
+ if (options.format === 'json') {
71
+ output = formatJson(diagnostics);
72
+ }
73
+ else if (options.format === 'markdown') {
74
+ output = formatMarkdown(diagnostics);
75
+ }
76
+ else {
77
+ output = formatTerminal(diagnostics, !options.color);
78
+ }
79
+ process.stdout.write(output);
80
+ const hasErrors = diagnostics.some((d) => d.severity === 'error');
81
+ process.exit(hasErrors ? 1 : 0);
82
+ }
83
+ catch (err) {
84
+ process.stderr.write(`Error: ${err.message}\n`);
85
+ process.exit(2);
86
+ }
87
+ });
88
+ }
89
+ //# 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
+ import type { Command } from 'commander';
2
+ export declare function registerFixCommand(program: Command): void;
@@ -0,0 +1,60 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { loadFile } from '../../loaders/file-loader.js';
3
+ import { LintEngine } from '../../core/engine.js';
4
+ import { allRules } from '../../rules/index.js';
5
+ import { applyFixes } from '../../core/fixer.js';
6
+ import { loadConfig } from '../../config/config.js';
7
+ export function registerFixCommand(program) {
8
+ program
9
+ .command('fix <input>')
10
+ .description('Auto-fix MCP tool schema issues and write the result')
11
+ .option('--output <path>', 'Write fixed output to file instead of stdout')
12
+ .option('--in-place', 'Overwrite the input file')
13
+ .option('--dry-run', 'Show what would be fixed without applying changes')
14
+ .option('--rules <rules>', 'Comma-separated list of rule IDs to apply fixes for')
15
+ .option('--config <path>', 'Path to .mcplintrc.json config file')
16
+ .action(async (input, options) => {
17
+ try {
18
+ const fileConfig = await loadConfig(options.config);
19
+ const tools = await loadFile(input);
20
+ const engine = new LintEngine(allRules, fileConfig);
21
+ const diagnostics = engine.lint(tools);
22
+ const onlyRules = options.rules
23
+ ? options.rules.split(',').map((r) => r.trim())
24
+ : undefined;
25
+ const fixable = diagnostics.filter((d) => d.fix != null && (!onlyRules || onlyRules.includes(d.ruleId)));
26
+ if (options.dryRun) {
27
+ if (fixable.length === 0) {
28
+ process.stdout.write('No fixable issues found.\n');
29
+ }
30
+ else {
31
+ process.stdout.write(`Would fix ${fixable.length} issue(s):\n`);
32
+ for (const d of fixable) {
33
+ process.stdout.write(` [${d.ruleId}] ${d.path}: ${d.fix.description}\n`);
34
+ }
35
+ }
36
+ process.exit(0);
37
+ return;
38
+ }
39
+ const fixed = applyFixes(tools, diagnostics, onlyRules);
40
+ const output = JSON.stringify(fixed, null, 2) + '\n';
41
+ if (options.inPlace) {
42
+ await writeFile(input, output, 'utf-8');
43
+ process.stdout.write(`Fixed ${fixable.length} issue(s) in ${input}\n`);
44
+ }
45
+ else if (options.output) {
46
+ await writeFile(options.output, output, 'utf-8');
47
+ process.stdout.write(`Fixed ${fixable.length} issue(s). Output written to ${options.output}\n`);
48
+ }
49
+ else {
50
+ process.stdout.write(output);
51
+ }
52
+ process.exit(0);
53
+ }
54
+ catch (err) {
55
+ process.stderr.write(`Error: ${err.message}\n`);
56
+ process.exit(2);
57
+ }
58
+ });
59
+ }
60
+ //# sourceMappingURL=fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix.js","sourceRoot":"","sources":["../../../src/cli/commands/fix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerInitCommand(program: Command): void;
@@ -0,0 +1,42 @@
1
+ import { writeFile, access } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ const DEFAULT_CONFIG = {
4
+ $schema: 'https://raw.githubusercontent.com/robertmcp/mcp-lint/main/config-schema.json',
5
+ rules: {
6
+ 'no-required-false': 'error',
7
+ 'no-content-encoding': 'error',
8
+ 'description-exists': 'warning',
9
+ 'no-empty-enum': 'error',
10
+ 'max-depth': 'warning',
11
+ 'no-recursive-refs': 'error',
12
+ 'valid-json-schema-subset': 'error',
13
+ 'no-unsupported-formats': 'warning',
14
+ },
15
+ clients: ['claude', 'cursor', 'gemini', 'vscode'],
16
+ ignore: [],
17
+ maxDepth: 5,
18
+ };
19
+ export function registerInitCommand(program) {
20
+ program
21
+ .command('init')
22
+ .description('Generate a default .mcplintrc.json in the current directory')
23
+ .option('--force', 'Overwrite existing config file')
24
+ .action(async (options) => {
25
+ const configPath = resolve(process.cwd(), '.mcplintrc.json');
26
+ if (!options.force) {
27
+ try {
28
+ await access(configPath);
29
+ process.stderr.write(`.mcplintrc.json already exists. Use --force to overwrite.\n`);
30
+ process.exit(2);
31
+ return;
32
+ }
33
+ catch {
34
+ // file doesn't exist, proceed
35
+ }
36
+ }
37
+ await writeFile(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n', 'utf-8');
38
+ process.stdout.write(`Created .mcplintrc.json\n`);
39
+ process.exit(0);
40
+ });
41
+ }
42
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,cAAc,GAAG;IACrB,OAAO,EAAE,8EAA8E;IACvF,KAAK,EAAE;QACL,mBAAmB,EAAE,OAAO;QAC5B,qBAAqB,EAAE,OAAO;QAC9B,oBAAoB,EAAE,SAAS;QAC/B,eAAe,EAAE,OAAO;QACxB,WAAW,EAAE,SAAS;QACtB,mBAAmB,EAAE,OAAO;QAC5B,0BAA0B,EAAE,OAAO;QACnC,wBAAwB,EAAE,SAAS;KACpC;IACD,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;IACjD,MAAM,EAAE,EAAE;IACV,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,SAAS,EAAE,gCAAgC,CAAC;SACnD,MAAM,CAAC,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAE7D,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6DAA6D,CAC9D,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,8BAA8B;YAChC,CAAC;QACH,CAAC;QAED,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Diagnostic } from '../../core/diagnostic.js';
2
+ export declare function formatJson(diagnostics: Diagnostic[]): string;
@@ -0,0 +1,4 @@
1
+ export function formatJson(diagnostics) {
2
+ return JSON.stringify(diagnostics, null, 2) + '\n';
3
+ }
4
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/cli/formatters/json.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,WAAyB;IAClD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AACrD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Diagnostic } from '../../core/diagnostic.js';
2
+ export declare function formatMarkdown(diagnostics: Diagnostic[]): string;
@@ -0,0 +1,44 @@
1
+ const SEVERITY_BADGE = {
2
+ error: '🔴 error',
3
+ warning: '🟡 warning',
4
+ info: '🔵 info',
5
+ };
6
+ export function formatMarkdown(diagnostics) {
7
+ const lines = ['# MCP Lint Report', ''];
8
+ const errors = diagnostics.filter((d) => d.severity === 'error').length;
9
+ const warnings = diagnostics.filter((d) => d.severity === 'warning').length;
10
+ const infos = diagnostics.filter((d) => d.severity === 'info').length;
11
+ if (diagnostics.length === 0) {
12
+ lines.push('✅ **No issues found.**', '');
13
+ return lines.join('\n');
14
+ }
15
+ lines.push('## Summary', '');
16
+ const summaryParts = [];
17
+ if (errors > 0)
18
+ summaryParts.push(`**${errors} error${errors !== 1 ? 's' : ''}**`);
19
+ if (warnings > 0)
20
+ summaryParts.push(`**${warnings} warning${warnings !== 1 ? 's' : ''}**`);
21
+ if (infos > 0)
22
+ summaryParts.push(`**${infos} info**`);
23
+ lines.push(summaryParts.join(', '), '');
24
+ const byTool = new Map();
25
+ for (const d of diagnostics) {
26
+ const list = byTool.get(d.toolName) ?? [];
27
+ list.push(d);
28
+ byTool.set(d.toolName, list);
29
+ }
30
+ lines.push('## Issues', '');
31
+ for (const [toolName, toolDiags] of byTool) {
32
+ lines.push(`### \`${toolName}\``, '');
33
+ lines.push('| Severity | Rule | Path | Message |');
34
+ lines.push('|----------|------|------|---------|');
35
+ for (const d of toolDiags) {
36
+ const badge = SEVERITY_BADGE[d.severity] ?? d.severity;
37
+ const msg = d.message.replace(/\|/g, '\\|');
38
+ lines.push(`| ${badge} | \`${d.ruleId}\` | \`${d.path}\` | ${msg} |`);
39
+ }
40
+ lines.push('');
41
+ }
42
+ return lines.join('\n');
43
+ }
44
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/cli/formatters/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Diagnostic } from '../../core/diagnostic.js';
2
+ export declare function formatTerminal(diagnostics: Diagnostic[], noColor?: boolean): string;
@@ -0,0 +1,48 @@
1
+ import chalk from 'chalk';
2
+ const ICONS = { error: '✖', warning: '⚠', info: 'ℹ' };
3
+ export function formatTerminal(diagnostics, noColor = false) {
4
+ if (noColor)
5
+ chalk.level = 0;
6
+ if (diagnostics.length === 0) {
7
+ return chalk.green('✔ No issues found.\n');
8
+ }
9
+ const byTool = new Map();
10
+ for (const d of diagnostics) {
11
+ const list = byTool.get(d.toolName) ?? [];
12
+ list.push(d);
13
+ byTool.set(d.toolName, list);
14
+ }
15
+ const lines = [''];
16
+ for (const [toolName, toolDiags] of byTool) {
17
+ lines.push(chalk.bold(toolName));
18
+ for (const d of toolDiags) {
19
+ const icon = d.severity === 'error'
20
+ ? chalk.red(ICONS.error)
21
+ : d.severity === 'warning'
22
+ ? chalk.yellow(ICONS.warning)
23
+ : chalk.blue(ICONS.info);
24
+ const msgColor = d.severity === 'error'
25
+ ? chalk.red
26
+ : d.severity === 'warning'
27
+ ? chalk.yellow
28
+ : chalk.blue;
29
+ lines.push(` ${icon} ${msgColor(d.message)}`);
30
+ lines.push(` ${chalk.gray(d.path)} ${chalk.dim(`[${d.ruleId}]`)}`);
31
+ }
32
+ lines.push('');
33
+ }
34
+ const errors = diagnostics.filter(d => d.severity === 'error').length;
35
+ const warnings = diagnostics.filter(d => d.severity === 'warning').length;
36
+ const infos = diagnostics.filter(d => d.severity === 'info').length;
37
+ const summary = [];
38
+ if (errors > 0)
39
+ summary.push(chalk.red(`${errors} error${errors !== 1 ? 's' : ''}`));
40
+ if (warnings > 0)
41
+ summary.push(chalk.yellow(`${warnings} warning${warnings !== 1 ? 's' : ''}`));
42
+ if (infos > 0)
43
+ summary.push(chalk.blue(`${infos} info`));
44
+ lines.push(summary.join(', '));
45
+ lines.push('');
46
+ return lines.join('\n');
47
+ }
48
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../../src/cli/formatters/terminal.ts"],"names":[],"mappings":"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"}