mcp-lint 0.6.0 → 0.7.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 (45) hide show
  1. package/README.md +60 -2
  2. package/dist/cli/commands/check.js +123 -31
  3. package/dist/cli/commands/check.js.map +1 -1
  4. package/dist/cli/commands/snapshot.d.ts +2 -0
  5. package/dist/cli/commands/snapshot.js +56 -0
  6. package/dist/cli/commands/snapshot.js.map +1 -0
  7. package/dist/index.js +3 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/installed/config.d.ts +34 -0
  10. package/dist/installed/config.js +130 -0
  11. package/dist/installed/config.js.map +1 -0
  12. package/dist/installed/format.d.ts +2 -0
  13. package/dist/installed/format.js +72 -0
  14. package/dist/installed/format.js.map +1 -0
  15. package/dist/installed/lint.d.ts +19 -0
  16. package/dist/installed/lint.js +49 -0
  17. package/dist/installed/lint.js.map +1 -0
  18. package/dist/loaders/index.d.ts +1 -0
  19. package/dist/loaders/index.js +1 -0
  20. package/dist/loaders/index.js.map +1 -1
  21. package/dist/loaders/map-tools.d.ts +16 -0
  22. package/dist/loaders/map-tools.js +33 -0
  23. package/dist/loaders/map-tools.js.map +1 -0
  24. package/dist/loaders/remote-loader.d.ts +2 -0
  25. package/dist/loaders/remote-loader.js +37 -0
  26. package/dist/loaders/remote-loader.js.map +1 -0
  27. package/dist/loaders/sse-loader.d.ts +1 -1
  28. package/dist/loaders/sse-loader.js +3 -17
  29. package/dist/loaders/sse-loader.js.map +1 -1
  30. package/dist/loaders/stdio-loader.d.ts +8 -1
  31. package/dist/loaders/stdio-loader.js +58 -12
  32. package/dist/loaders/stdio-loader.js.map +1 -1
  33. package/dist/mcp-server.js +1 -1
  34. package/dist/recover/probe.js +1 -1
  35. package/dist/rules/index.js +2 -0
  36. package/dist/rules/index.js.map +1 -1
  37. package/dist/rules/tool-name.d.ts +2 -0
  38. package/dist/rules/tool-name.js +44 -0
  39. package/dist/rules/tool-name.js.map +1 -0
  40. package/dist/snapshot/serialize.d.ts +2 -0
  41. package/dist/snapshot/serialize.js +17 -0
  42. package/dist/snapshot/serialize.js.map +1 -0
  43. package/examples/github-action.yml +1 -1
  44. package/examples/names/unsafe.json +22 -0
  45. package/package.json +2 -2
