Skip to content

Commit 31a39b4

Browse files
authored
add datashape schema defs to MCP (#860)
- **improve debug logs** - **rm context-specific comment** - **fix: pass correct codeCourse, courseIDs** - **fix: use cardID, courseID fields...** - **[wip] add sk-contributor course app** - **bump: version 0.1.11-26** - **de-decorate courseID for packed manifest** - **lookup datatShape from allDataShapesRaw...** - **add type-check script** - **add zod 4 for json serde of DataShape schema** - **add zoddify and serialize fcn for DataShapes** - **add zedSchema param** - **export zoddifiers** - **serialize and store datashape schema during reg..** - **check existing entries for schemaSerialization** - **add schema lookup resource** - **use defined schema...** - **expose object literal and z.object wrapped schema** - **type check fix** - **lockfile**
2 parents 7d6974c + 3dc3dd9 commit 31a39b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2567
-108
lines changed

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"type": "module",
88
"description": "CLI scaffolding tool for vue-skuilder projects",
99
"bin": {

packages/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"license": "MIT",
88
"main": "dist/index.js",
99
"module": "dist/index.esm.js",

packages/common-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"main": "./dist/common-ui.umd.js",
88
"module": "./dist/common-ui.es.js",
99
"types": "./dist/index.d.ts",

packages/common/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"type": "module",
88
"main": "dist/index.js",
99
"module": "dist/index.mjs",
@@ -32,7 +32,8 @@
3232
"typescript": "~5.7.2"
3333
},
3434
"dependencies": {
35-
"moment": "^2.30.1"
35+
"moment": "^2.30.1",
36+
"zod": "^4.0.0"
3637
},
3738
"stableVersion": "0.1.10"
3839
}

packages/common/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ export * from './interfaces/index.js';
1717
// enums
1818
export * from './enums/index.js';
1919

20+
// schemas
21+
export * from './schemas/dataShapeToZod.js';
22+
2023
// docker utilities (Node.js only - not exported in main index for browser compatibility)
2124
// Use explicit import: import { CouchDBManager } from '@vue-skuilder/common/docker'

packages/common/src/namespacer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export class NameSpacer {
33
const splitArray = shapeStr.split('.');
44

55
if (splitArray.length !== 3) {
6-
throw new Error('shapeStr not valid');
6+
throw new Error(`shapeStr [${shapeStr}] not valid`);
77
} else {
88
return {
99
course: splitArray[0],
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { z, ZodRawShape } from 'zod';
2+
import { DataShape, FieldDefinition, FieldType, Status } from '../index.js';
3+
4+
/**
5+
* Converts a FieldType enum to appropriate Zod schema
6+
*/
7+
function fieldTypeToZodSchema(field: FieldDefinition): z.ZodTypeAny {
8+
let baseSchema: z.ZodTypeAny;
9+
10+
switch (field.type) {
11+
case FieldType.STRING:
12+
baseSchema = z.string().min(1, `${field.name} cannot be empty`);
13+
break;
14+
15+
case FieldType.MARKDOWN:
16+
baseSchema = z.string().min(1, `${field.name} cannot be empty`);
17+
18+
// Special handling for known patterns like Blanks
19+
if (field.name === 'Input') {
20+
baseSchema = baseSchema
21+
.refine(
22+
(value): value is string => typeof value === 'string' && value.includes('{{') && value.includes('}}'),
23+
'Must contain at least one blank in format {{answer}} or {{answer1|answer2||distractor}}'
24+
)
25+
.describe(
26+
"Markdown text with blanks. Format: {{answer}} for fill-in or {{answer1|answer2||distractor1|distractor2}} for multiple choice. Use the 'fill-in-card-authoring' MCP prompt for detailed syntax guidance."
27+
);
28+
} else {
29+
baseSchema = baseSchema.describe('Markdown content');
30+
}
31+
break;
32+
33+
case FieldType.NUMBER:
34+
baseSchema = z.number().describe(`Numeric value for ${field.name}`);
35+
break;
36+
37+
case FieldType.INT:
38+
baseSchema = z.number().int().describe(`Integer value for ${field.name}`);
39+
break;
40+
41+
case FieldType.IMAGE:
42+
baseSchema = z.any().optional().describe('Image file');
43+
break;
44+
45+
case FieldType.AUDIO:
46+
baseSchema = z.any().optional().describe('Audio file');
47+
break;
48+
49+
case FieldType.MIDI:
50+
baseSchema = z.any().optional().describe('MIDI file');
51+
break;
52+
53+
case FieldType.MEDIA_UPLOADS:
54+
baseSchema = z
55+
.array(z.any())
56+
.optional()
57+
.describe('Optional media files (images, audio, etc.)');
58+
break;
59+
60+
case FieldType.CHESS_PUZZLE:
61+
baseSchema = z
62+
.string()
63+
.min(1, 'Chess puzzle cannot be empty')
64+
.describe('Chess puzzle in FEN or PGN format');
65+
break;
66+
67+
default:
68+
baseSchema = z.any().describe(`Field of type ${field.type}`);
69+
}
70+
71+
// Add custom validation if present
72+
if (field.validator) {
73+
const originalValidator = field.validator;
74+
baseSchema = baseSchema.refine(
75+
(value) => {
76+
try {
77+
const result = originalValidator.test(String(value));
78+
return result.status === Status.ok;
79+
} catch {
80+
return false;
81+
}
82+
},
83+
{
84+
message: originalValidator.instructions || `Validation failed for ${field.name}`,
85+
}
86+
);
87+
88+
// Add validator instructions as description if available
89+
if (originalValidator.instructions) {
90+
baseSchema = baseSchema.describe(
91+
`${baseSchema.description || ''}\nInstructions: ${originalValidator.instructions}`.trim()
92+
);
93+
}
94+
}
95+
96+
return baseSchema;
97+
}
98+
99+
/**
100+
* Converts a DataShape to a Zod schema
101+
*/
102+
export function toZod(dataShape: DataShape): z.ZodObject<ZodRawShape> {
103+
const schemaFields: Record<string, z.ZodTypeAny> = {};
104+
105+
dataShape.fields.forEach((field) => {
106+
schemaFields[field.name] = fieldTypeToZodSchema(field);
107+
});
108+
109+
return z.object(schemaFields).describe(`DataShape: ${dataShape.name} - Schema for card creation`);
110+
}
111+
112+
/**
113+
* Converts a DataShape to JSON Schema string using Zod v4 native conversion
114+
*/
115+
export function toZodJSON(dataShape: DataShape): string {
116+
const zodSchema = toZod(dataShape);
117+
const jsonSchema = z.toJSONSchema(zodSchema);
118+
return JSON.stringify(jsonSchema, null, 2);
119+
}

packages/common/src/wire-format.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface DataShape55 {
6666
// [ ] rename this to something else - disambiguate from DataShape in base-course
6767
name: NamespacedDatashape;
6868
questionTypes: PouchDB.Core.DocumentId[];
69+
serializedZodSchema?: string;
6970
}
7071

7172
type NamespacedQuestion = string; // ${course}.question.${question}

packages/courseware/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"type": "module",
88
"main": "./dist/index.cjs.js",
99
"module": "./dist/index.mjs",

packages/db/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publishConfig": {
44
"access": "public"
55
},
6-
"version": "0.1.11-25",
6+
"version": "0.1.11-26",
77
"description": "Database layer for vue-skuilder",
88
"main": "dist/index.js",
99
"module": "dist/index.mjs",

0 commit comments

Comments
 (0)