@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.
- package/clear-firebase.js +37 -0
- package/core/command-bus.js +6 -3
- package/core/server.js +98 -3
- package/domains/art-tree/README.md +134 -0
- package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
- package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
- package/domains/art-tree/index.js +58 -58
- package/domains/art-tree/lessons/3d/box-properties.js +38 -0
- package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
- package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
- package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
- package/domains/art-tree/lessons/advanced/angles.js +30 -0
- package/domains/art-tree/lessons/advanced/distances.js +26 -0
- package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
- package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
- package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
- package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
- package/domains/art-tree/lessons/basic/circles.js +26 -0
- package/domains/art-tree/lessons/basic/composition.js +34 -0
- package/domains/art-tree/lessons/basic/curves.js +31 -0
- package/domains/art-tree/lessons/basic/ellipses.js +26 -0
- package/domains/art-tree/lessons/basic/freehand.js +19 -0
- package/domains/art-tree/lessons/basic/index.js +36 -0
- package/domains/art-tree/lessons/basic/lines.js +38 -0
- package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
- package/domains/art-tree/lessons/basic/polygons.js +25 -0
- package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
- package/domains/art-tree/lessons/basic/spiral.js +21 -0
- package/domains/art-tree/lessons/basic/squares.js +28 -0
- package/domains/art-tree/lessons/basic/strokes.js +23 -0
- package/domains/art-tree/lessons/basic/triangles.js +30 -0
- package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
- package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
- package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
- package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
- package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
- package/domains/art-tree/lessons/curves/curve-types.js +44 -0
- package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
- package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
- package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
- package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
- package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
- package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
- package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
- package/domains/art-tree/lessons/index.js +42 -0
- package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
- package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
- package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
- package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
- package/domains/art-tree/lessons/materials/material-glass.js +21 -0
- package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
- package/domains/art-tree/lessons/materials/material-texture.js +26 -0
- package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
- package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
- package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
- package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
- package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
- package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
- package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
- package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
- package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
- package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
- package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
- package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
- package/domains/art-tree/lessons/transforms/bend.js +27 -0
- package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
- package/domains/art-tree/lessons/transforms/cut.js +25 -0
- package/domains/art-tree/lessons/transforms/hollow.js +24 -0
- package/domains/art-tree/lessons/transforms/rotate.js +16 -0
- package/domains/art-tree/lessons/transforms/squash.js +31 -0
- package/domains/art-tree/lessons/transforms/stretch.js +31 -0
- package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
- package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
- package/domains/art-tree/lib/tool-register.js +104 -0
- package/domains/art-tree/lib/validators.js +78 -0
- package/domains/art-tree/shapes/grid.js +37 -0
- package/domains/art-tree/shapes/polyline.js +26 -0
- package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
- package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
- package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
- package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
- package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
- package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
- package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
- package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
- package/domains/art-tree/tools/index.js +53 -0
- package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
- package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
- package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
- package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
- package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
- package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
- package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
- package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
- package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
- package/package.json +1 -1
- package/rules/README.md +358 -285
- package/rules/index.js +9 -9
- package/rules/prerequisites.js +121 -64
- package/rules/rules.js +76 -35
- package/rules/workflow.js +270 -56
- package/tools/canvas-tools.js +2 -2
- package/tools/drawing-tools.js +23 -12
- package/tools/workspace-tools.js +5 -4
- package/domains/art-tree/3d_lessons.js +0 -114
- package/domains/art-tree/advanced_lessons.js +0 -124
- package/domains/art-tree/cross_section_lessons.js +0 -65
- package/domains/art-tree/curve_lessons.js +0 -138
- package/domains/art-tree/ellipse_lessons.js +0 -105
- package/domains/art-tree/hidden_lessons.js +0 -55
- package/domains/art-tree/lessons.js +0 -305
- package/domains/art-tree/light_lessons.js +0 -92
- package/domains/art-tree/material_lessons.js +0 -71
- package/domains/art-tree/perspective_lessons.js +0 -138
- package/domains/art-tree/sky_lessons_1.js +0 -82
- package/domains/art-tree/surface_lessons.js +0 -112
- package/domains/art-tree/transform_lessons_1.js +0 -108
- /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
- /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
- /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
- /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
- /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
- /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
- /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
- /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
- /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
- /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
- /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
- /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
- /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
- /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
- /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
- /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
- /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);
|
package/core/command-bus.js
CHANGED
|
@@ -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 (${
|
|
33
|
+
content: [{ type: 'text', text: `⏱ Timeout (${actualTimeout}ms). Is the browser tab open with session: ${SESSION}?` }]
|
|
31
34
|
});
|
|
32
|
-
},
|
|
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) =>
|
|
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
|