mcp-lint 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/catalog/lint.d.ts +12 -0
- package/dist/catalog/lint.js +113 -0
- package/dist/catalog/lint.js.map +1 -0
- package/dist/catalog/list.d.ts +6 -0
- package/dist/catalog/list.js.map +1 -1
- package/dist/cli/commands/catalog.d.ts +3 -0
- package/dist/cli/commands/catalog.js +17 -6
- package/dist/cli/commands/catalog.js.map +1 -1
- package/dist/rules/catalog.d.ts +9 -0
- package/dist/rules/catalog.js +33 -0
- package/dist/rules/catalog.js.map +1 -0
- package/dist/rules/index.js +2 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/examples/github-action.yml +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Proof page, with a live bench: [mcp-lint-alpha.vercel.app](https://mcp-lint-alph
|
|
|
13
13
|
|
|
14
14
|
MCP servers expose tool schemas consumed by **Claude**, **Cursor**, **Gemini**, **VS Code Copilot**, **Windsurf**, **Cline**, **OpenAI Agents SDK**, and **Continue.dev**. Each client has different JSON Schema support and quirks — a schema that works in Claude may silently break in Cursor or OpenAI. `mcp-lint` catches these issues before your users do.
|
|
15
15
|
|
|
16
|
-
This repository is **1.0.
|
|
16
|
+
This repository is **1.0.4**. Install it with `npx mcp-lint@1.0.4`. npm `1.0.3` is the previous registry release. npm `1.0.1` and npm `1.0.2` were not published. Design notes: [PRODUCT.md](PRODUCT.md), [ROADMAP.md](ROADMAP.md), [ARCHITECTURE.md](ARCHITECTURE.md), [SECURITY.md](SECURITY.md), [RELEASE.md](RELEASE.md). Those files describe the current code. They are not extra commands.
|
|
17
17
|
|
|
18
18
|
```
|
|
19
19
|
$ npx mcp-lint check tools.json
|
|
@@ -212,14 +212,14 @@ mcp-lint report tools.json --update-baseline audit.baseline.json
|
|
|
212
212
|
|
|
213
213
|
### `mcp-lint catalog`
|
|
214
214
|
|
|
215
|
-
Lists resources and prompts from one live server. It does not read installed configs and it does not call a tool.
|
|
215
|
+
Lists resources and prompts from one live server, then lints that list. It does not read installed configs and it does not call a tool. An empty or repeated URI, an empty resource name, an empty or repeated prompt name, and an empty or repeated argument name are errors and exit 1. A repeated resource display name, and a missing description, are warnings and do not. `explain` prints the rule. The fix class is `manual`: the command does not rewrite the server.
|
|
216
216
|
|
|
217
217
|
```bash
|
|
218
218
|
mcp-lint catalog --server stdio -- node server.js
|
|
219
219
|
mcp-lint catalog --server http --url https://example.com/mcp --format json
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
-
`--server` is `stdio`, `sse`, or `http`. Remote transports need `--url`. `--timeout`, `--max-items`, and `--max-bytes` bound each list. A method the server does not implement is `unsupported` and exits 0. Any other list failure exits 1. Usage errors exit 2. `--format` is `terminal` or `json`.
|
|
222
|
+
`--server` is `stdio`, `sse`, or `http`. Remote transports need `--url`. `--timeout`, `--max-items`, and `--max-bytes` bound each list. A method the server does not implement is `unsupported` and exits 0. Any other list failure exits 1. A lint error also exits 1. Usage errors exit 2. `--format` is `terminal` or `json`. JSON adds a `findings` array to the list. The array is empty when the list is clean.
|
|
223
223
|
|
|
224
224
|
---
|
|
225
225
|
|
|
@@ -432,9 +432,9 @@ jobs:
|
|
|
432
432
|
runs-on: ubuntu-latest
|
|
433
433
|
steps:
|
|
434
434
|
- uses: actions/checkout@v4
|
|
435
|
-
- uses: robert19001-cmyk/mcp-lint@v1.0.
|
|
436
|
-
# Tag v1.0.
|
|
437
|
-
# Tag v1.0.2 and tag v1.0.1 do the same.
|
|
435
|
+
- uses: robert19001-cmyk/mcp-lint@v1.0.4
|
|
436
|
+
# Tag v1.0.4 installs mcp-lint@1 unless version is set.
|
|
437
|
+
# Tag v1.0.3, tag v1.0.2, and tag v1.0.1 do the same.
|
|
438
438
|
# Tag v1.0.0 still installs npm latest.
|
|
439
439
|
with:
|
|
440
440
|
input: tools.json
|
|
@@ -457,7 +457,7 @@ jobs:
|
|
|
457
457
|
| `sarif` | `false` | Upload SARIF when this `mcp-lint` release includes `report`. The workflow needs `security-events: write`. |
|
|
458
458
|
| `version` | `1` | npm dist-tag or version. `1` installs `mcp-lint@1`. Tag `v1.0.0` ignores this input and installs `latest`. |
|
|
459
459
|
|
|
460
|
-
The action in this repository writes a `::error`, `::warning`, or `::notice` annotation from `check --format json`. With an empty `input` it also looks one directory down, skipping `node_modules` and dot directories. `sarif: true` uploads SARIF when the installed CLI has `report`. Tag `v1.0.
|
|
460
|
+
The action in this repository writes a `::error`, `::warning`, or `::notice` annotation from `check --format json`. With an empty `input` it also looks one directory down, skipping `node_modules` and dot directories. `sarif: true` uploads SARIF when the installed CLI has `report`. Tag `v1.0.4` installs `mcp-lint@1` unless `version` is set. Tag `v1.0.3`, tag `v1.0.2`, and tag `v1.0.1` do the same. Tag `v1.0.0` still installs npm `latest`. The workflow needs `security-events: write`.
|
|
461
461
|
|
|
462
462
|
### Manual workflow
|
|
463
463
|
|
|
@@ -774,7 +774,7 @@ The repository includes `glama.json` and a production Dockerfile so MCP director
|
|
|
774
774
|
|
|
775
775
|
## Site
|
|
776
776
|
|
|
777
|
-
The proof page is in `site/`. Version 1.0.
|
|
777
|
+
The proof page is in `site/`. Version 1.0.4. It runs the same recovery scorer, schema linter, and preflight function as the CLI. The check bench uses the same error index, title collapse, and next step as the terminal. `catalog` lints the resources and prompts it listed.
|
|
778
778
|
|
|
779
779
|
```bash
|
|
780
780
|
npm run site
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Diagnostic } from '../core/diagnostic.js';
|
|
2
|
+
import type { CatalogReport } from './list.js';
|
|
3
|
+
export interface CatalogFinding {
|
|
4
|
+
kind: 'resource' | 'prompt';
|
|
5
|
+
ruleId: string;
|
|
6
|
+
severity: 'error' | 'warning';
|
|
7
|
+
message: string;
|
|
8
|
+
name: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function lintCatalog(report: CatalogReport): CatalogFinding[];
|
|
12
|
+
export declare function catalogFindingsToDiagnostics(findings: CatalogFinding[]): Diagnostic[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { promptArgumentDescription, promptArgumentName, promptDescription, promptName, resourceDescription, resourceName, resourceUri, } from '../rules/catalog.js';
|
|
2
|
+
const CLIENTS = [
|
|
3
|
+
'claude', 'cursor', 'gemini', 'vscode', 'windsurf', 'cline', 'openai', 'continue',
|
|
4
|
+
];
|
|
5
|
+
export function lintCatalog(report) {
|
|
6
|
+
const findings = [];
|
|
7
|
+
if (report.resources.status === 'ran')
|
|
8
|
+
lintResources(report.resources.items, findings);
|
|
9
|
+
if (report.prompts.status === 'ran')
|
|
10
|
+
lintPrompts(report.prompts.items, findings);
|
|
11
|
+
return findings;
|
|
12
|
+
}
|
|
13
|
+
export function catalogFindingsToDiagnostics(findings) {
|
|
14
|
+
return findings.map((finding) => ({
|
|
15
|
+
ruleId: finding.ruleId,
|
|
16
|
+
severity: finding.severity,
|
|
17
|
+
message: finding.message,
|
|
18
|
+
toolName: finding.name,
|
|
19
|
+
path: finding.path,
|
|
20
|
+
clients: CLIENTS,
|
|
21
|
+
fix: {
|
|
22
|
+
class: 'manual',
|
|
23
|
+
description: 'You edit this on the server. mcp-lint does not write it.',
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
function lintResources(items, findings) {
|
|
28
|
+
const seenUris = new Map();
|
|
29
|
+
const seenNames = new Map();
|
|
30
|
+
for (const item of items) {
|
|
31
|
+
const uri = item.uri?.trim() ?? '';
|
|
32
|
+
if (uri)
|
|
33
|
+
seenUris.set(uri, (seenUris.get(uri) ?? 0) + 1);
|
|
34
|
+
const name = item.name?.trim() ?? '';
|
|
35
|
+
if (name)
|
|
36
|
+
seenNames.set(name, (seenNames.get(name) ?? 0) + 1);
|
|
37
|
+
}
|
|
38
|
+
const reportedUris = new Set();
|
|
39
|
+
const reportedNames = new Set();
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
const uri = item.uri?.trim() ?? '';
|
|
42
|
+
const name = item.name?.trim() ?? '';
|
|
43
|
+
const label = name || uri || '(resource)';
|
|
44
|
+
if (!uri) {
|
|
45
|
+
findings.push(finding('resource', resourceUri.id, 'error', `Resource "${label}" has an empty URI.`, label, 'uri'));
|
|
46
|
+
}
|
|
47
|
+
else if ((seenUris.get(uri) ?? 0) > 1 && !reportedUris.has(uri)) {
|
|
48
|
+
reportedUris.add(uri);
|
|
49
|
+
findings.push(finding('resource', resourceUri.id, 'error', `Resource URI "${uri}" appears ${seenUris.get(uri)} times in this list.`, label, 'uri'));
|
|
50
|
+
}
|
|
51
|
+
if (!name) {
|
|
52
|
+
findings.push(finding('resource', resourceName.id, 'error', `Resource "${uri || '(resource)'}" is missing a name.`, label, 'name'));
|
|
53
|
+
}
|
|
54
|
+
else if ((seenNames.get(name) ?? 0) > 1 && !reportedNames.has(name)) {
|
|
55
|
+
reportedNames.add(name);
|
|
56
|
+
findings.push(finding('resource', resourceName.id, 'warning', `Resource name "${name}" appears ${seenNames.get(name)} times. The URIs still differ, and the picker shows one label twice.`, name, 'name'));
|
|
57
|
+
}
|
|
58
|
+
if (!item.description?.trim()) {
|
|
59
|
+
findings.push(finding('resource', resourceDescription.id, 'warning', `Resource "${label}" is missing a description.`, label, 'description'));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function lintPrompts(items, findings) {
|
|
64
|
+
const seenNames = new Map();
|
|
65
|
+
for (const item of items) {
|
|
66
|
+
const name = item.name?.trim() ?? '';
|
|
67
|
+
if (name)
|
|
68
|
+
seenNames.set(name, (seenNames.get(name) ?? 0) + 1);
|
|
69
|
+
}
|
|
70
|
+
const reportedNames = new Set();
|
|
71
|
+
for (const item of items) {
|
|
72
|
+
const name = item.name?.trim() ?? '';
|
|
73
|
+
const label = name || '(prompt)';
|
|
74
|
+
if (!name) {
|
|
75
|
+
findings.push(finding('prompt', promptName.id, 'error', 'A prompt is missing a name.', label, 'name'));
|
|
76
|
+
}
|
|
77
|
+
else if ((seenNames.get(name) ?? 0) > 1 && !reportedNames.has(name)) {
|
|
78
|
+
reportedNames.add(name);
|
|
79
|
+
findings.push(finding('prompt', promptName.id, 'error', `Prompt name "${name}" appears ${seenNames.get(name)} times in this list.`, name, 'name'));
|
|
80
|
+
}
|
|
81
|
+
if (!item.description?.trim()) {
|
|
82
|
+
findings.push(finding('prompt', promptDescription.id, 'warning', `Prompt "${label}" is missing a description.`, label, 'description'));
|
|
83
|
+
}
|
|
84
|
+
lintArguments(label, item.arguments ?? [], findings);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function lintArguments(promptLabel, args, findings) {
|
|
88
|
+
const seen = new Map();
|
|
89
|
+
for (const arg of args) {
|
|
90
|
+
const name = arg.name?.trim() ?? '';
|
|
91
|
+
if (name)
|
|
92
|
+
seen.set(name, (seen.get(name) ?? 0) + 1);
|
|
93
|
+
}
|
|
94
|
+
const reported = new Set();
|
|
95
|
+
args.forEach((arg, index) => {
|
|
96
|
+
const name = arg.name?.trim() ?? '';
|
|
97
|
+
const path = name ? `arguments.${name}` : `arguments.${index}`;
|
|
98
|
+
if (!name) {
|
|
99
|
+
findings.push(finding('prompt', promptArgumentName.id, 'error', `Prompt "${promptLabel}" has an argument with an empty name.`, promptLabel, path));
|
|
100
|
+
}
|
|
101
|
+
else if ((seen.get(name) ?? 0) > 1 && !reported.has(name)) {
|
|
102
|
+
reported.add(name);
|
|
103
|
+
findings.push(finding('prompt', promptArgumentName.id, 'error', `Prompt "${promptLabel}" uses argument name "${name}" ${seen.get(name)} times.`, promptLabel, path));
|
|
104
|
+
}
|
|
105
|
+
if (name && !arg.description?.trim()) {
|
|
106
|
+
findings.push(finding('prompt', promptArgumentDescription.id, 'warning', `Argument "${name}" in prompt "${promptLabel}" is missing a description.`, promptLabel, path));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function finding(kind, ruleId, severity, message, name, path) {
|
|
111
|
+
return { kind, ruleId, severity, message, name, path };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/catalog/lint.ts"],"names":[],"mappings":"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"}
|
package/dist/catalog/list.d.ts
CHANGED
|
@@ -7,10 +7,16 @@ export interface CatalogResource {
|
|
|
7
7
|
description?: string;
|
|
8
8
|
mimeType?: string;
|
|
9
9
|
}
|
|
10
|
+
export interface CatalogPromptArgument {
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
required?: boolean;
|
|
14
|
+
}
|
|
10
15
|
export interface CatalogPrompt {
|
|
11
16
|
name: string;
|
|
12
17
|
title?: string;
|
|
13
18
|
description?: string;
|
|
19
|
+
arguments?: CatalogPromptArgument[];
|
|
14
20
|
}
|
|
15
21
|
export interface CatalogSource {
|
|
16
22
|
listResources(): Promise<{
|
package/dist/catalog/list.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/catalog/list.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AACjD,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/catalog/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import { type CatalogFinding } from '../../catalog/lint.js';
|
|
3
|
+
import { type CatalogReport } from '../../catalog/list.js';
|
|
2
4
|
export declare function registerCatalogCommand(program: Command): void;
|
|
5
|
+
export declare function catalogExitCode(report: CatalogReport, findings: CatalogFinding[]): number;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { catalogRemote, catalogStdio } from '../../catalog/connect.js';
|
|
2
|
+
import { catalogFindingsToDiagnostics, lintCatalog } from '../../catalog/lint.js';
|
|
2
3
|
import { DEFAULT_CATALOG_MAX_BYTES, DEFAULT_CATALOG_MAX_ITEMS, DEFAULT_CATALOG_TIMEOUT_MS, } from '../../catalog/list.js';
|
|
4
|
+
import { formatTerminal } from '../formatters/terminal.js';
|
|
3
5
|
export function registerCatalogCommand(program) {
|
|
4
6
|
program
|
|
5
7
|
.command('catalog')
|
|
6
|
-
.description('List resources and prompts from a live MCP server. Does not call tools.')
|
|
8
|
+
.description('List resources and prompts from a live MCP server, then lint that list. Does not call tools.')
|
|
7
9
|
.option('--server <type>', 'Server transport: stdio|sse|http')
|
|
8
10
|
.option('--url <url>', 'Remote server URL for sse or http')
|
|
9
11
|
.option('--timeout <ms>', 'Timeout in milliseconds', String(DEFAULT_CATALOG_TIMEOUT_MS))
|
|
@@ -39,9 +41,11 @@ export function registerCatalogCommand(program) {
|
|
|
39
41
|
if (options.format !== 'json' && options.format !== 'terminal') {
|
|
40
42
|
throw new Error('--format must be json or terminal');
|
|
41
43
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const findings = lintCatalog(report);
|
|
45
|
+
process.stdout.write(options.format === 'json'
|
|
46
|
+
? JSON.stringify({ ...report, findings }, null, 2) + '\n'
|
|
47
|
+
: render(report, findings));
|
|
48
|
+
process.exit(catalogExitCode(report, findings));
|
|
45
49
|
}
|
|
46
50
|
catch (err) {
|
|
47
51
|
process.stderr.write(`Error: ${err.message}\n`);
|
|
@@ -49,13 +53,20 @@ export function registerCatalogCommand(program) {
|
|
|
49
53
|
}
|
|
50
54
|
});
|
|
51
55
|
}
|
|
52
|
-
function
|
|
56
|
+
export function catalogExitCode(report, findings) {
|
|
57
|
+
const failed = report.resources.status === 'failed' || report.prompts.status === 'failed';
|
|
58
|
+
const errors = findings.some((finding) => finding.severity === 'error');
|
|
59
|
+
return failed || errors ? 1 : 0;
|
|
60
|
+
}
|
|
61
|
+
function render(report, findings) {
|
|
53
62
|
const lines = ['', 'resources'];
|
|
54
63
|
lines.push(...sectionLines(report.resources, (item) => `${item.uri} ${item.name}`));
|
|
55
64
|
lines.push('', 'prompts');
|
|
56
65
|
lines.push(...sectionLines(report.prompts, (item) => item.title ? `${item.name} ${item.title}` : item.name));
|
|
57
66
|
lines.push('');
|
|
58
|
-
|
|
67
|
+
if (findings.length === 0)
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
return lines.join('\n') + formatTerminal(catalogFindingsToDiagnostics(findings));
|
|
59
70
|
}
|
|
60
71
|
function sectionLines(section, line) {
|
|
61
72
|
if (section.status !== 'ran')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../src/cli/commands/catalog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,GAE3B,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../src/cli/commands/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Rule } from '../core/rule.js';
|
|
2
|
+
export declare const resourceUri: Rule;
|
|
3
|
+
export declare const resourceName: Rule;
|
|
4
|
+
export declare const resourceDescription: Rule;
|
|
5
|
+
export declare const promptName: Rule;
|
|
6
|
+
export declare const promptDescription: Rule;
|
|
7
|
+
export declare const promptArgumentName: Rule;
|
|
8
|
+
export declare const promptArgumentDescription: Rule;
|
|
9
|
+
export declare const catalogRules: Rule[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const CLIENTS = [
|
|
2
|
+
'claude', 'cursor', 'gemini', 'vscode', 'windsurf', 'cline', 'openai', 'continue',
|
|
3
|
+
];
|
|
4
|
+
function listed(id, severity, description, why, bad, good, fixNote) {
|
|
5
|
+
return {
|
|
6
|
+
id,
|
|
7
|
+
severity,
|
|
8
|
+
description,
|
|
9
|
+
fixClass: 'manual',
|
|
10
|
+
clients: CLIENTS,
|
|
11
|
+
docs: { why, badExample: bad, goodExample: good, fixNote },
|
|
12
|
+
check(_tool, _context) {
|
|
13
|
+
return [];
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export const resourceUri = listed('resource/uri', 'error', 'A resource URI must be present, and the same URI must not appear twice in one list.', 'A client reads a resource by URI. An empty URI cannot be read. Two entries with the same URI do not say which body belongs to the call.', { resources: [{ uri: '', name: 'notes' }, { uri: 'file://readme', name: 'a' }, { uri: 'file://readme', name: 'b' }] }, { resources: [{ uri: 'file://notes', name: 'notes' }, { uri: 'file://readme', name: 'readme' }] }, 'Give each resource its own non-empty URI. mcp-lint does not invent one.');
|
|
18
|
+
export const resourceName = listed('resource/name', 'error', 'A resource needs a name. The same display name twice is a warning when the URIs differ.', 'The name is what the picker shows. An empty name leaves a blank row. The same name on two URIs makes the picker ambiguous, while the URI still identifies the read.', { resources: [{ uri: 'file://a', name: '' }, { uri: 'file://b', name: 'notes' }, { uri: 'file://c', name: 'notes' }] }, { resources: [{ uri: 'file://a', name: 'alpha' }, { uri: 'file://b', name: 'beta' }] }, 'Name each resource. Rename one of a pair that shares a display name. mcp-lint does not invent a name.');
|
|
19
|
+
export const resourceDescription = listed('resource/description', 'warning', 'A resource should have a description so a client can choose it.', 'Clients show the description next to the name when an agent decides which resource to read. A missing description leaves only the name.', { uri: 'file://readme', name: 'readme' }, { uri: 'file://readme', name: 'readme', description: 'Repository overview.' }, 'Write the description on the server. mcp-lint does not invent one.');
|
|
20
|
+
export const promptName = listed('prompt/name', 'error', 'A prompt name must be present, and the same name must not appear twice in one list.', 'A client calls prompts/get with the name. An empty name cannot be called. Two entries with the same name make one of them unreachable.', { prompts: [{ name: '' }, { name: 'review' }, { name: 'review' }] }, { prompts: [{ name: 'review_diff' }, { name: 'review_issue' }] }, 'Give each prompt its own non-empty name. mcp-lint does not rename it.');
|
|
21
|
+
export const promptDescription = listed('prompt/description', 'warning', 'A prompt should have a description so a client can choose it.', 'Clients show the description when an agent decides which prompt to run. A missing description leaves only the name.', { name: 'review' }, { name: 'review', description: 'Review the current diff.' }, 'Write the description on the server. mcp-lint does not invent one.');
|
|
22
|
+
export const promptArgumentName = listed('prompt/argument-name', 'error', 'Each prompt argument needs a name, and that name must be unique inside the prompt.', 'The client fills arguments by name. An empty name or two arguments with the same name make the prompt impossible to fill.', { name: 'review', arguments: [{ name: '' }, { name: 'path' }, { name: 'path' }] }, { name: 'review', arguments: [{ name: 'path', description: 'File to review.' }] }, 'Name each argument once inside the prompt. mcp-lint does not rename it.');
|
|
23
|
+
export const promptArgumentDescription = listed('prompt/argument-description', 'warning', 'A prompt argument should have a description.', 'The description tells the agent what to put in the argument. Without it, the name is the only hint.', { name: 'review', arguments: [{ name: 'path' }] }, { name: 'review', arguments: [{ name: 'path', description: 'File to review.' }] }, 'Write the argument description on the server. mcp-lint does not invent one.');
|
|
24
|
+
export const catalogRules = [
|
|
25
|
+
resourceUri,
|
|
26
|
+
resourceName,
|
|
27
|
+
resourceDescription,
|
|
28
|
+
promptName,
|
|
29
|
+
promptDescription,
|
|
30
|
+
promptArgumentName,
|
|
31
|
+
promptArgumentDescription,
|
|
32
|
+
];
|
|
33
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/rules/catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { openaiNoAdditionalProperties, openaiStrictTypes } from './client-specif
|
|
|
17
17
|
import { noUnreachableInstruction } from './agent-unreachable.js';
|
|
18
18
|
import { destructiveUnmarked, readonlyConflict } from './destructive-hint.js';
|
|
19
19
|
import { toolNameClientSafe, toolNameDuplicate, toolNameServerCollision } from './tool-name.js';
|
|
20
|
+
import { catalogRules } from './catalog.js';
|
|
20
21
|
export const allRules = [
|
|
21
22
|
noRequiredFalse,
|
|
22
23
|
noContentEncoding,
|
|
@@ -43,5 +44,6 @@ export const allRules = [
|
|
|
43
44
|
toolNameClientSafe,
|
|
44
45
|
toolNameDuplicate,
|
|
45
46
|
toolNameServerCollision,
|
|
47
|
+
...catalogRules,
|
|
46
48
|
];
|
|
47
49
|
//# sourceMappingURL=index.js.map
|
package/dist/rules/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,OAAO,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAC9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,OAAO,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAC9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,CAAC,MAAM,QAAQ,GAAW;IAC9B,eAAe;IACf,iBAAiB;IACjB,iBAAiB;IACjB,WAAW;IACX,QAAQ;IACR,eAAe;IACf,qBAAqB;IACrB,oBAAoB;IACpB,iBAAiB;IACjB,uBAAuB;IACvB,0BAA0B;IAC1B,oBAAoB;IACpB,8BAA8B;IAC9B,qBAAqB;IACrB,eAAe;IACf,oBAAoB;IACpB,yBAAyB;IACzB,4BAA4B;IAC5B,iBAAiB;IACjB,wBAAwB;IACxB,mBAAmB;IACnB,gBAAgB;IAChB,kBAAkB;IAClB,iBAAiB;IACjB,uBAAuB;IACvB,GAAG,YAAY;CAChB,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.0.
|
|
1
|
+
export declare const VERSION = "1.0.4";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '1.0.
|
|
1
|
+
export const VERSION = '1.0.4';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -15,10 +15,10 @@ jobs:
|
|
|
15
15
|
- uses: actions/checkout@v4
|
|
16
16
|
|
|
17
17
|
- name: Lint MCP tool schemas
|
|
18
|
-
# Tag v1.0.
|
|
19
|
-
# Tag v1.0.2 and tag v1.0.1 do the same.
|
|
18
|
+
# Tag v1.0.4 installs mcp-lint@1 unless version is set.
|
|
19
|
+
# Tag v1.0.3, tag v1.0.2, and tag v1.0.1 do the same.
|
|
20
20
|
# Tag v1.0.0 still installs npm latest.
|
|
21
|
-
uses: robert19001-cmyk/mcp-lint@v1.0.
|
|
21
|
+
uses: robert19001-cmyk/mcp-lint@v1.0.4
|
|
22
22
|
with:
|
|
23
23
|
input: tools.json
|
|
24
24
|
severity: warning
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-lint",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"mcpName": "io.github.robert19001-cmyk/mcp-lint",
|
|
5
5
|
"description": "Lint MCP tool schemas before they break Claude, Cursor, Gemini, VS Code, Windsurf, Cline, OpenAI, or Continue. Run npx mcp-lint.",
|
|
6
6
|
"type": "module",
|