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.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/cli/commands/check.d.ts +2 -0
- package/dist/cli/commands/check.js +89 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/fix.d.ts +2 -0
- package/dist/cli/commands/fix.js +60 -0
- package/dist/cli/commands/fix.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +42 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/formatters/json.d.ts +2 -0
- package/dist/cli/formatters/json.js +4 -0
- package/dist/cli/formatters/json.js.map +1 -0
- package/dist/cli/formatters/markdown.d.ts +2 -0
- package/dist/cli/formatters/markdown.js +44 -0
- package/dist/cli/formatters/markdown.js.map +1 -0
- package/dist/cli/formatters/terminal.d.ts +2 -0
- package/dist/cli/formatters/terminal.js +48 -0
- package/dist/cli/formatters/terminal.js.map +1 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/config.js +38 -0
- package/dist/config/config.js.map +1 -0
- package/dist/core/diagnostic.d.ts +15 -0
- package/dist/core/diagnostic.js +2 -0
- package/dist/core/diagnostic.js.map +1 -0
- package/dist/core/engine.d.ts +8 -0
- package/dist/core/engine.js +35 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/fixer.d.ts +3 -0
- package/dist/core/fixer.js +37 -0
- package/dist/core/fixer.js.map +1 -0
- package/dist/core/rule.d.ts +52 -0
- package/dist/core/rule.js +2 -0
- package/dist/core/rule.js.map +1 -0
- package/dist/core/severity.d.ts +1 -0
- package/dist/core/severity.js +2 -0
- package/dist/core/severity.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/file-loader.d.ts +2 -0
- package/dist/loaders/file-loader.js +30 -0
- package/dist/loaders/file-loader.js.map +1 -0
- package/dist/loaders/sse-loader.d.ts +2 -0
- package/dist/loaders/sse-loader.js +19 -0
- package/dist/loaders/sse-loader.js.map +1 -0
- package/dist/loaders/stdio-loader.d.ts +2 -0
- package/dist/loaders/stdio-loader.js +19 -0
- package/dist/loaders/stdio-loader.js.map +1 -0
- package/dist/rules/client-specific/claude.d.ts +2 -0
- package/dist/rules/client-specific/claude.js +30 -0
- package/dist/rules/client-specific/claude.js.map +1 -0
- package/dist/rules/client-specific/cursor.d.ts +2 -0
- package/dist/rules/client-specific/cursor.js +49 -0
- package/dist/rules/client-specific/cursor.js.map +1 -0
- package/dist/rules/client-specific/gemini.d.ts +3 -0
- package/dist/rules/client-specific/gemini.js +60 -0
- package/dist/rules/client-specific/gemini.js.map +1 -0
- package/dist/rules/client-specific/vscode.d.ts +2 -0
- package/dist/rules/client-specific/vscode.js +22 -0
- package/dist/rules/client-specific/vscode.js.map +1 -0
- package/dist/rules/description-exists.d.ts +2 -0
- package/dist/rules/description-exists.js +35 -0
- package/dist/rules/description-exists.js.map +1 -0
- package/dist/rules/index.d.ts +2 -0
- package/dist/rules/index.js +28 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/max-depth.d.ts +2 -0
- package/dist/rules/max-depth.js +59 -0
- package/dist/rules/max-depth.js.map +1 -0
- package/dist/rules/no-content-encoding.d.ts +2 -0
- package/dist/rules/no-content-encoding.js +33 -0
- package/dist/rules/no-content-encoding.js.map +1 -0
- package/dist/rules/no-empty-enum.d.ts +2 -0
- package/dist/rules/no-empty-enum.js +33 -0
- package/dist/rules/no-empty-enum.js.map +1 -0
- package/dist/rules/no-recursive-refs.d.ts +2 -0
- package/dist/rules/no-recursive-refs.js +68 -0
- package/dist/rules/no-recursive-refs.js.map +1 -0
- package/dist/rules/no-required-false.d.ts +2 -0
- package/dist/rules/no-required-false.js +33 -0
- package/dist/rules/no-required-false.js.map +1 -0
- package/dist/rules/no-unsupported-formats.d.ts +2 -0
- package/dist/rules/no-unsupported-formats.js +39 -0
- package/dist/rules/no-unsupported-formats.js.map +1 -0
- package/dist/rules/valid-json-schema-subset.d.ts +2 -0
- package/dist/rules/valid-json-schema-subset.js +43 -0
- package/dist/rules/valid-json-schema-subset.js.map +1 -0
- package/dist/utils/schema-walker.d.ts +3 -0
- package/dist/utils/schema-walker.js +52 -0
- package/dist/utils/schema-walker.js.map +1 -0
- 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
|
+
[](https://www.npmjs.com/package/mcp-lint)
|
|
4
|
+
[](https://www.npmjs.com/package/mcp-lint)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](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,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,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,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 @@
|
|
|
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,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,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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"}
|