@@ -0,0 +1,72 @@
1
+ import chalk from 'chalk';
2
+ import { formatMarkdown } from '../cli/formatters/markdown.js';
3
+ import { formatTerminal } from '../cli/formatters/terminal.js';
4
+ export function formatInstalled(reports, format, noColor = false) {
5
+ if (format === 'json')
6
+ return formatInstalledJson(reports);
7
+ if (format === 'markdown')
8
+ return formatInstalledMarkdown(reports);
9
+ return formatInstalledTerminal(reports, noColor);
10
+ }
11
+ function formatInstalledTerminal(reports, noColor) {
12
+ if (noColor)
13
+ chalk.level = 0;
14
+ const lines = [];
15
+ for (const report of reports) {
16
+ const where = `${report.server.sourceLabel} ${report.server.sourcePath}`;
17
+ lines.push(chalk.bold(where));
18
+ lines.push(chalk.bold(` ${report.server.name}`));
19
+ if (report.status === 'skipped-remote') {
20
+ lines.push(chalk.dim(' skipped remote URL. Pass --remote to connect.'));
21
+ lines.push('');
22
+ continue;
23
+ }
24
+ if (report.status === 'failed') {
25
+ lines.push(chalk.red(` failed to start: ${report.error ?? 'unknown error'}`));
26
+ lines.push('');
27
+ continue;
28
+ }
29
+ lines.push(chalk.dim(` ${report.tools.length} tool${report.tools.length === 1 ? '' : 's'}`));
30
+ lines.push(indent(formatTerminal(report.diagnostics, noColor)));
31
+ }
32
+ return `${lines.join('\n').replace(/\n+$/, '')}\n`;
33
+ }
34
+ function formatInstalledMarkdown(reports) {
35
+ const lines = [];
36
+ for (const report of reports) {
37
+ lines.push(`## ${report.server.name}`);
38
+ lines.push('');
39
+ lines.push(`Source: \`${report.server.sourcePath}\``);
40
+ lines.push('');
41
+ if (report.status === 'skipped-remote') {
42
+ lines.push('_Skipped remote URL. Pass `--remote` to connect._');
43
+ lines.push('');
44
+ continue;
45
+ }
46
+ if (report.status === 'failed') {
47
+ lines.push(`Failed to start: ${report.error ?? 'unknown error'}`);
48
+ lines.push('');
49
+ continue;
50
+ }
51
+ lines.push(formatMarkdown(report.diagnostics).trimEnd());
52
+ lines.push('');
53
+ }
54
+ return `${lines.join('\n').trimEnd()}\n`;
55
+ }
56
+ function formatInstalledJson(reports) {
57
+ return `${JSON.stringify({
58
+ servers: reports.map((report) => ({
59
+ source: report.server.sourcePath,
60
+ label: report.server.sourceLabel,
61
+ name: report.server.name,
62
+ status: report.status,
63
+ toolCount: report.tools.length,
64
+ ...(report.error ? { error: report.error } : {}),
65
+ diagnostics: report.diagnostics.map(({ fix: _fix, ...diagnostic }) => diagnostic),
66
+ })),
67
+ }, null, 2)}\n`;
68
+ }
69
+ function indent(text) {
70
+ return text.split('\n').map((line) => (line.length > 0 ? ` ${line}` : line)).join('\n');
71
+ }
72
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/installed/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Diagnostic } from '../core/diagnostic.js';
2
+ import type { MCPTool } from '../core/rule.js';
3
+ import { loadRemote } from '../loaders/remote-loader.js';
4
+ import { loadStdio } from '../loaders/stdio-loader.js';
5
+ import type { InstalledServer } from './config.js';
6
+ export interface ServerReport {
7
+ server: InstalledServer;
8
+ status: 'linted' | 'skipped-remote' | 'failed';
9
+ tools: MCPTool[];
10
+ diagnostics: Diagnostic[];
11
+ error?: string;
12
+ }
13
+ export declare function lintInstalledServers(servers: InstalledServer[], options: {
14
+ remote: boolean;
15
+ timeoutMs: number;
16
+ lint: (tools: MCPTool[]) => Diagnostic[];
17
+ loadStdio?: typeof loadStdio;
18
+ loadRemote?: typeof loadRemote;
19
+ }): Promise<ServerReport[]>;
@@ -0,0 +1,49 @@
1
+ import { loadRemote } from '../loaders/remote-loader.js';
2
+ import { loadStdio } from '../loaders/stdio-loader.js';
3
+ export async function lintInstalledServers(servers, options) {
4
+ const loadLocal = options.loadStdio ?? loadStdio;
5
+ const loadUrl = options.loadRemote ?? loadRemote;
6
+ const reports = [];
7
+ for (const server of servers) {
8
+ if (server.kind === 'invalid') {
9
+ reports.push(failed(server, server.detail ?? 'Server entry is incomplete'));
10
+ continue;
11
+ }
12
+ if (server.kind === 'remote') {
13
+ if (!options.remote) {
14
+ reports.push({
15
+ server,
16
+ status: 'skipped-remote',
17
+ tools: [],
18
+ diagnostics: [],
19
+ });
20
+ continue;
21
+ }
22
+ try {
23
+ const tools = await loadUrl(server.url ?? '', server.remoteType ?? 'http', options.timeoutMs);
24
+ reports.push({ server, status: 'linted', tools, diagnostics: options.lint(tools) });
25
+ }
26
+ catch (err) {
27
+ reports.push(failed(server, err.message));
28
+ }
29
+ continue;
30
+ }
31
+ try {
32
+ const tools = await loadLocal(server.command ?? '', server.args, {
33
+ env: server.env,
34
+ cwd: server.cwd,
35
+ timeoutMs: options.timeoutMs,
36
+ inheritProcessEnv: true,
37
+ });
38
+ reports.push({ server, status: 'linted', tools, diagnostics: options.lint(tools) });
39
+ }
40
+ catch (err) {
41
+ reports.push(failed(server, err.message));
42
+ }
43
+ }
44
+ return reports;
45
+ }
46
+ function failed(server, error) {
47
+ return { server, status: 'failed', tools: [], diagnostics: [], error };
48
+ }
49
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/installed/lint.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAWvD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAA0B,EAC1B,OAMC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,IAAI,UAAU,CAAC;IACjD,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,4BAA4B,CAAC,CAAC,CAAC;YAC5E,SAAS;QACX,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM;oBACN,MAAM,EAAE,gBAAgB;oBACxB,KAAK,EAAE,EAAE;oBACT,WAAW,EAAE,EAAE;iBAChB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,MAAM,CAAC,UAAU,IAAI,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;gBAC9F,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC,CAAC;YACvD,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE;gBAC/D,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,iBAAiB,EAAE,IAAI;aACxB,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,MAAM,CAAC,MAAuB,EAAE,KAAa;IACpD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AACzE,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export { loadFile } from './file-loader.js';
2
+ export { loadRemote } from './remote-loader.js';
2
3
  export { loadSSE } from './sse-loader.js';
