@pixel-normal-edit/mcp 2.0.10 → 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 (136) hide show
  1. package/clear-firebase.js +37 -0
  2. package/core/command-bus.js +6 -3
  3. package/core/server.js +98 -3
  4. package/domains/art-tree/README.md +134 -0
  5. package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
  6. package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
  7. package/domains/art-tree/index.js +58 -58
  8. package/domains/art-tree/lessons/3d/box-properties.js +38 -0
  9. package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
  10. package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
  11. package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
  12. package/domains/art-tree/lessons/advanced/angles.js +30 -0
  13. package/domains/art-tree/lessons/advanced/distances.js +26 -0
  14. package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
  15. package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
  16. package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
  17. package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
  18. package/domains/art-tree/lessons/basic/circles.js +26 -0
  19. package/domains/art-tree/lessons/basic/composition.js +34 -0
  20. package/domains/art-tree/lessons/basic/curves.js +31 -0
  21. package/domains/art-tree/lessons/basic/ellipses.js +26 -0
  22. package/domains/art-tree/lessons/basic/freehand.js +19 -0
  23. package/domains/art-tree/lessons/basic/index.js +36 -0
  24. package/domains/art-tree/lessons/basic/lines.js +38 -0
  25. package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
  26. package/domains/art-tree/lessons/basic/polygons.js +25 -0
  27. package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
  28. package/domains/art-tree/lessons/basic/spiral.js +21 -0
  29. package/domains/art-tree/lessons/basic/squares.js +28 -0
  30. package/domains/art-tree/lessons/basic/strokes.js +23 -0
  31. package/domains/art-tree/lessons/basic/triangles.js +30 -0
  32. package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
  33. package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
  34. package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
  35. package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
  36. package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
  37. package/domains/art-tree/lessons/curves/curve-types.js +44 -0
  38. package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
  39. package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
  40. package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
  41. package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
  42. package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
  43. package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
  44. package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
  45. package/domains/art-tree/lessons/index.js +42 -0
  46. package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
  47. package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
  48. package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
  49. package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
  50. package/domains/art-tree/lessons/materials/material-glass.js +21 -0
  51. package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
  52. package/domains/art-tree/lessons/materials/material-texture.js +26 -0
  53. package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
  54. package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
  55. package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
  56. package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
  57. package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
  58. package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
  59. package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
  60. package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
  61. package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
  62. package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
  63. package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
  64. package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
  65. package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
  66. package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
  67. package/domains/art-tree/lessons/transforms/bend.js +27 -0
  68. package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
  69. package/domains/art-tree/lessons/transforms/cut.js +25 -0
  70. package/domains/art-tree/lessons/transforms/hollow.js +24 -0
  71. package/domains/art-tree/lessons/transforms/rotate.js +16 -0
  72. package/domains/art-tree/lessons/transforms/squash.js +31 -0
  73. package/domains/art-tree/lessons/transforms/stretch.js +31 -0
  74. package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
  75. package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
  76. package/domains/art-tree/lib/tool-register.js +104 -0
  77. package/domains/art-tree/lib/validators.js +78 -0
  78. package/domains/art-tree/shapes/grid.js +37 -0
  79. package/domains/art-tree/shapes/polyline.js +26 -0
  80. package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
  81. package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
  82. package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
  83. package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
  84. package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
  85. package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
  86. package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
  87. package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
  88. package/domains/art-tree/tools/index.js +53 -0
  89. package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
  90. package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
  91. package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
  92. package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
  93. package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
  94. package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
  95. package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
  96. package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
  97. package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
  98. package/package.json +1 -1
  99. package/rules/README.md +358 -285
  100. package/rules/index.js +9 -9
  101. package/rules/prerequisites.js +121 -64
  102. package/rules/rules.js +76 -35
  103. package/rules/workflow.js +270 -56
  104. package/tools/canvas-tools.js +2 -2
  105. package/tools/drawing-tools.js +23 -12
  106. package/tools/workspace-tools.js +5 -4
  107. package/domains/art-tree/3d_lessons.js +0 -114
  108. package/domains/art-tree/advanced_lessons.js +0 -124
  109. package/domains/art-tree/cross_section_lessons.js +0 -65
  110. package/domains/art-tree/curve_lessons.js +0 -138
  111. package/domains/art-tree/ellipse_lessons.js +0 -105
  112. package/domains/art-tree/hidden_lessons.js +0 -55
  113. package/domains/art-tree/lessons.js +0 -305
  114. package/domains/art-tree/light_lessons.js +0 -92
  115. package/domains/art-tree/material_lessons.js +0 -71
  116. package/domains/art-tree/perspective_lessons.js +0 -138
  117. package/domains/art-tree/sky_lessons_1.js +0 -82
  118. package/domains/art-tree/surface_lessons.js +0 -112
  119. package/domains/art-tree/transform_lessons_1.js +0 -108
  120. /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
  121. /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
  122. /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
  123. /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
  124. /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
  125. /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
  126. /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
  127. /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
  128. /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
  129. /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
  130. /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
  131. /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
  132. /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
  133. /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
  134. /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
  135. /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
  136. /package/domains/art-tree/{vocab_shapes.js → shapes/vocab.js} +0 -0
