@pixel-normal-edit/mcp 2.1.0 → 2.2.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 (132) hide show
  1. package/clear-firebase.js +37 -0
  2. package/domains/art-tree/README.md +134 -0
  3. package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
  4. package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
  5. package/domains/art-tree/index.js +58 -58
  6. package/domains/art-tree/lessons/3d/box-properties.js +38 -0
  7. package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
  8. package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
  9. package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
  10. package/domains/art-tree/lessons/advanced/angles.js +30 -0
  11. package/domains/art-tree/lessons/advanced/distances.js +26 -0
  12. package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
  13. package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
  14. package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
  15. package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
  16. package/domains/art-tree/lessons/basic/circles.js +26 -0
  17. package/domains/art-tree/lessons/basic/composition.js +34 -0
  18. package/domains/art-tree/lessons/basic/curves.js +31 -0
  19. package/domains/art-tree/lessons/basic/ellipses.js +26 -0
  20. package/domains/art-tree/lessons/basic/freehand.js +19 -0
  21. package/domains/art-tree/lessons/basic/index.js +36 -0
  22. package/domains/art-tree/lessons/basic/lines.js +38 -0
  23. package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
  24. package/domains/art-tree/lessons/basic/polygons.js +25 -0
  25. package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
  26. package/domains/art-tree/lessons/basic/spiral.js +21 -0
  27. package/domains/art-tree/lessons/basic/squares.js +28 -0
  28. package/domains/art-tree/lessons/basic/strokes.js +23 -0
  29. package/domains/art-tree/lessons/basic/triangles.js +30 -0
  30. package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
  31. package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
  32. package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
  33. package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
  34. package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
  35. package/domains/art-tree/lessons/curves/curve-types.js +44 -0
  36. package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
  37. package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
  38. package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
  39. package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
  40. package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
  41. package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
  42. package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
  43. package/domains/art-tree/lessons/index.js +42 -0
  44. package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
  45. package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
  46. package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
  47. package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
  48. package/domains/art-tree/lessons/materials/material-glass.js +21 -0
  49. package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
  50. package/domains/art-tree/lessons/materials/material-texture.js +26 -0
  51. package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
  52. package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
  53. package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
  54. package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
  55. package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
  56. package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
  57. package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
  58. package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
  59. package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
  60. package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
  61. package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
  62. package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
  63. package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
  64. package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
  65. package/domains/art-tree/lessons/transforms/bend.js +27 -0
  66. package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
  67. package/domains/art-tree/lessons/transforms/cut.js +25 -0
  68. package/domains/art-tree/lessons/transforms/hollow.js +24 -0
  69. package/domains/art-tree/lessons/transforms/rotate.js +16 -0
  70. package/domains/art-tree/lessons/transforms/squash.js +31 -0
  71. package/domains/art-tree/lessons/transforms/stretch.js +31 -0
  72. package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
  73. package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
  74. package/domains/art-tree/lib/tool-register.js +104 -0
  75. package/domains/art-tree/lib/validators.js +78 -0
  76. package/domains/art-tree/shapes/grid.js +37 -0
  77. package/domains/art-tree/shapes/polyline.js +26 -0
  78. package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
  79. package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
  80. package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
  81. package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
  82. package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
  83. package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
  84. package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
  85. package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
  86. package/domains/art-tree/tools/index.js +53 -0
  87. package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
  88. package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
  89. package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
  90. package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
  91. package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
  92. package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
  93. package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
  94. package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
  95. package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
  96. package/package.json +1 -1
  97. package/rules/prerequisites.js +30 -30
  98. package/rules/rules.js +1 -1
  99. package/rules/workflow.js +28 -20
  100. package/tools/canvas-tools.js +2 -2
  101. package/tools/drawing-tools.js +23 -12
  102. package/tools/workspace-tools.js +5 -4
  103. package/domains/art-tree/3d_lessons.js +0 -114
  104. package/domains/art-tree/advanced_lessons.js +0 -124
  105. package/domains/art-tree/cross_section_lessons.js +0 -65
  106. package/domains/art-tree/curve_lessons.js +0 -138
  107. package/domains/art-tree/ellipse_lessons.js +0 -105
  108. package/domains/art-tree/hidden_lessons.js +0 -55
  109. package/domains/art-tree/lessons.js +0 -305
  110. package/domains/art-tree/light_lessons.js +0 -92
  111. package/domains/art-tree/material_lessons.js +0 -71
  112. package/domains/art-tree/perspective_lessons.js +0 -138
  113. package/domains/art-tree/sky_lessons_1.js +0 -82
  114. package/domains/art-tree/surface_lessons.js +0 -112
  115. package/domains/art-tree/transform_lessons_1.js +0 -108
  116. /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
  117. /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
  118. /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
  119. /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
  120. /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
  121. /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
  122. /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
  123. /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
  124. /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
  125. /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
  126. /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
  127. /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
  128. /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
  129. /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
  130. /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
  131. /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
  132. /package/domains/art-tree/{vocab_shapes.js → shapes/vocab.js} +0 -0
