validate-schemas.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /**
  2. * validate-schemas.js
  3. * 构建后校验所有 MCP 工具的 inputSchema,检测常见 JSON Schema 错误:
  4. * - array 类型缺少 items
  5. * - object 类型缺少 properties
  6. * - enum 为空数组
  7. * - type 字段缺失
  8. */
  9. const path = require('path');
  10. const fs = require('fs');
  11. // 颜色
  12. const RED = '\x1b[31m';
  13. const YELLOW = '\x1b[33m';
  14. const GREEN = '\x1b[32m';
  15. const RESET = '\x1b[0m';
  16. let errors = 0;
  17. let warnings = 0;
  18. function validateSchema(schema, context, toolName) {
  19. if (!schema || typeof schema !== 'object') return;
  20. // 检查 array 缺少 items
  21. if (schema.type === 'array' && !schema.items) {
  22. console.error(`${RED}ERROR${RESET} [${toolName}] ${context}: type "array" missing "items"`);
  23. errors++;
  24. }
  25. // 检查 items 但不是 array
  26. if (schema.items && schema.type !== 'array') {
  27. console.warn(`${YELLOW}WARN${RESET} [${toolName}] ${context}: has "items" but type is "${schema.type}", not "array"`);
  28. warnings++;
  29. }
  30. // 检查 enum 为空
  31. if (schema.enum && Array.isArray(schema.enum) && schema.enum.length === 0) {
  32. console.warn(`${YELLOW}WARN${RESET} [${toolName}] ${context}: empty "enum" array`);
  33. warnings++;
  34. }
  35. // 递归检查 properties
  36. if (schema.properties && typeof schema.properties === 'object') {
  37. for (const [key, value] of Object.entries(schema.properties)) {
  38. validateSchema(value, `${context}.${key}`, toolName);
  39. }
  40. }
  41. // 递归检查 items
  42. if (schema.items && typeof schema.items === 'object') {
  43. validateSchema(schema.items, `${context}.items`, toolName);
  44. }
  45. }
  46. function main() {
  47. const distDir = path.join(__dirname, '..', 'dist');
  48. if (!fs.existsSync(distDir)) {
  49. console.error(`${RED}ERROR${RESET} dist/ not found. Run "npm run build" first.`);
  50. process.exit(1);
  51. }
  52. // 收集所有工具定义
  53. const tools = [];
  54. // 1. 加载 CocosTools
  55. try {
  56. const { CocosTools } = require(path.join(distDir, 'tools', 'cocos', 'cocos-tools.js'));
  57. const cocosTools = new CocosTools();
  58. tools.push(...cocosTools.getTools());
  59. } catch (e) {
  60. console.warn(`${YELLOW}WARN${RESET} Could not load CocosTools: ${e.message}`);
  61. }
  62. // 2. 加载独立工具集
  63. const toolFiles = [
  64. 'tools/scene-tools.js',
  65. 'tools/node-tools.js',
  66. 'tools/component-tools.js',
  67. 'tools/prefab-tools.js',
  68. 'tools/project-tools.js',
  69. 'tools/scene-view-tools.js',
  70. 'tools/reference-image-tools.js',
  71. 'tools/preferences-tools.js',
  72. 'tools/asset-advanced-tools.js',
  73. 'tools/debug-tools.js',
  74. 'tools/server-tools.js',
  75. ];
  76. for (const file of toolFiles) {
  77. try {
  78. const fullPath = path.join(distDir, file);
  79. if (!fs.existsSync(fullPath)) continue;
  80. const mod = require(fullPath);
  81. // 找到导出的类并实例化
  82. for (const [, ExportedClass] of Object.entries(mod)) {
  83. if (typeof ExportedClass === 'function' && ExportedClass.prototype && typeof ExportedClass.prototype.getTools === 'function') {
  84. const instance = new ExportedClass();
  85. tools.push(...instance.getTools());
  86. }
  87. }
  88. } catch (e) {
  89. console.warn(`${YELLOW}WARN${RESET} Could not load ${file}: ${e.message}`);
  90. }
  91. }
  92. console.log(`\nValidating ${tools.length} tool schemas...\n`);
  93. for (const tool of tools) {
  94. if (tool.inputSchema) {
  95. validateSchema(tool.inputSchema, 'inputSchema', tool.name);
  96. }
  97. }
  98. console.log('');
  99. if (errors > 0) {
  100. console.error(`${RED}FAILED${RESET}: ${errors} error(s), ${warnings} warning(s)`);
  101. process.exit(1);
  102. } else if (warnings > 0) {
  103. console.log(`${YELLOW}PASSED${RESET} with ${warnings} warning(s)`);
  104. } else {
  105. console.log(`${GREEN}PASSED${RESET}: All schemas valid`);
  106. }
  107. }
  108. main();