@@ -0,0 +1,37 @@
1
+ require('dotenv').config({ path: require('path').join(__dirname, '../../.env') });
2
+ const { initializeApp } = require('firebase/app');
3
+ const { getFirestore, collection, getDocs, deleteDoc, doc } = require('firebase/firestore');
4
+
5
+ const app = initializeApp({
6
+ apiKey: 'AIzaSyBSrvCt58Jhsh14wbC2bD2KLFUUVbAVim0',
7
+ authDomain: 'pixel-normal-edit.firebaseapp.com',
8
+ projectId: 'pixel-normal-edit',
9
+ storageBucket: 'pixel-normal-edit.firebasestorage.app',
10
+ messagingSenderId: '397075334229',
11
+ appId: '1:397075334229:web:b02eede3fc7b41d02f80dc',
12
+ });
13
+
14
+ const db = getFirestore(app);
15
+
16
+ async function clearAll() {
17
+ console.log('Fetching mcp_sessions...');
18
+ const sessionsSnap = await getDocs(collection(db, 'mcp_sessions'));
19
+ console.log(`Found ${sessionsSnap.size} sessions.`);
20
+
21
+ for (const sessionDoc of sessionsSnap.docs) {
22
+ const sessionId = sessionDoc.id;
23
+ console.log(`Clearing commands for session: ${sessionId}`);
24
+ const cmdsRef = collection(db, 'mcp_sessions', sessionId, 'commands');
25
+ const cmdsSnap = await getDocs(cmdsRef);
26
+ let count = 0;
27
+ for (const cmdDoc of cmdsSnap.docs) {
28
+ await deleteDoc(doc(db, 'mcp_sessions', sessionId, 'commands', cmdDoc.id));
29
+ count++;
30
+ }
31
+ console.log(`Deleted ${count} commands.`);
32
+ }
33
+ console.log('Done!');
34
+ process.exit(0);
35
+ }
36
+
37
+ clearAll().catch(console.error);
@@ -15,11 +15,14 @@ const TIMEOUT = parseInt(process.env.MCP_TIMEOUT || '20000');
15
15
  /**
16
16
  * Send a command payload to the browser via Firestore and wait for response.
17
17
  * @param {Object} payload - The command action and parameters
18
+ * @param {number} timeoutOverride - Optional timeout in milliseconds
18
19
  * @returns {Promise<Object>} MCP-compatible response object
19
20
  */