@@ -1,53 +1,7 @@
1
- const shapes = require('./shapes');
2
- const transformShapes = require('./transform_shapes');
3
- const structureLessons = require('./structure_lessons');
4
-
5
- /**
6
- * Lesson: Rotate (Rotation)
7
- * Analyzing how objects change when rotated around X, Y, Z axes.
8
- * We can reuse the Axis Orientation lesson as it perfectly demonstrates rotation.
9
- */
10
- function lessonRotate(canvasSize = 32, color = '#ff5722') {
11
- // Reusing the axis orientation lesson which draws cylinders rotated along X, Y, Z
12
- return structureLessons.lessonAxisOrientation(canvasSize, color);
13
- }
14
-
15
1
  /**
16
- * Lesson: Cut (Slice a form)
17
- * Sphere -> Truncated Sphere (exposing internal cross-section)
2
+ * Auto-extracted from transform_lessons_2.js
18
3
  */
19
- function lessonCut(canvasSize = 32, color = '#8bc34a') {
20
- const commands = [];
21
- const cx = Math.floor(canvasSize / 2);
22
- const cy = Math.floor(canvasSize / 2);
23
- const r = Math.floor(canvasSize * 0.35);
24
-
25
- // Original sphere (ghost)
26
- commands.push(shapes.drawCircle(cx, cy, r, '#bdbdbd', false));
27
-
28
- // Cut sphere
29
- commands.push(...transformShapes.drawTruncatedSphere(cx, cy, r, color));
30
-
31
- return commands;
32
- }
33
-
34
- /**
35
- * Lesson: Hollow (Carve interior)
36
- * Cylinder -> Pipe/Hole
37
- */
38
- function lessonHollow(canvasSize = 32, color = '#3f51b5') {
39
- const commands = [];
40
- const cx = Math.floor(canvasSize / 2);
41
- const cy = Math.floor(canvasSize / 2);
42
- const outerR = Math.floor(canvasSize * 0.35);
43
- const innerR = Math.floor(canvasSize * 0.25);
44
- const h = Math.floor(canvasSize * 0.6);
45
-
46
- // Hollow cylinder
47
- commands.push(...transformShapes.drawHollowCylinder(cx, cy, outerR, innerR, h, color));
48
-
49
- return commands;
50
- }
4
+ const shapes = require('../../shapes');
51
5
 
