mcp-lint 0.6.2 → 0.8.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 (53) hide show
  1. package/README.md +59 -3
  2. package/dist/cli/commands/check.js +160 -32
  3. package/dist/cli/commands/check.js.map +1 -1
  4. package/dist/cli/commands/init.js +1 -2
  5. package/dist/cli/commands/init.js.map +1 -1
  6. package/dist/cli/commands/snapshot.d.ts +2 -0
  7. package/dist/cli/commands/snapshot.js +56 -0
  8. package/dist/cli/commands/snapshot.js.map +1 -0
  9. package/dist/discover/files.d.ts +3 -0
  10. package/dist/discover/files.js +35 -0
  11. package/dist/discover/files.js.map +1 -0
  12. package/dist/discover/format.d.ts +6 -0
  13. package/dist/discover/format.js +20 -0
  14. package/dist/discover/format.js.map +1 -0
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/installed/config.d.ts +34 -0
  18. package/dist/installed/config.js +130 -0
  19. package/dist/installed/config.js.map +1 -0
  20. package/dist/installed/format.d.ts +2 -0
  21. package/dist/installed/format.js +72 -0
  22. package/dist/installed/format.js.map +1 -0
  23. package/dist/installed/lint.d.ts +19 -0
  24. package/dist/installed/lint.js +49 -0
  25. package/dist/installed/lint.js.map +1 -0
  26. package/dist/loaders/index.d.ts +1 -0
  27. package/dist/loaders/index.js +1 -0
  28. package/dist/loaders/index.js.map +1 -1
  29. package/dist/loaders/map-tools.d.ts +16 -0
  30. package/dist/loaders/map-tools.js +33 -0
  31. package/dist/loaders/map-tools.js.map +1 -0
  32. package/dist/loaders/remote-loader.d.ts +2 -0
  33. package/dist/loaders/remote-loader.js +37 -0
  34. package/dist/loaders/remote-loader.js.map +1 -0
  35. package/dist/loaders/sse-loader.d.ts +1 -1
  36. package/dist/loaders/sse-loader.js +3 -17
  37. package/dist/loaders/sse-loader.js.map +1 -1
  38. package/dist/loaders/stdio-loader.d.ts +8 -1
  39. package/dist/loaders/stdio-loader.js +58 -12
  40. package/dist/loaders/stdio-loader.js.map +1 -1
  41. package/dist/mcp-server.js +1 -1
  42. package/dist/recover/probe.js +1 -1
  43. package/dist/rules/index.js +2 -0
  44. package/dist/rules/index.js.map +1 -1
  45. package/dist/rules/tool-name.d.ts +2 -0
  46. package/dist/rules/tool-name.js +44 -0
  47. package/dist/rules/tool-name.js.map +1 -0
  48. package/dist/snapshot/serialize.d.ts +2 -0
  49. package/dist/snapshot/serialize.js +17 -0
  50. package/dist/snapshot/serialize.js.map +1 -0
  51. package/examples/github-action.yml +1 -1
  52. package/examples/names/unsafe.json +22 -0
  53. package/package.json +7 -3
@@ -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.2
18
+ uses: robert19001-cmyk/mcp-lint@v0.8.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,8 +1,8 @@
1
1
  {
2
2
  "name": "mcp-lint",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "mcpName": "io.github.robert19001-cmyk/mcp-lint",
5
- "description": "Lint MCP server tool schemas for cross-client compatibility, runtime preflight, and agent recovery from tool failures",
5
+ "description": "Lint MCP tool schemas before they break Claude, Cursor, Gemini, or OpenAI. Run npx mcp-lint.",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -62,7 +62,11 @@
62
62
  "recover",
63
63
  "agent-safety",
64
64
  "policy",
65
- "guardrails"
65
+ "guardrails",
66
+ "claude",
67
+ "cursor",
68
+ "openai",
69
+ "gemini"
66
70
  ],
67
71
  "license": "MIT",
68
72
  "engines": {