3
4
  export { loadStdio } from './stdio-loader.js';
@@ -1,4 +1,5 @@
1
1
  export { loadFile } from './file-loader.js';
2
+ export { loadRemote } from './remote-loader.js';
2
3
  export { loadSSE } from './sse-loader.js';
3
4
  export { loadStdio } from './stdio-loader.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loaders/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { MCPTool } from '../core/rule.js';
2
+ interface ListedTool {
3
+ name: string;
4
+ title?: string;
5
+ description?: string;
6
+ inputSchema?: object;
7
+ annotations?: {
8
+ title?: string;
9
+ readOnlyHint?: boolean;
10
+ destructiveHint?: boolean;
11
+ idempotentHint?: boolean;
12
+ openWorldHint?: boolean;
13
+ };
14
+ }
15
+ export declare function mapListedTools(tools: ListedTool[]): MCPTool[];
16
+ export {};
@@ -0,0 +1,33 @@
1
+ export function mapListedTools(tools) {
2
+ return tools.map((tool) => {
3
+ const mapped = {
4
+ name: tool.name,
5
+ inputSchema: tool.inputSchema ?? { type: 'object' },
6
+ };
7
+ if (tool.title)
8
+ mapped.title = tool.title;
9
+ if (tool.description)
10
+ mapped.description = tool.description;
11
+ const annotations = pickAnnotations(tool.annotations);
12
+ if (annotations)
13
+ mapped.annotations = annotations;
14
+ return mapped;
15
+ });
16
+ }
17
+ function pickAnnotations(annotations) {
18
+ if (!annotations)
19
+ return undefined;
20
+ const picked = {};
21
+ if (annotations.title !== undefined)
22
+ picked.title = annotations.title;
23
+ if (annotations.readOnlyHint !== undefined)
24
+ picked.readOnlyHint = annotations.readOnlyHint;
25
+ if (annotations.destructiveHint !== undefined)
26
+ picked.destructiveHint = annotations.destructiveHint;
27
+ if (annotations.idempotentHint !== undefined)
28
+ picked.idempotentHint = annotations.idempotentHint;
29
+ if (annotations.openWorldHint !== undefined)
30
+ picked.openWorldHint = annotations.openWorldHint;
31
+ return Object.keys(picked).length > 0 ? picked : undefined;
32
+ }
33
+ //# sourceMappingURL=map-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-tools.js","sourceRoot":"","sources":["../../src/loaders/map-tools.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,MAAM,GAAY;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAG,IAAI,CAAC,WAAkD,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC5F,CAAC;QACF,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1C,IAAI,IAAI,CAAC,WAAW;YAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,WAAW;YAAE,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;QAClD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,WAAsC;IAC7D,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;IACtE,IAAI,WAAW,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,WAAW,CAAC,YAAY,CAAC;IAC3F,IAAI,WAAW,CAAC,eAAe,KAAK,SAAS;QAAE,MAAM,CAAC,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC;IACpG,IAAI,WAAW,CAAC,cAAc,KAAK,SAAS;QAAE,MAAM,CAAC,cAAc,GAAG,WAAW,CAAC,cAAc,CAAC;IACjG,IAAI,WAAW,CAAC,aAAa,KAAK,SAAS;QAAE,MAAM,CAAC,aAAa,GAAG,WAAW,CAAC,aAAa,CAAC;IAC9F,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { MCPTool } from '../core/rule.js';
2
+ export declare function loadRemote(url: string, remoteType?: string, timeoutMs?: number): Promise<MCPTool[]>;
@@ -0,0 +1,37 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
3
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4
+ import { mapListedTools } from './map-tools.js';
5
+ export async function loadRemote(url, remoteType = 'http', timeoutMs) {
6
+ const target = new URL(url);
7
+ const transport = remoteType === 'sse'
8
+ ? new SSEClientTransport(target)
9
+ : new StreamableHTTPClientTransport(target);
10
+ const client = new Client({ name: 'mcp-lint', version: '0.7.0' }, { capabilities: {} });
11
+ let timer;
12
+ try {
13
+ const connect = async () => {
14
+ await client.connect(transport);
15
+ const result = await client.listTools();
16
+ return mapListedTools(result.tools);
17
+ };
18
+ if (!timeoutMs || timeoutMs < 1)
19
+ return await connect();
20
+ return await Promise.race([
21
+ connect(),
22
+ new Promise((_, reject) => {
23
+ timer = setTimeout(() => {
24
+ void transport.close();
25
+ reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${url}`));
26
+ }, timeoutMs);
27
+ }),
28
+ ]);
29
+ }
30
+ finally {
31
+ if (timer)
32
+ clearTimeout(timer);
33
+ await client.close().catch(() => { });
34
+ await transport.close().catch(() => { });
35
+ }
36
+ }
37
+ //# sourceMappingURL=remote-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-loader.js","sourceRoot":"","sources":["../../src/loaders/remote-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,UAAU,GAAG,MAAM,EAAE,SAAkB;IACnF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,UAAU,KAAK,KAAK;QACpC,CAAC,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC;QAChC,CAAC,CAAC,IAAI,6BAA6B,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IAExF,IAAI,KAAgD,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;YACzB,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YACxC,OAAO,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC;QACF,IAAI,CAAC,SAAS,IAAI,SAAS,GAAG,CAAC;YAAE,OAAO,MAAM,OAAO,EAAE,CAAC;QACxD,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACxB,OAAO,EAAE;YACT,IAAI,OAAO,CAAY,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBACnC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;oBACtB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;oBACvB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,SAAS,kBAAkB,GAAG,EAAE,CAAC,CAAC,CAAC;gBACzE,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC"}
@@ -1,2 +1,2 @@
1
1
  import type { MCPTool } from '../core/rule.js';
2
- export declare function loadSSE(url: string): Promise<MCPTool[]>;
2
+ export declare function loadSSE(url: string, timeoutMs?: number): Promise<MCPTool[]>;
@@ -1,19 +1,5 @@
1
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
3
- export async function loadSSE(url) {
4
- const transport = new SSEClientTransport(new URL(url));
5
- const client = new Client({ name: 'mcp-lint', version: '0.1.0' }, { capabilities: {} });
6
- await client.connect(transport);
7
- try {
8
- const result = await client.listTools();
9
- return result.tools.map((t) => ({
10
- name: t.name,
11
- description: t.description,
12
- inputSchema: t.inputSchema,
13
- }));
14
- }
15
- finally {
16
- await client.close();
17
- }
1
+ import { loadRemote } from './remote-loader.js';
2
+ export async function loadSSE(url, timeoutMs) {
3
+ return loadRemote(url, 'sse', timeoutMs);
18
4
  }
19
5
  //# sourceMappingURL=sse-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse-loader.js","sourceRoot":"","sources":["../../src/loaders/sse-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAG7E,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW;IACvC,MAAM,SAAS,GAAG,IAAI,kBAAkB,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IAExF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QACxC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAqC;SACrD,CAAC,CAAC,CAAC;IACN,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"sse-loader.js","sourceRoot":"","sources":["../../src/loaders/sse-loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,SAAkB;IAC3D,OAAO,UAAU,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC"}
@@ -1,2 +1,9 @@
1
1
  import type { MCPTool } from '../core/rule.js';
2
- export declare function loadStdio(command: string, args: string[]): Promise<MCPTool[]>;
2
+ export interface StdioLoadOptions {
3
+ env?: Record<string, string>;
4
+ cwd?: string;
5
+ timeoutMs?: number;
6
+ inheritProcessEnv?: boolean;
7
+ }
8
+ export declare function loadStdio(command: string, args: string[], options?: StdioLoadOptions): Promise<MCPTool[]>;
9
+ export declare function childEnvironment(extra?: Record<string, string>, inheritProcessEnv?: boolean): Record<string, string>;
@@ -1,19 +1,65 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
- export async function loadStdio(command, args) {
4
- const transport = new StdioClientTransport({ command, args });
5
- const client = new Client({ name: 'mcp-lint', version: '0.1.0' }, { capabilities: {} });
6
- await client.connect(transport);
2
+ import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import { mapListedTools } from './map-tools.js';
4
+ export async function loadStdio(command, args, options = {}) {
5
+ const transport = new StdioClientTransport({
6
+ command,
7
+ args,
8
+ env: options.env || options.inheritProcessEnv
9
+ ? childEnvironment(options.env, Boolean(options.inheritProcessEnv))
10
+ : undefined,
11
+ stderr: 'pipe',
12
+ cwd: options.cwd,
13
+ });
14
+ drain(transport.stderr);
15
+ const client = new Client({ name: 'mcp-lint', version: '0.7.0' }, { capabilities: {} });
7
16
  try {
8
- const result = await client.listTools();
9
- return result.tools.map((t) => ({
10
- name: t.name,
11
- description: t.description,
12
- inputSchema: t.inputSchema,
13
- }));
17
+ return await withTimeout(options.timeoutMs, command, async () => {
18
+ await client.connect(transport);
19
+ const result = await client.listTools();
20
+ return mapListedTools(result.tools);
21
+ }, async () => {
22
+ await transport.close();
23
+ });
14
24
  }
15
25
  finally {
16
- await client.close();
26
+ await client.close().catch(() => { });
27
+ await transport.close().catch(() => { });
17
28
  }
18
29
  }
30
+ export function childEnvironment(extra, inheritProcessEnv = false) {
31
+ const env = { ...getDefaultEnvironment() };
32
+ if (inheritProcessEnv) {
33
+ for (const [key, value] of Object.entries(process.env)) {
34
+ if (typeof value === 'string')
35
+ env[key] = value;
36
+ }
37
+ }
38
+ if (extra)
39
+ Object.assign(env, extra);
40
+ return env;
41
+ }
42
+ async function withTimeout(timeoutMs, label, run, cancel) {
43
+ if (!timeoutMs || timeoutMs < 1)
44
+ return run();
45
+ let timer;
46
+ try {
47
+ return await Promise.race([
48
+ run(),
49
+ new Promise((_, reject) => {
50
+ timer = setTimeout(() => {
51
+ void cancel();
52
+ reject(new Error(`Timed out after ${timeoutMs}ms waiting for ${label}`));
53
+ }, timeoutMs);
54
+ }),
55
+ ]);
56
+ }
57
+ finally {
58
+ if (timer)
59
+ clearTimeout(timer);
60
+ }
61
+ }
62
+ function drain(stream) {
63
+ stream?.on('data', () => { });
64
+ }
19
65
  //# sourceMappingURL=stdio-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio-loader.js","sourceRoot":"","sources":["../../src/loaders/stdio-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAGjF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe,EAAE,IAAc;IAC7D,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IAExF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QACxC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAqC;SACrD,CAAC,CAAC,CAAC;IACN,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"stdio-loader.js","sourceRoot":"","sources":["../../src/loaders/stdio-loader.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ import { preflight } from './preflight/engine.js';
13
13
  import { parsePolicy } from './preflight/policy.js';
14
14
  import { scoreRecovery } from './recover/score.js';
15
15
  import { FAILURE_KINDS } from './recover/types.js';
16
- const VERSION = '0.6.0';
16
+ const VERSION = '0.7.0';
17
17
  const ClientIdSchema = z.enum([
18
18
  'claude',
19
19
  'cursor',
@@ -47,7 +47,7 @@ export async function probeClient(client, options = {}) {
47
47
  }
48
48
  export async function probeStdio(command, args, options = {}) {
49
49
  const transport = new StdioClientTransport({ command, args });
50
- const client = new Client({ name: 'mcp-lint-recover', version: '0.6.0' }, { capabilities: {} });
50
+ const client = new Client({ name: 'mcp-lint-recover', version: '0.7.0' }, { capabilities: {} });
51
51
  await client.connect(transport);
52
52
  try {
53
53
  return await probeClient(client, options);
@@ -16,6 +16,7 @@ import { clineDescriptionMaxLength } from './client-specific/cline.js';
16
16
  import { openaiNoAdditionalProperties, openaiStrictTypes } from './client-specific/openai.js';
17
17
  import { noUnreachableInstruction } from './agent-unreachable.js';
18
18
  import { destructiveUnmarked, readonlyConflict } from './destructive-hint.js';
19
+ import { toolNameClientSafe } from './tool-name.js';
19
20
  export const allRules = [
20
21
  noRequiredFalse,
21
22
  noContentEncoding,
@@ -39,5 +40,6 @@ export const allRules = [
39
40
  noUnreachableInstruction,
40
41
  destructiveUnmarked,
41
42
  readonlyConflict,
43
+ toolNameClientSafe,
42
44
  ];
43
45
  //# sourceMappingURL=index.js.map
@@ -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;AAE9E,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;CACjB,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,MAAM,gBAAgB,CAAC;AAEpD,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;CACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from '../core/rule.js';
2
+ export declare const toolNameClientSafe: Rule;
@@ -0,0 +1,44 @@
1
+ const CLIENTS = [
2
+ 'claude', 'cursor', 'gemini', 'vscode', 'windsurf', 'cline', 'openai', 'continue',
3
+ ];
4
+ // OpenAI and Azure: ^[A-Za-z0-9_-]{1,64}$
5
+ // Gemini also requires the first character to be a letter or underscore.
6
+ const SAFE_NAME = /^[A-Za-z0-9_-]{1,64}$/;
7
+ const LEADING_DIGIT = /^[0-9]/;
8
+ export const toolNameClientSafe = {
9
+ id: 'tool-name/client-safe',
10
+ severity: 'error',
11
+ description: 'Tool names must be 1–64 characters of letters, digits, underscores, or hyphens, and should not start with a digit.',
12
+ clients: CLIENTS,
13
+ docs: {
14
+ why: 'OpenAI and Azure reject a tool name outside ^[A-Za-z0-9_-]{1,64}$. A space, slash, or dot makes the tool disappear. Gemini also refuses a name that starts with a digit.',
15
+ badExample: { name: 'search files' },
16
+ goodExample: { name: 'search_files' },
17
+ fixNote: 'Rename the tool. Use letters, digits, underscores, or hyphens, start with a letter or underscore, and stay within 64 characters.',
18
+ },
19
+ check(tool, _context) {
20
+ const name = tool.name ?? '';
21
+ if (!SAFE_NAME.test(name)) {
22
+ return [{
23
+ ruleId: 'tool-name/client-safe',
24
+ severity: 'error',
25
+ message: `Tool name "${name}" is not safe across clients. Use 1–64 characters: letters, digits, underscore, or hyphen.`,
26
+ toolName: name || '(unnamed)',
27
+ path: 'name',
28
+ clients: ['claude', 'openai', 'gemini'],
29
+ }];
30
+ }
31
+ if (LEADING_DIGIT.test(name)) {
32
+ return [{
33
+ ruleId: 'tool-name/client-safe',
34
+ severity: 'warning',
35
+ message: `Tool name "${name}" starts with a digit. Gemini requires a letter or an underscore first.`,
36
+ toolName: name,
37
+ path: 'name',
38
+ clients: ['gemini'],
39
+ }];
40
+ }
41
+ return [];
42
+ },
43
+ };
44
+ //# sourceMappingURL=tool-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-name.js","sourceRoot":"","sources":["../../src/rules/tool-name.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,GAAe;IAC1B,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;CAClF,CAAC;AAEF,0CAA0C;AAC1C,yEAAyE;AACzE,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAC1C,MAAM,aAAa,GAAG,QAAQ,CAAC;AAE/B,MAAM,CAAC,MAAM,kBAAkB,GAAS;IACtC,EAAE,EAAE,uBAAuB;IAC3B,QAAQ,EAAE,OAAO;IACjB,WAAW,EAAE,oHAAoH;IACjI,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE;QACJ,GAAG,EAAE,0KAA0K;QAC/K,UAAU,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;QACpC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;QACrC,OAAO,EAAE,kIAAkI;KAC5I;IAED,KAAK,CAAC,IAAa,EAAE,QAAqB;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC;oBACN,MAAM,EAAE,uBAAuB;oBAC/B,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,cAAc,IAAI,4FAA4F;oBACvH,QAAQ,EAAE,IAAI,IAAI,WAAW;oBAC7B,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;iBACxC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC;oBACN,MAAM,EAAE,uBAAuB;oBAC/B,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,cAAc,IAAI,yEAAyE;oBACpG,QAAQ,EAAE,IAAI;oBACd,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,CAAC,QAAQ,CAAC;iBACpB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { MCPTool } from '../core/rule.js';
2
+ export declare function serializeTools(tools: MCPTool[]): string;
@@ -0,0 +1,17 @@
1
+ export function serializeTools(tools) {
2
+ const sorted = [...tools].sort((left, right) => left.name.localeCompare(right.name));
3
+ const shaped = sorted.map((tool) => {
4
+ const record = { name: tool.name };
5
+ if (tool.title)
6
+ record.title = tool.title;
7
+ if (tool.description)
8
+ record.description = tool.description;
9
+ record.inputSchema = tool.inputSchema ?? {};
10
+ if (tool.annotations && Object.keys(tool.annotations).length > 0) {
11
+ record.annotations = tool.annotations;
12
+ }
13
+ return record;
14
+ });
15
+ return `${JSON.stringify(shaped, null, 2)}\n`;
16
+ }
17
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../src/snapshot/serialize.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACjC,MAAM,MAAM,GAA4B,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5D,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1C,IAAI,IAAI,CAAC,WAAW;YAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACxC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC"}
@@ -15,7 +15,7 @@ jobs:
15
15
  - uses: actions/checkout@v4
16
16
 
17
17
  - name: Lint MCP tool schemas
18
- uses: robert19001-cmyk/mcp-lint@v0.6.0
18
+ uses: robert19001-cmyk/mcp-lint@v0.7.0
19
19
  with:
20
20
  input: tools.json
21
21
  severity: warning
@@ -0,0 +1,22 @@
1
+ [
2
+ {
3
+ "name": "search files",
4
+ "description": "Search the workspace.",
5
+ "inputSchema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "q": { "type": "string", "description": "Text to find" }
9
+ }
10
+ }
11
+ },
12
+ {
13
+ "name": "repo/read",
14
+ "description": "Read one file.",
15
+ "inputSchema": { "type": "object", "properties": {} }
16
+ },
17
+ {
18
+ "name": "9tools",
19
+ "description": "List tools.",
20
+ "inputSchema": { "type": "object", "properties": {} }
21
+ }
22
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-lint",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "mcpName": "io.github.robert19001-cmyk/mcp-lint",
5
5
  "description": "Lint MCP server tool schemas for cross-client compatibility, runtime preflight, and agent recovery from tool failures",
6
6
  "type": "module",
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/robert19001-cmyk/mcp-lint/issues"
13
13
  },
14
- "homepage": "https://github.com/robert19001-cmyk/mcp-lint#readme",
14
+ "homepage": "https://mcp-lint-alpha.vercel.app/",
15
15
  "bin": {
16
16
  "mcp-lint": "dist/index.js",
17
17
  "mcp-lint-server": "dist/mcp-server.js"