52
6
  /**
53
7
  * Lesson: Combine (Merge forms)
@@ -94,9 +48,4 @@ function lessonCombine(canvasSize = 32, color = '#607d8b') {
94
48
  return commands;
95
49
  }
96
50
 
97
- module.exports = {
98
- lessonRotate,
99
- lessonCut,
100
- lessonHollow,
101
- lessonCombine
102
- };
51
+ module.exports = { lessonCombine };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Cut (Slice a form)
8
+ * Sphere -> Truncated Sphere (exposing internal cross-section)
9
+ */
10
+ function lessonCut(canvasSize = 32, color = '#8bc34a') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const r = Math.floor(canvasSize * 0.35);
15
+
16
+ // Original sphere (ghost)
17
+ commands.push(shapes.drawCircle(cx, cy, r, '#bdbdbd', false));
18
+
19
+ // Cut sphere
20
+ commands.push(...transformShapes.drawTruncatedSphere(cx, cy, r, color));
21
+
22
+ return commands;
23
+ }
24
+
25
+ module.exports = { lessonCut };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Hollow (Carve interior)
8
+ * Cylinder -> Pipe/Hole
9
+ */
10
+ function lessonHollow(canvasSize = 32, color = '#3f51b5') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const outerR = Math.floor(canvasSize * 0.35);
15
+ const innerR = Math.floor(canvasSize * 0.25);
16
+ const h = Math.floor(canvasSize * 0.6);
17
+
18
+ // Hollow cylinder
19
+ commands.push(...transformShapes.drawHollowCylinder(cx, cy, outerR, innerR, h, color));
20
+
21
+ return commands;
22
+ }
23
+
24
+ module.exports = { lessonHollow };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Rotate (Rotation)
8
+ * Analyzing how objects change when rotated around X, Y, Z axes.
9
+ * We can reuse the Axis Orientation lesson as it perfectly demonstrates rotation.
10
+ */
11
+ function lessonRotate(canvasSize = 32, color = '#ff5722') {
12
+ // Reusing the axis orientation lesson which draws cylinders rotated along X, Y, Z
13
+ return structureLessons.lessonAxisOrientation(canvasSize, color);
14
+ }
15
+
16
+ module.exports = { lessonRotate };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Squash (Compress)
8
+ * Sphere -> Disk, Box -> Plate
9
+ */
10
+ function lessonSquash(canvasSize = 32, color = '#ff9800') {
11
+ const commands = [];
12
+ const qX1 = Math.floor(canvasSize * 0.25);
13
+ const qX2 = Math.floor(canvasSize * 0.75);
14
+ const cy = Math.floor(canvasSize / 2);
15
+ const r = Math.floor(canvasSize * 0.15);
16
+
17
+ // 1. Squash: Sphere -> Disk
18
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original
19
+ // Squashed into a flat ellipse/disk
20
+ commands.push(shapes.drawEllipse(qX1, cy, r * 1.2, Math.floor(r * 0.3), color, false));
21
+
22
+ // 2. Squash: Box -> Plate
23
+ const w = Math.floor(canvasSize * 0.2);
24
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false)); // Original
25
+ // Squashed into plate
26
+ commands.push(shapes.drawRectangle(qX2 - w, cy - Math.floor(w*0.2), w * 2, Math.floor(w*0.4), '#4caf50', false));
27
+
28
+ return commands;
29
+ }
30
+
31
+ module.exports = { lessonSquash };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Stretch (Elongate)
8
+ * Sphere -> Egg, Box -> Bar
9
+ */
10
+ function lessonStretch(canvasSize = 32, color = '#2196f3') {
11
+ const commands = [];
12
+ const qX1 = Math.floor(canvasSize * 0.25);
13
+ const qX2 = Math.floor(canvasSize * 0.75);
14
+ const cy = Math.floor(canvasSize / 2);
15
+ const r = Math.floor(canvasSize * 0.15);
16
+
17
+ // 1. Stretch: Sphere -> Egg
18
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original sphere (ghost)
19
+ commands.push(...transformShapes.drawEgg(qX1, cy, r, color)); // Stretched into egg
20
+
21
+ // 2. Stretch: Box -> Bar (Tall)
22
+ const w = Math.floor(canvasSize * 0.15);
23
+ // Original box (ghost)
24
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false));
25
+ // Stretched bar
26
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w * 1.5, w, w * 3, '#9c27b0', false));
27
+
28
+ return commands;
29
+ }
30
+
31
+ module.exports = { lessonStretch };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Taper & Swell (Narrow or bulge)
8
+ * Cylinder -> Bottle
9
+ */
10
+ function lessonTaperSwell(canvasSize = 32, color = '#00bcd4') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const totalH = Math.floor(canvasSize * 0.8);
14
+ const topY = Math.floor(canvasSize * 0.1);
15
+ const bottomY = topY + totalH;
16
+ const r = Math.floor(canvasSize * 0.25);
17
+
18
+ // Original Cylinder (ghost)
19
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, bottomY, '#bdbdbd'));
20
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, bottomY, '#bdbdbd'));
21
+
22
+ // Tapered and Swelled form (Bottle)
23
+ const sections = [
24
+ { y: topY, r: Math.floor(r * 0.3) }, // Neck (Tapered)
25
+ { y: topY + Math.floor(totalH * 0.4), r: r },// Body (Swelled back to original radius)
26
+ { y: bottomY, r: Math.floor(r * 0.8) } // Base (Slightly tapered)
27
+ ];
28
+ commands.push(...csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true));
29
+
30
+ return commands;
31
+ }
32
+
33
+ module.exports = { lessonTaperSwell };
@@ -1,4 +1,7 @@
1
- const vocabShapes = require('./vocab_shapes');
1
+ /**
2
+ * Auto-extracted from vocab_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
2
5
 
3
6
  /**
4
7
  * Lesson: Visual Vocabulary - House Breakdown
@@ -15,18 +18,4 @@ function lessonVocabHouse(canvasSize = 64, step = 1) {
15
18
  return vocabShapes.drawHouseBreakdown(cx, cy, canvasSize, validStep);
16
19
  }
17
20
 
18
- function getVocabLessonCatalog() {
19
- return [
20
- {
21
- id: 'vocab_house',
22
- name: 'House Breakdown (5 Steps)',
23
- description: 'Break down a house: Masses -> Structural -> Functional -> Identity -> Materials. Supply step=1..5',
24
- fn: lessonVocabHouse
25
- }
26
- ];
27
- }
28
-
29
- module.exports = {
30
- lessonVocabHouse,
31
- getVocabLessonCatalog
32
- };
21
+ module.exports = { lessonVocabHouse };
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * tool-register.js — Art Tree: Tool Registration Helpers
4
+ *
5
+ * Provides helper functions to reduce boilerplate when registering
6
+ * Art Tree MCP tools. Common patterns are abstracted here.
7
+ */
8
+
9
+ const { z } = require('zod');
10
+ const { registerTool, sendCommand } = require('../../core/server');
11
+ const { hexColor, canvasSize } = require('./validators');
12
+
13
+ /**
14
+ * Register a lesson tool with standard pattern
15
+ * @param {McpServer} server - MCP server instance
16
+ * @param {string} name - Tool name
17
+ * @param {string} description - Tool description
18
+ * @param {Function} lessonFn - Function that returns array of commands
19
+ * @param {Object} [options] - Optional configuration
20
+ * @param {number} [options.canvasSizeMax=64] - Max canvas size
21
+ * @param {string} [options.defaultColor] - Default color (uses hexColor default if not provided)
22
+ */
23
+ function registerLessonTool(server, name, description, lessonFn, options = {}) {
24
+ const { canvasSizeMax = 64, defaultColor } = options;
25
+
26
+ const schema = {
27
+ canvasSize: canvasSize.max(canvasSizeMax),
28
+ };
29
+
30
+ if (defaultColor) {
31
+ schema.color = hexColor.default(defaultColor);
32
+ }
33
+
34
+ registerTool(server, name, description, schema, async (params) => {
35
+ const commands = lessonFn(params.canvasSize, params.color || defaultColor);
36
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
37
+ return {
38
+ content: [{
39
+ type: 'text',
40
+ text: `✓ ${description.split(' — ')[0]} completed (${commands.length} strokes)`
41
+ }]
42
+ };
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Register a catalog tool that returns a list of available lessons
48
+ * @param {McpServer} server - MCP server instance
49
+ * @param {string} name - Tool name
50
+ * @param {string} description - Tool description
51
+ * @param {Function} getCatalogFn - Function that returns catalog array
52
+ * @param {string} catalogTitle - Title for the catalog output
53
+ */
54
+ function registerCatalogTool(server, name, description, getCatalogFn, catalogTitle) {
55
+ registerTool(server, name, description, {}, () => {
56
+ const catalog = getCatalogFn();
57
+ const text = catalog.map(l =>
58
+ ` • ${l.id}: ${l.name} — ${l.description}`
59
+ ).join('\n');
60
+ return { content: [{ type: 'text', text: `📚 ${catalogTitle}:\n\n${text}` }] };
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Register a tool that executes a specific lesson step
66
+ * @param {McpServer} server - MCP server instance
67
+ * @param {string} name - Tool name
68
+ * @param {string} description - Tool description
69
+ * @param {Function} lessonStepFn - Function that takes step number and returns commands
70
+ * @param {Object} [options] - Optional configuration
71
+ * @param {number} [options.minStep=1] - Minimum step number
72
+ * @param {number} [options.maxStep=10] - Maximum step number
73
+ * @param {number} [options.canvasSizeMax=64] - Max canvas size
74
+ * @param {string} [options.defaultColor] - Default color
75
+ */
76
+ function registerStepTool(server, name, description, lessonStepFn, options = {}) {
77
+ const { minStep = 1, maxStep = 10, canvasSizeMax = 64, defaultColor } = options;
78
+
79
+ const schema = {
80
+ canvasSize: canvasSize.max(canvasSizeMax),
81
+ step: z.number().int().min(minStep).max(maxStep).describe('Step number'),
82
+ };
83
+
84
+ if (defaultColor) {
85
+ schema.color = hexColor.default(defaultColor);
86
+ }
87
+
88
+ registerTool(server, name, description, schema, async (params) => {
89
+ const commands = lessonStepFn(params.canvasSize, params.step, params.color || defaultColor);
90
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
91
+ return {
92
+ content: [{
93
+ type: 'text',
94
+ text: `✓ ${description.split(' — ')[0]} - Step ${params.step} completed`
95
+ }]
96
+ };
97
+ });
98
+ }
99
+
100
+ module.exports = {
101
+ registerLessonTool,
102
+ registerCatalogTool,
103
+ registerStepTool,
104
+ };
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * validators.js — Art Tree: Shared Zod Validators
4
+ *
5
+ * Common validation schemas used across all Art Tree tools.
6
+ * Centralizes repeated patterns to reduce code duplication.
7
+ */
8
+
9
+ const { z } = require('zod');
10
+
11
+ /**
12
+ * Hex color validator - ensures valid 6-character hex color codes
13
+ */
14
+ const hexColor = z.string()
15
+ .regex(/^#[0-9a-fA-F]{6}$/)
16
+ .describe('Hex color e.g. #ff0000');
17
+
18
+ /**
19
+ * Canvas size validator - ensures reasonable canvas dimensions
20
+ */
21
+ const canvasSize = z.number()
22
+ .int()
23
+ .min(16)
24
+ .max(128)
25
+ .default(32)
26
+ .describe('Canvas dimension (16-128, default 32)');
27
+
28
+ /**
29
+ * Coordinate validator - ensures integer coordinates
30
+ */
31
+ const coordinate = z.number()
32
+ .int()
33
+ .describe('Integer coordinate');
34
+
35
+ /**
36
+ * Positive integer validator - for sizes, counts, etc.
37
+ */
38
+ const positiveInt = z.number()
39
+ .int()
40
+ .min(1)
41
+ .describe('Positive integer (min 1)');
42
+
43
+ /**
44
+ * Radius validator - ensures positive radius
45
+ */
46
+ const radius = z.number()
47
+ .int()
48
+ .min(1)
49
+ .describe('Radius (min 1)');
50
+
51
+ /**
52
+ * Boolean with default false
53
+ */
54
+ const booleanFalse = z.boolean()
55
+ .default(false)
56
+ .describe('Boolean flag (default false)');
57
+
58
+ /**
59
+ * Common tool parameter schemas
60
+ */
61
+ const schemas = {
62
+ hexColor,
63
+ canvasSize,
64
+ coordinate,
65
+ positiveInt,
66
+ radius,
67
+ booleanFalse,
68
+ };
69
+
70
+ module.exports = {
71
+ hexColor,
72
+ canvasSize,
73
+ coordinate,
74
+ positiveInt,
75
+ radius,
76
+ booleanFalse,
77
+ schemas,
78
+ };
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * shapes/grid.js — Art Tree: Grid Shape Primitives
4
+ *
5
+ * Grid-related shape drawing functions.
6
+ */
7
+
8
+ const { drawLine } = require('./basic');
9
+
10
+ /**
11
+ * Draw a grid of squares (useful for pixel art foundation lessons).
12
+ * @param {number} startX - Top-left X
13
+ * @param {number} startY - Top-left Y
14
+ * @param {number} cols - Number of columns
15
+ * @param {number} rows - Number of rows
16
+ * @param {number} cellSize - Size of each cell
17
+ * @param {string} color - Grid line color
18
+ * @returns {Array<Object>} Array of command payloads
19
+ */
20
+ function drawGrid(startX, startY, cols, rows, cellSize, color) {
21
+ const commands = [];
22
+ // Horizontal lines
23
+ for (let r = 0; r <= rows; r++) {
24
+ const y = startY + r * cellSize;
25
+ commands.push(drawLine(startX, y, startX + cols * cellSize, y, color));
26
+ }
27
+ // Vertical lines
28
+ for (let c = 0; c <= cols; c++) {
29
+ const x = startX + c * cellSize;
30
+ commands.push(drawLine(x, startY, x, startY + rows * cellSize, color));
31
+ }
32
+ return commands;
33
+ }
34
+
35
+ module.exports = {
36
+ drawGrid,
37
+ };
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * shapes/polyline.js — Art Tree: Polyline Shape Primitives
4
+ *
5
+ * Polyline-related shape drawing functions.
6
+ */
7
+
8
+ const { drawLine } = require('./basic');
9
+
10
+ /**
11
+ * Draw a continuous polyline from a list of points.
12
+ * @param {Array<{x: number, y: number}>} points - Array of vertices
13
+ * @param {string} color - Hex color
14
+ * @returns {Array<Object>} Array of command payloads
15
+ */
16
+ function drawPolyline(points, color) {
17
+ const commands = [];
18
+ for (let i = 0; i < points.length - 1; i++) {
19
+ commands.push(drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color));
20
+ }
21
+ return commands;
22
+ }
23
+
24
+ module.exports = {
25
+ drawPolyline,
26
+ };
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const lessons3d = require('./3d_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const lessons3d = require('../lessons/3d');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const advLessons = require('./advanced_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const advLessons = require('../lessons/advanced');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const analysisLessons = require('./analysis_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const analysisLessons = require('../lessons/analysis');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -9,9 +9,9 @@
9
9
  * sendCommand — they do NOT create their own drawing system.
10
10
  */
11
11
  const { z } = require('zod');
12
- const { registerTool, sendCommand } = require('../../core/server');
13
- const shapes = require('./shapes');
14
- const lessons = require('./lessons');
12
+ const { registerTool, sendCommand } = require('../../../core/server');
13
+ const shapes = require('../shapes/basic');
14
+ const lessons = require('../lessons/basic');
15
15
 
16
16
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
17
17
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const csLessons = require('./cross_section_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const csLessons = require('../lessons/cross_section');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const curveLessons = require('./curve_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const curveLessons = require('../lessons/curve');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const ellipseLessons = require('./ellipse_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const ellipseLessons = require('../lessons/ellipse');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const hiddenLessons = require('./hidden_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const hiddenLessons = require('../lessons/hidden');
4
4
 
5
5
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
6
 
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * tools/index.js — Art Tree: MCP Tools Index
4
+ *
5
+ * Main entry point for registering all Art Tree MCP tools.
6
+ * Imports and registers all tool categories.
7
+ */
8
+
9
+ const basic = require('./basic');
10
+ const lessons = require('./lessons');
11
+ const advanced = require('./advanced');
12
+ const curves = require('./curves');
13
+ const ellipses = require('./ellipses');
14
+ const forms3d = require('./3d');
15
+ const structure = require('./structure');
16
+ const crossSections = require('./cross-sections');
17
+ const transforms = require('./transforms');
18
+ const surfaces = require('./surfaces');
19
+ const perspective = require('./perspective');
20
+ const hidden = require('./hidden');
21
+ const lighting = require('./lighting');
22
+ const materials = require('./materials');
23
+ const analysis = require('./analysis');
24
+ const layers = require('./layers');
25
+ const sky = require('./sky');
26
+
27
+ /**
28
+ * Register all Art Tree MCP tools on the server
29
+ * @param {McpServer} server
30
+ */
31
+ function registerAll(server) {
32
+ basic.register(server);
33
+ lessons.register(server);
34
+ advanced.register(server);
35
+ curves.register(server);
36
+ ellipses.register(server);
37
+ forms3d.register(server);
38
+ structure.register(server);
39
+ crossSections.register(server);
40
+ transforms.register(server);
41
+ surfaces.register(server);
42
+ perspective.register(server);
43
+ hidden.register(server);
44
+ lighting.register(server);
45
+ materials.register(server);
46
+ analysis.register(server);
47
+ layers.register(server);
48
+ sky.register(server);
49
+ }
50
+
51
+ module.exports = {
52
+ registerAll,
53
+ };
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const layerLessons = require('./layer_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const layerLessons = require('../lessons/layer');
4
4
 
5
5
  /**
6
6
  * Register all layer tools on the MCP server.
@@ -1,6 +1,6 @@
1
1
  const { z } = require('zod');
2
- const { registerTool, sendCommand } = require('../../core/server');
3
- const lightLessons = require('./light_lessons');
2
+ const { registerTool, sendCommand } = require('../../../core/server');
3
+ const lightLessons = require('../lessons/light');
4
4
 
5
5
  /**
6
6
  * Register all lighting & shading tools on the MCP server.