20
- async function sendCommand(payload) {
21
+ async function sendCommand(payload, timeoutOverride = null) {
21
22
  const id = crypto.randomUUID();
22
23
  const ref = doc(db, 'mcp_sessions', SESSION, 'commands', id);
24
+ const actualTimeout = timeoutOverride || TIMEOUT;
25
+
23
26
  await setDoc(ref, { ...payload, status: 'pending', timestamp: Date.now() });
24
27
 
25
28
  return new Promise((resolve) => {
@@ -27,9 +30,9 @@ async function sendCommand(payload) {
27
30
  unsub();
28
31
  resolve({
29
32
  isError: true,
30
- content: [{ type: 'text', text: `⏱ Timeout (${TIMEOUT}ms). Is the browser tab open with session: ${SESSION}?` }]
33
+ content: [{ type: 'text', text: `⏱ Timeout (${actualTimeout}ms). Is the browser tab open with session: ${SESSION}?` }]
31
34
  });
32
- }, TIMEOUT);
35
+ }, actualTimeout);
33
36
 
34
37
  const unsub = onSnapshot(ref, (snap) => {
35
38
  const d = snap.data();
package/core/server.js CHANGED
@@ -6,7 +6,9 @@
6
6
  * Provides a tool registration factory for consistent tool definitions.
7
7
  */
8
8
  const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
9
- const { sendCommand } = require('./command-bus');
9
+ const { sendCommand, SESSION } = require('./command-bus');
10
+ const workflow = require('../rules/workflow');
11
+ const { DRAWING_STEPS } = require('../rules/rules');
10
12
 
11
13
  /**
12
14
  * Create a new MCP server instance
@@ -16,16 +18,109 @@ function createServer() {
16
18
  return new McpServer({ name: 'PixelNormalEdit', version: '2.0.0' });
17
19
  }
18
20
 
21
+ /**
22
+ * Read-only tools that do not modify the canvas and should bypass strict workflow checks
23
+ */
24
+ const READ_ONLY_TOOLS = [
25
+ 'ping',
26
+ 'layer_get_info',
27
+ 'workspace_list_tabs',
28
+ 'workspace_get_active_tab',
29
+ 'animation_get_info',
30
+ 'anchor_list',
31
+ 'query_snapshot',
32
+ 'query_bounding_box',
33
+ 'query_palette',
34
+ 'query_pixel',
35
+ 'query_export_image',
36
+ 'query_document_state',
37
+ 'workflow_status',
38
+ 'workflow_get_progress'
39
+ ];
40
+
41
+ /**
42
+ * Identify if a tool is a drawing tool that should only be allowed in drawing steps
43
+ */
44
+ function isDrawingTool(name) {
45
+ return name.startsWith('draw_') ||
46
+ name.startsWith('bulk_') ||
47
+ name.startsWith('filter_') ||
48
+ name.startsWith('sprite_') ||
49
+ name.startsWith('region_') ||
50
+ name.startsWith('layer_') ||
51
+ name.startsWith('animation_') ||
52
+ name.startsWith('art_tree_');
53
+ }
54
+
19
55
  /**
20
56
  * Register a single-command tool on the server
21
57
  * @param {McpServer} server - The MCP server instance
22
58
  * @param {string} name - Tool name
23
59
  * @param {string} desc - Tool description
24
60
  * @param {Object} schema - Zod schema for parameters
25
- * @param {Function} mapToCmd - Function mapping params to command payload
61
+ * @param {Function} mapToCmd - Function mapping params to command payload or returning MCP response
26
62
  */
27
63
  function registerTool(server, name, desc, schema, mapToCmd) {
28
- server.tool(name, desc, schema, async (params) => sendCommand(mapToCmd(params)));
64
+ server.tool(name, desc, schema, async (params) => {
65
+ // 1. Strict Middleware Enforcement (Run BEFORE tool execution)
66
+ if (!READ_ONLY_TOOLS.includes(name)) {
67
+ const state = workflow.getState(SESSION);
68
+
69
+ // Allow workflow_start to run even if state is null
70
+ if (!state && name !== 'workflow_start') {
71
+ return {
72
+ isError: true,
73
+ content: [{ type: 'text', text: "⛔ Middleware Block: Workflow not started! Call workflow_start first." }]
74
+ };
75
+ }
76
+
77
+ if (state) {
78
+ if (state.step_status === 'WAITING_FOR_USER') {
79
+ return {
80
+ isError: true,
81
+ content: [{ type: 'text', text: "⛔ Middleware Block: Workflow is paused! Waiting for user approval. You cannot execute this tool." }]
82
+ };
83
+ }
84
+
85
+ if (state.step_status === 'COMPLETED') {
86
+ return {
87
+ isError: true,
88
+ content: [{ type: 'text', text: "⛔ Middleware Block: Workflow is completed! Please start a new workflow." }]
89
+ };
90
+ }
91
+
92
+ // Block creating new tabs in EDIT_IMAGE mode
93
+ if (name === 'workspace_create_tab' && state.current_mode === 'EDIT_IMAGE') {
94
+ return {
95
+ isError: true,
96
+ content: [{ type: 'text', text: "⛔ Middleware Block: You cannot create new images in EDIT_IMAGE mode. Please only edit the existing image." }]
97
+ };
98
+ }
99
+
100
+ // Check Drawing Steps Enforcement
101
+ if (isDrawingTool(name)) {
102
+ const currentStep = state.steps[state.currentStepIndex];
103
+ if (!currentStep || !DRAWING_STEPS.includes(currentStep.id)) {
104
+ return {
105
+ isError: true,
106
+ content: [{ type: 'text', text: `⛔ Middleware Block: You cannot use drawing/layer tools in the current step ("${currentStep ? currentStep.label : 'None'}"). You must advance to a drawing step (e.g. EXECUTE_PHASES) first.` }]
107
+ };
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ // 2. Execute the tool implementation
114
+ const res = mapToCmd(params);
115
+
116
+ // 3. If the tool implementation returns an MCP response directly (local tool like workflow_*)
117
+ if (res && res.content) {
118
+ return res;
119
+ }
120
+
121
+ // 4. Forward the command payload to the browser
122
+ return sendCommand(res);
123
+ });
29
124
  }
30
125
 
31
126
  module.exports = { createServer, registerTool, sendCommand };
@@ -0,0 +1,134 @@
1
+ # Art Tree Domain Module
2
+
3
+ Art education domain providing interactive drawing lessons through MCP tools.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ domains/art-tree/
9
+ ├── index.js # Entry point - registers all tools
10
+ ├── lib/ # Shared utilities
11
+ │ ├── validators.js # Common Zod schemas
12
+ │ └── tool-register.js # Tool registration helpers
13
+ ├── shapes/ # Shape primitives (building blocks)
14
+ │ ├── index.js # Exports all shapes
15
+ │ ├── basic.js # Line, square, rect, circle, ellipse, triangle, polygon
16
+ │ ├── grid.js # Grid drawing
17
+ │ └── polyline.js # Polyline drawing
18
+ ├── lessons/ # Lesson implementations
19
+ │ ├── index.js # Exports all lessons
20
+ │ ├── basic/ # Basic shape lessons (13 lessons)
21
+ │ ├── advanced/ # Advanced properties (5 lessons)
22
+ │ ├── curves/ # Curve lessons (3 lessons)
23
+ │ ├── ellipses/ # Ellipse lessons
24
+ │ ├── 3d/ # 3D form lessons (4 lessons)
25
+ │ ├── structure/ # Structure lessons (3 lessons)
26
+ │ ├── cross-sections/ # Cross-section lessons (3 lessons)
27
+ │ ├── transforms/ # Transform lessons (8 lessons)
28
+ │ ├── surfaces/ # Surface lessons (4 lessons)
29
+ │ ├── perspective/ # Perspective lessons (4 lessons)
30
+ │ ├── hidden/ # Hidden shapes lessons (3 lessons)
31
+ │ ├── lighting/ # Lighting lessons (3 lessons)
32
+ │ ├── materials/ # Material lessons (3 lessons)
33
+ │ ├── analysis/ # Analysis capstone (10 steps)
34
+ │ ├── layers/ # Layer structure lessons (7 steps)
35
+ │ └── sky/ # Sky & sun lessons (3 lessons)
36
+ └── tools/ # MCP tool registration
37
+ ├── index.js # Registers all tool categories
38
+ ├── basic.js # Basic shape tools
39
+ ├── lessons.js # Lesson tools
40
+ ├── advanced.js # Advanced tools
41
+ ├── curves.js # Curve tools
42
+ ├── ellipses.js # Ellipse tools
43
+ ├── 3d.js # 3D tools
44
+ ├── structure.js # Structure tools
45
+ ├── cross-sections.js # Cross-section tools
46
+ ├── transforms.js # Transform tools
47
+ ├── surfaces.js # Surface tools
48
+ ├── perspective.js # Perspective tools
49
+ ├── hidden.js # Hidden tools
50
+ ├── lighting.js # Lighting tools
51
+ ├── materials.js # Material tools
52
+ ├── analysis.js # Analysis tools
53
+ ├── layers.js # Layer tools
54
+ └── sky.js # Sky tools
55
+ ```
56
+
57
+ ## Layer Separation
58
+
59
+ ### 1. Shapes Layer (Primitives)
60
+ - **Purpose**: Low-level drawing primitives that map 1:1 to core drawing actions
61
+ - **Location**: `shapes/`
62
+ - **Returns**: Command payloads (not executed)
63
+ - **Example**: `drawLine()`, `drawCircle()`, `drawGrid()`
64
+
65
+ ### 2. Lessons Layer (Educational Content)
66
+ - **Purpose**: Educational sequences that teach art concepts
67
+ - **Location**: `lessons/`
68
+ - **Returns**: Array of command payloads
69
+ - **Example**: `lessonLines()`, `lessonCircles()`, `lesson3DBox()`
70
+
71
+ ### 3. Tools Layer (MCP API)
72
+ - **Purpose**: MCP tool definitions that expose shapes/lessons to AI agents
73
+ - **Location**: `tools/`
74
+ - **Returns**: MCP responses
75
+ - **Example**: `art_tree_draw_line`, `art_tree_lesson_lines`
76
+
77
+ ## Design Principles
78
+
79
+ 1. **Separation of Concerns**: Each layer has a single responsibility
80
+ 2. **Composition over Inheritance**: Lessons compose shapes, tools compose lessons
81
+ 3. **No Duplication**: Shared validators and registration helpers in `lib/`
82
+ 4. **Consistent Naming**: Clear patterns for all files and functions
83
+ 5. **Easy Navigation**: Related functionality grouped together
84
+
85
+ ## Usage
86
+
87
+ ### Registering Tools
88
+
89
+ ```javascript
90
+ const { registerAll } = require('./domains/art-tree');
91
+ registerAll(server);
92
+ ```
93
+
94
+ ### Using Shapes
95
+
96
+ ```javascript
97
+ const shapes = require('./domains/art-tree/shapes');
98
+ const cmd = shapes.drawCircle(10, 10, 5, '#ff0000');
99
+ // Returns: { action: 'drawCircle', cx: 10, cy: 10, r: 5, color: '#ff0000' }
100
+ ```
101
+
102
+ ### Using Lessons
103
+
104
+ ```javascript
105
+ const lessons = require('./domains/art-tree/lessons');
106
+ const commands = lessons.lessonLines(32, '#ff0000');
107
+ // Returns: Array of command payloads
108
+ ```
109
+
110
+ ## Adding New Features
111
+
112
+ ### New Shape
113
+ 1. Add function to `shapes/basic.js` (or create new file in `shapes/`)
114
+ 2. Export from `shapes/index.js`
115
+
116
+ ### New Lesson
117
+ 1. Create lesson function in appropriate `lessons/[category]/` folder
118
+ 2. Export from `lessons/[category]/index.js`
119
+ 3. Export from `lessons/index.js`
120
+
121
+ ### New Tool
122
+ 1. Create tool file in `tools/[category].js`
123
+ 2. Use helpers from `lib/tool-register.js`
124
+ 3. Import and call `register()` in `tools/index.js`
125
+
126
+ ## Migration Notes
127
+
128
+ This is a refactored version of the original art-tree module. The old flat structure with 17+ `*_tools.js` files has been reorganized into:
129
+ - Clear layer separation (shapes → lessons → tools)
130
+ - Category-based organization
131
+ - Shared utilities to reduce duplication
132
+ - Consistent naming conventions
133
+
134
+ All original functionality is preserved, just better organized.
@@ -0,0 +1,168 @@
1
+ # Art Tree Module Reorganization Plan
2
+
3
+ ## Current Issues
4
+
5
+ 1. **Too many files**: 17+ separate `*_tools.js` files in one directory
6
+ 2. **Inconsistent naming**: Mix of `_tools.js`, `_lessons.js`, `_shapes.js` suffixes
7
+ 3. **Code duplication**: Every tool file repeats the same boilerplate (zod imports, hexColor schema, register function pattern)
8
+ 4. **Hard to navigate**: Difficult to find related functionality
9
+ 5. **Mixed concerns**: Tools, lessons, and shapes all mixed together
10
+
11
+ ## Proposed Structure
12
+
13
+ ```
14
+ domains/art-tree/
15
+ ├── index.js # Main entry point - register all tools
16
+ ├── README.md # Domain documentation
17
+ ├── lib/ # Shared utilities
18
+ │ ├── validators.js # Common Zod schemas (hexColor, canvasSize, etc.)
19
+ │ └── tool-register.js # Tool registration helper with common patterns
20
+ ├── shapes/ # Shape primitives (currently shapes.js)
21
+ │ ├── index.js # Export all shapes
22
+ │ ├── basic.js # Line, square, rect, circle, ellipse, triangle, polygon
23
+ │ ├── grid.js # Grid drawing
24
+ │ └── polyline.js # Polyline drawing
25
+ ├── lessons/ # Lesson implementations
26
+ │ ├── index.js # Export all lessons + catalog functions
27
+ │ ├── basic/ # Basic shape lessons
28
+ │ │ ├── lines.js
29
+ │ │ ├── squares.js
30
+ │ │ ├── circles.js
31
+ │ │ ├── ellipses.js
32
+ │ │ ├── triangles.js
33
+ │ │ ├── polygons.js
34
+ │ │ ├── composition.js
35
+ │ │ ├── freehand.js
36
+ │ │ ├── proportions.js
37
+ │ │ ├── curves.js
38
+ │ │ ├── spiral.js
39
+ │ │ ├── strokes.js
40
+ │ │ └── parallel-intersecting.js
41
+ │ ├── advanced/ # Advanced property lessons
42
+ │ │ ├── ratios.js
43
+ │ │ ├── symmetry.js
44
+ │ │ ├── angles.js
45
+ │ │ ├── distances.js
46
+ │ │ └── relationships.js
47
+ │ ├── curves/ # Curve-specific lessons
48
+ │ │ ├── types.js
49
+ │ │ ├── topology.js
50
+ │ │ └── properties.js
51
+ │ ├── 3d/ # 3D form lessons
52
+ │ │ ├── box.js
53
+ │ │ ├── sphere.js
54
+ │ │ ├── cylinder.js
55
+ │ │ └── cone.js
56
+ │ ├── structure/ # Structure lessons
57
+ │ │ ├── xyz-axes.js
58
+ │ │ ├── bottle.js
59
+ │ │ └── orientation.js
60
+ │ ├── cross-sections/ # Cross-section lessons
61
+ │ │ ├── bottle.js
62
+ │ │ ├── glass.js
63
+ │ │ └── head.js
64
+ │ ├── transforms/ # Transform lessons
65
+ │ │ ├── stretch.js
66
+ │ │ ├── squash.js
67
+ │ │ ├── taper.js
68
+ │ │ ├── bend.js
69
+ │ │ ├── rotate.js
70
+ │ │ ├── cut.js
71
+ │ │ ├── hollow.js
72
+ │ │ └── combine.js
73
+ │ ├── surfaces/ # Surface lessons
74
+ │ │ ├── types.js
75
+ │ │ ├── edges.js
76
+ │ │ ├── cup.js
77
+ │ │ └── chair.js
78
+ │ ├── perspective/ # Perspective lessons
79
+ │ │ ├── 1point.js
80
+ │ │ ├── 2point.js
81
+ │ │ ├── 3point.js
82
+ │ │ └── foreshorten.js
83
+ │ ├── hidden/ # Hidden shapes lessons
84
+ │ │ ├── box.js
85
+ │ │ ├── head.js
86
+ │ │ └── cup.js
87
+ │ ├── lighting/ # Lighting lessons
88
+ │ │ ├── zones.js
89
+ │ │ ├── direction.js
90
+ │ │ └── contact.js
91
+ │ ├── materials/ # Material lessons
92
+ │ │ ├── shiny.js
93
+ │ │ ├── glass.js
94
+ │ │ └── texture.js
95
+ │ ├── analysis/ # Analysis lessons (capstone)
96
+ │ │ ├── step-1.js
97
+ │ │ ├── step-2.js
98
+ │ │ ├── ... (up to step-10)
99
+ │ │ └── index.js
100
+ │ ├── layers/ # Layer structure lessons
101
+ │ │ ├── step-1.js
102
+ │ │ ├── ... (up to step-7)
103
+ │ │ └── index.js
104
+ │ └── sky/ # Sky & sun lessons
105
+ │ ├── sun-shapes.js
106
+ │ ├── sunset-colors.js
107
+ │ └── sunset-sky.js
108
+ └── tools/ # MCP tool registration
109
+ ├── index.js # Main tool registration entry
110
+ ├── basic.js # Basic shape tools
111
+ ├── lessons.js # Lesson tools (catalog + individual)
112
+ ├── advanced.js # Advanced property tools
113
+ ├── curves.js # Curve tools
114
+ ├── ellipses.js # Ellipse tools
115
+ ├── 3d.js # 3D form tools
116
+ ├── structure.js # Structure tools
117
+ ├── cross-sections.js # Cross-section tools
118
+ ├── transforms.js # Transform tools
119
+ ├── surfaces.js # Surface tools
120
+ ├── perspective.js # Perspective tools
121
+ ├── hidden.js # Hidden shapes tools
122
+ ├── lighting.js # Lighting tools
123
+ ├── materials.js # Material tools
124
+ ├── analysis.js # Analysis tools
125
+ ├── layers.js # Layer tools
126
+ └── sky.js # Sky tools
127
+ ```
128
+
129
+ ## Benefits
130
+
131
+ 1. **Clear separation of concerns**: Shapes, lessons, and tools are separate
132
+ 2. **Easier navigation**: Related files grouped together
133
+ 3. **Less duplication**: Shared validators and registration helpers
134
+ 4. **Scalable**: Easy to add new lessons/tools without cluttering
135
+ 5. **Consistent naming**: Clear pattern for all files
136
+ 6. **Better documentation**: Each subfolder can have its own README
137
+
138
+ ## Migration Strategy
139
+
140
+ 1. Create new directory structure
141
+ 2. Move and refactor files in batches:
142
+ - First: lib/ (shared utilities)
143
+ - Second: shapes/ (no dependencies)
144
+ - Third: lessons/ (depends on shapes)
145
+ - Fourth: tools/ (depends on lessons)
146
+ 3. Update index.js to use new structure
147
+ 4. Test each batch before moving to next
148
+ 5. Delete old files after verification
149
+
150
+ ## Implementation Priority
151
+
152
+ **Phase 1 - Foundation (No breaking changes)**
153
+ - Create lib/ with validators and helpers
154
+ - Create shapes/ and move shapes.js
155
+
156
+ **Phase 2 - Lessons (Depends on Phase 1)**
157
+ - Create lessons/ structure
158
+ - Move all lesson files
159
+
160
+ **Phase 3 - Tools (Depends on Phase 2)**
161
+ - Create tools/ structure
162
+ - Move all tool files
163
+ - Update index.js
164
+
165
+ **Phase 4 - Cleanup**
166
+ - Remove old files
167
+ - Update documentation
168
+ - Verify all tests pass