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.
- package/README.md +60 -2
- package/dist/cli/commands/check.js +123 -31
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/snapshot.d.ts +2 -0
- package/dist/cli/commands/snapshot.js +56 -0
- package/dist/cli/commands/snapshot.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/installed/config.d.ts +34 -0
- package/dist/installed/config.js +130 -0
- package/dist/installed/config.js.map +1 -0
- package/dist/installed/format.d.ts +2 -0
- package/dist/installed/format.js +72 -0
- package/dist/installed/format.js.map +1 -0
- package/dist/installed/lint.d.ts +19 -0
- package/dist/installed/lint.js +49 -0
- package/dist/installed/lint.js.map +1 -0
- package/dist/loaders/index.d.ts +1 -0
- package/dist/loaders/index.js +1 -0
- package/dist/loaders/index.js.map +1 -1
- package/dist/loaders/map-tools.d.ts +16 -0
- package/dist/loaders/map-tools.js +33 -0
- package/dist/loaders/map-tools.js.map +1 -0
- package/dist/loaders/remote-loader.d.ts +2 -0
- package/dist/loaders/remote-loader.js +37 -0
- package/dist/loaders/remote-loader.js.map +1 -0
- package/dist/loaders/sse-loader.d.ts +1 -1
- package/dist/loaders/sse-loader.js +3 -17
- package/dist/loaders/sse-loader.js.map +1 -1
- package/dist/loaders/stdio-loader.d.ts +8 -1
- package/dist/loaders/stdio-loader.js +58 -12
- package/dist/loaders/stdio-loader.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/recover/probe.js +1 -1
- package/dist/rules/index.js +2 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/tool-name.d.ts +2 -0
- package/dist/rules/tool-name.js +44 -0
- package/dist/rules/tool-name.js.map +1 -0
- package/dist/snapshot/serialize.d.ts +2 -0
- package/dist/snapshot/serialize.js +17 -0
- package/dist/snapshot/serialize.js.map +1 -0
- package/examples/github-action.yml +1 -1
- package/examples/names/unsafe.json +22 -0
- 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"}
|
package/dist/loaders/index.d.ts
CHANGED
package/dist/loaders/index.js
CHANGED
|
@@ -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,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 {
|
|
2
|
-
|
|
3
|
-
|
|
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":"
|
|
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
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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;
|
|
1
|
+
{"version":3,"file":"stdio-loader.js","sourceRoot":"","sources":["../../src/loaders/stdio-loader.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -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.
|
|
16
|
+
const VERSION = '0.7.0';
|
|
17
17
|
const ClientIdSchema = z.enum([
|
|
18
18
|
'claude',
|
|
19
19
|
'cursor',
|
package/dist/recover/probe.js
CHANGED
|
@@ -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.
|
|
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);
|
package/dist/rules/index.js
CHANGED
|
@@ -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
|
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;
|
|
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,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,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"}
|
|
@@ -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.
|
|
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://
|
|
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"
|