@mcp-web/core 0.1.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 (102) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +253 -0
  3. package/dist/addTool.typetest.d.ts +11 -0
  4. package/dist/addTool.typetest.d.ts.map +1 -0
  5. package/dist/addTool.typetest.js +248 -0
  6. package/dist/create-state-tools.d.ts +77 -0
  7. package/dist/create-state-tools.d.ts.map +1 -0
  8. package/dist/create-state-tools.js +181 -0
  9. package/dist/create-tool.d.ts +90 -0
  10. package/dist/create-tool.d.ts.map +1 -0
  11. package/dist/create-tool.js +82 -0
  12. package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts +8 -0
  13. package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts.map +1 -0
  14. package/dist/expanded-schema-tools/generate-fixed-shape-tools.js +53 -0
  15. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts +2 -0
  16. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts.map +1 -0
  17. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.js +331 -0
  18. package/dist/expanded-schema-tools/index.d.ts +4 -0
  19. package/dist/expanded-schema-tools/index.d.ts.map +1 -0
  20. package/dist/expanded-schema-tools/index.js +2 -0
  21. package/dist/expanded-schema-tools/integration.test.d.ts +2 -0
  22. package/dist/expanded-schema-tools/integration.test.d.ts.map +1 -0
  23. package/dist/expanded-schema-tools/integration.test.js +599 -0
  24. package/dist/expanded-schema-tools/schema-analysis.d.ts +18 -0
  25. package/dist/expanded-schema-tools/schema-analysis.d.ts.map +1 -0
  26. package/dist/expanded-schema-tools/schema-analysis.js +142 -0
  27. package/dist/expanded-schema-tools/schema-analysis.test.d.ts +2 -0
  28. package/dist/expanded-schema-tools/schema-analysis.test.d.ts.map +1 -0
  29. package/dist/expanded-schema-tools/schema-analysis.test.js +314 -0
  30. package/dist/expanded-schema-tools/schema-helpers.d.ts +69 -0
  31. package/dist/expanded-schema-tools/schema-helpers.d.ts.map +1 -0
  32. package/dist/expanded-schema-tools/schema-helpers.js +139 -0
  33. package/dist/expanded-schema-tools/schema-helpers.test.d.ts +2 -0
  34. package/dist/expanded-schema-tools/schema-helpers.test.d.ts.map +1 -0
  35. package/dist/expanded-schema-tools/schema-helpers.test.js +223 -0
  36. package/dist/expanded-schema-tools/tool-generator.d.ts +10 -0
  37. package/dist/expanded-schema-tools/tool-generator.d.ts.map +1 -0
  38. package/dist/expanded-schema-tools/tool-generator.js +430 -0
  39. package/dist/expanded-schema-tools/tool-generator.test.d.ts +2 -0
  40. package/dist/expanded-schema-tools/tool-generator.test.d.ts.map +1 -0
  41. package/dist/expanded-schema-tools/tool-generator.test.js +689 -0
  42. package/dist/expanded-schema-tools/types.d.ts +26 -0
  43. package/dist/expanded-schema-tools/types.d.ts.map +1 -0
  44. package/dist/expanded-schema-tools/types.js +1 -0
  45. package/dist/expanded-schema-tools/utils.d.ts +16 -0
  46. package/dist/expanded-schema-tools/utils.d.ts.map +1 -0
  47. package/dist/expanded-schema-tools/utils.js +35 -0
  48. package/dist/expanded-schema-tools/utils.test.d.ts +2 -0
  49. package/dist/expanded-schema-tools/utils.test.d.ts.map +1 -0
  50. package/dist/expanded-schema-tools/utils.test.js +169 -0
  51. package/dist/group-state.d.ts +60 -0
  52. package/dist/group-state.d.ts.map +1 -0
  53. package/dist/group-state.js +54 -0
  54. package/dist/index.d.ts +14 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +13 -0
  57. package/dist/query.d.ts +104 -0
  58. package/dist/query.d.ts.map +1 -0
  59. package/dist/query.js +128 -0
  60. package/dist/schema-helpers.d.ts +69 -0
  61. package/dist/schema-helpers.d.ts.map +1 -0
  62. package/dist/schema-helpers.js +139 -0
  63. package/dist/schemas.d.ts +140 -0
  64. package/dist/schemas.d.ts.map +1 -0
  65. package/dist/schemas.js +70 -0
  66. package/dist/tool-generators/generate-basic-state-tools.d.ts +23 -0
  67. package/dist/tool-generators/generate-basic-state-tools.d.ts.map +1 -0
  68. package/dist/tool-generators/generate-basic-state-tools.js +95 -0
  69. package/dist/tool-generators/generate-fixed-shape-tools.d.ts +8 -0
  70. package/dist/tool-generators/generate-fixed-shape-tools.d.ts.map +1 -0
  71. package/dist/tool-generators/generate-fixed-shape-tools.js +53 -0
  72. package/dist/tool-generators/index.d.ts +6 -0
  73. package/dist/tool-generators/index.d.ts.map +1 -0
  74. package/dist/tool-generators/index.js +3 -0
  75. package/dist/tool-generators/schema-analysis.d.ts +18 -0
  76. package/dist/tool-generators/schema-analysis.d.ts.map +1 -0
  77. package/dist/tool-generators/schema-analysis.js +142 -0
  78. package/dist/tool-generators/schema-helpers.d.ts +87 -0
  79. package/dist/tool-generators/schema-helpers.d.ts.map +1 -0
  80. package/dist/tool-generators/schema-helpers.js +157 -0
  81. package/dist/tool-generators/tool-generator.d.ts +11 -0
  82. package/dist/tool-generators/tool-generator.d.ts.map +1 -0
  83. package/dist/tool-generators/tool-generator.js +437 -0
  84. package/dist/tool-generators/types.d.ts +26 -0
  85. package/dist/tool-generators/types.d.ts.map +1 -0
  86. package/dist/tool-generators/types.js +1 -0
  87. package/dist/tool-generators/utils.d.ts +16 -0
  88. package/dist/tool-generators/utils.d.ts.map +1 -0
  89. package/dist/tool-generators/utils.js +35 -0
  90. package/dist/types.d.ts +17 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +1 -0
  93. package/dist/utils.d.ts +31 -0
  94. package/dist/utils.d.ts.map +1 -0
  95. package/dist/utils.js +108 -0
  96. package/dist/web.d.ts +680 -0
  97. package/dist/web.d.ts.map +1 -0
  98. package/dist/web.js +1312 -0
  99. package/dist/zod-to-tools.d.ts +49 -0
  100. package/dist/zod-to-tools.d.ts.map +1 -0
  101. package/dist/zod-to-tools.js +623 -0
  102. package/package.json +58 -0
@@ -0,0 +1,437 @@
1
+ import { z } from 'zod';
2
+ import { generateFixedShapeTools } from './generate-fixed-shape-tools.js';
3
+ import { analyzeSchemaShape, findIdField } from './schema-analysis.js';
4
+ import { deriveAddInputSchema, deriveSetInputSchema, unwrapSchema, validateSystemFields, } from './schema-helpers.js';
5
+ import { deepMerge } from './utils.js';
6
+ // ============================================================================
7
+ // Main Entry Point
8
+ // ============================================================================
9
+ /**
10
+ * Generates MCP tools for a schema based on its shape.
11
+ * - Fixed-shape schemas → get + set with deep merge
12
+ * - Dynamic-shape schemas → get + add + set + delete (arrays) or get + set + delete (records)
13
+ * - Mixed schemas → asymmetric get/set + collection tools
14
+ *
15
+ * Returns tool definitions without registering them - the caller is responsible for registration.
16
+ */
17
+ export function generateToolsForSchema(options) {
18
+ const { name, schema } = options;
19
+ const tools = [];
20
+ const warnings = [];
21
+ // Validate schema
22
+ const unwrapped = unwrapSchema(schema);
23
+ // Validate system fields if it's an object
24
+ if (unwrapped instanceof z.ZodObject) {
25
+ validateSystemFields(unwrapped);
26
+ }
27
+ // Analyze schema shape
28
+ const shape = analyzeSchemaShape(schema);
29
+ // Warn about optional fields (once)
30
+ if (shape.hasOptionalFields) {
31
+ warnings.push(`⚠️ Warning: Schema for '${name}' uses optional() on field(s): ${shape.optionalPaths.join(', ')}.\n\n` +
32
+ `Problem: optional() creates ambiguity in partial updates:\n` +
33
+ `- Is the field missing because AI didn't provide it? (keep current)\n` +
34
+ `- Or because AI wants to clear it? (set to undefined)\n\n` +
35
+ `Solution: Use nullable() instead:\n` +
36
+ `- fieldName: z.string().nullable()\n\n` +
37
+ `This makes intent explicit:\n` +
38
+ `- Omit field → keep current value\n` +
39
+ `- Pass null → clear the value`);
40
+ }
41
+ // Generate tools based on schema type
42
+ if (shape.type === 'fixed') {
43
+ // Fixed-shape: primitives, tuples, or objects with only fixed props
44
+ tools.push(...generateFixedShapeTools(options, shape));
45
+ }
46
+ else if (shape.type === 'dynamic') {
47
+ // Dynamic-shape: arrays or records at root, or objects with only dynamic props
48
+ if (shape.subtype === 'array') {
49
+ tools.push(...generateArrayTools(options));
50
+ }
51
+ else if (shape.subtype === 'record') {
52
+ tools.push(...generateRecordTools(options));
53
+ }
54
+ }
55
+ else if (shape.type === 'mixed') {
56
+ // Mixed: objects with both fixed and dynamic props
57
+ tools.push(...generateMixedObjectTools(options, shape));
58
+ }
59
+ else {
60
+ // Unsupported type
61
+ warnings.push(`⚠️ Warning: Schema for '${name}' contains unsupported types.\n` +
62
+ `Only JSON-compatible types are supported (objects, arrays, records, primitives).`);
63
+ }
64
+ return { tools, warnings };
65
+ }
66
+ // ============================================================================
67
+ // Array Tools Generator
68
+ // ============================================================================
69
+ /**
70
+ * Generates tools for array schemas.
71
+ * Creates 4 tools: get, add, set, delete
72
+ * Uses index-based addressing by default, ID-based when id() marker present.
73
+ */
74
+ function generateArrayTools(options) {
75
+ const { name, description, get, set, schema } = options;
76
+ const tools = [];
77
+ const unwrapped = unwrapSchema(schema);
78
+ const elementSchema = unwrapped.element;
79
+ // Check if element is an object with id() marker
80
+ const elementUnwrapped = unwrapSchema(elementSchema);
81
+ let idField = { type: 'none' };
82
+ if (elementUnwrapped instanceof z.ZodObject) {
83
+ idField = findIdField(elementUnwrapped);
84
+ }
85
+ const useIdBased = idField.type === 'explicit';
86
+ // Derive input schemas (exclude system fields)
87
+ let addInputSchema = elementSchema;
88
+ let setInputSchema = elementSchema;
89
+ if (elementUnwrapped instanceof z.ZodObject) {
90
+ addInputSchema = deriveAddInputSchema(elementUnwrapped);
91
+ setInputSchema = deriveSetInputSchema(elementUnwrapped);
92
+ }
93
+ // GET tool
94
+ if (useIdBased) {
95
+ const keyField = idField.field;
96
+ tools.push({
97
+ name: `get_${name}`,
98
+ description: `Get ${description} by ID, or get all if no ID provided`,
99
+ inputSchema: z.object({
100
+ id: z.string().optional().describe('The ID of the item to retrieve. If omitted, returns all items.'),
101
+ }),
102
+ handler: async (input) => {
103
+ const array = get();
104
+ if (input.id !== undefined) {
105
+ return array.find((item) => item[keyField] === input.id);
106
+ }
107
+ return array;
108
+ },
109
+ });
110
+ }
111
+ else {
112
+ tools.push({
113
+ name: `get_${name}`,
114
+ description: `Get ${description} by index, or get all if no index provided`,
115
+ inputSchema: z.object({
116
+ index: z.number().int().min(0).optional().describe('The zero-based index of the item to retrieve. If omitted, returns all items.'),
117
+ }),
118
+ handler: async (input) => {
119
+ const array = get();
120
+ if (input.index !== undefined) {
121
+ return array[input.index];
122
+ }
123
+ return array;
124
+ },
125
+ });
126
+ }
127
+ // ADD tool
128
+ if (useIdBased) {
129
+ tools.push({
130
+ name: `add_${name}`,
131
+ description: `Add a new item to ${description}`,
132
+ inputSchema: z.object({
133
+ value: addInputSchema.describe('The item to add.'),
134
+ }),
135
+ handler: async (input) => {
136
+ const array = get();
137
+ const parsed = elementSchema.parse(input.value);
138
+ set([...array, parsed]);
139
+ return { success: true, value: parsed };
140
+ },
141
+ });
142
+ }
143
+ else {
144
+ tools.push({
145
+ name: `add_${name}`,
146
+ description: `Add a new item to ${description} at the specified index (default: end)`,
147
+ inputSchema: z.object({
148
+ value: addInputSchema.describe('The item to add.'),
149
+ index: z.number().int().min(0).optional().describe('The zero-based index at which to insert the item. If omitted, the item is added at the end.'),
150
+ }),
151
+ handler: async (input) => {
152
+ const array = get();
153
+ const parsed = elementSchema.parse(input.value);
154
+ if (input.index !== undefined) {
155
+ set([...array.slice(0, input.index), parsed, ...array.slice(input.index)]);
156
+ }
157
+ else {
158
+ set([...array, parsed]);
159
+ }
160
+ return { success: true, value: parsed };
161
+ },
162
+ });
163
+ }
164
+ // SET tool (partial update with deep merge)
165
+ if (useIdBased) {
166
+ const keyField = idField.field;
167
+ tools.push({
168
+ name: `set_${name}`,
169
+ description: `Update an item in ${description} by ID (partial update with deep merge)`,
170
+ inputSchema: z.object({
171
+ id: z.string().describe('The ID of the item to update.'),
172
+ value: setInputSchema.describe('The fields to update. Only provided fields will be changed; omitted fields retain their current values.'),
173
+ }),
174
+ handler: async (input) => {
175
+ const array = get();
176
+ const index = array.findIndex((item) => item[keyField] === input.id);
177
+ if (index === -1) {
178
+ throw new Error(`Item with id '${input.id}' not found in ${name}`);
179
+ }
180
+ const merged = deepMerge(array[index], input.value);
181
+ const validated = elementSchema.parse(merged);
182
+ const newArray = [...array];
183
+ newArray[index] = validated;
184
+ set(newArray);
185
+ return { success: true, value: validated };
186
+ },
187
+ });
188
+ }
189
+ else {
190
+ tools.push({
191
+ name: `set_${name}`,
192
+ description: `Update an item in ${description} by index (partial update with deep merge)`,
193
+ inputSchema: z.object({
194
+ index: z.number().int().min(0).describe('The zero-based index of the item to update.'),
195
+ value: setInputSchema.describe('The fields to update. Only provided fields will be changed; omitted fields retain their current values.'),
196
+ }),
197
+ handler: async (input) => {
198
+ const array = get();
199
+ if (input.index >= array.length) {
200
+ throw new Error(`Index ${input.index} out of bounds for ${name} (length: ${array.length})`);
201
+ }
202
+ const merged = deepMerge(array[input.index], input.value);
203
+ const validated = elementSchema.parse(merged);
204
+ const newArray = [...array];
205
+ newArray[input.index] = validated;
206
+ set(newArray);
207
+ return { success: true, value: validated };
208
+ },
209
+ });
210
+ }
211
+ // DELETE tool
212
+ if (useIdBased) {
213
+ const keyField = idField.field;
214
+ tools.push({
215
+ name: `delete_${name}`,
216
+ description: `Delete an item from ${description} by ID, or delete all items`,
217
+ inputSchema: z.object({
218
+ id: z.string().optional().describe('The ID of the item to delete. Ignored if `all` is true.'),
219
+ all: z.literal(true).optional().describe(`Delete all items in ${description}. This takes precedence over \`id\`.`),
220
+ }),
221
+ handler: async (input) => {
222
+ const array = get();
223
+ if (input.all) {
224
+ set([]);
225
+ }
226
+ else if (input.id) {
227
+ const index = array.findIndex((item) => item[keyField] === input.id);
228
+ if (index !== -1) {
229
+ set([...array.slice(0, index), ...array.slice(index + 1)]);
230
+ }
231
+ }
232
+ return { success: true };
233
+ },
234
+ });
235
+ }
236
+ else {
237
+ tools.push({
238
+ name: `delete_${name}`,
239
+ description: `Delete an item from ${description} by index, or delete all items`,
240
+ inputSchema: z.object({
241
+ index: z.number().int().min(0).optional().describe('The zero-based index of the item to delete. Ignored if `all` is true.'),
242
+ all: z.literal(true).optional().describe(`Delete all items in ${description}. This takes precedence over \`index\`.`),
243
+ }),
244
+ handler: async (input) => {
245
+ const array = get();
246
+ if (input.all) {
247
+ set([]);
248
+ }
249
+ else if (input.index !== undefined) {
250
+ if (input.index < array.length) {
251
+ set([...array.slice(0, input.index), ...array.slice(input.index + 1)]);
252
+ }
253
+ }
254
+ return { success: true };
255
+ },
256
+ });
257
+ }
258
+ return tools;
259
+ }
260
+ // ============================================================================
261
+ // Record Tools Generator
262
+ // ============================================================================
263
+ /**
264
+ * Generates tools for record schemas.
265
+ * Creates 3 tools: get, set (upsert), delete
266
+ * Records use string keys naturally, no ID marker needed.
267
+ */
268
+ function generateRecordTools(options) {
269
+ const { name, description, get, set, schema } = options;
270
+ const tools = [];
271
+ const unwrapped = unwrapSchema(schema);
272
+ const def = unwrapped._def;
273
+ const valueSchema = def.valueType || z.unknown();
274
+ // Derive input schemas (exclude system fields if value is object)
275
+ const valueUnwrapped = unwrapSchema(valueSchema);
276
+ let setInputSchema = valueSchema;
277
+ if (valueUnwrapped instanceof z.ZodObject) {
278
+ setInputSchema = deriveSetInputSchema(valueUnwrapped);
279
+ }
280
+ // GET tool
281
+ tools.push({
282
+ name: `get_${name}`,
283
+ description: `Get ${description} by key, or get all if no key provided`,
284
+ inputSchema: z.object({
285
+ key: z.string().optional().describe('The key of the entry to retrieve. If omitted, returns all entries.'),
286
+ }),
287
+ handler: async (input) => {
288
+ const record = get();
289
+ if (input.key !== undefined) {
290
+ return record[input.key];
291
+ }
292
+ return record;
293
+ },
294
+ });
295
+ // SET tool (upsert: add or update)
296
+ tools.push({
297
+ name: `set_${name}`,
298
+ description: `Set (add or update) an entry in ${description} (partial update with deep merge for objects)`,
299
+ inputSchema: z.object({
300
+ key: z.string().describe('The key for the entry to set.'),
301
+ value: setInputSchema.describe('The value to set. For existing object entries, only provided fields will be changed.'),
302
+ }),
303
+ handler: async (input) => {
304
+ const record = get();
305
+ // If value schema is object and entry exists, deep merge
306
+ if (valueUnwrapped instanceof z.ZodObject && record[input.key] !== undefined) {
307
+ const merged = deepMerge(record[input.key], input.value);
308
+ const validated = valueSchema.parse(merged);
309
+ set({ ...record, [input.key]: validated });
310
+ return { success: true, value: validated };
311
+ }
312
+ // Otherwise, full replacement (upsert)
313
+ const validated = valueSchema.parse(input.value);
314
+ set({ ...record, [input.key]: validated });
315
+ return { success: true, value: validated };
316
+ },
317
+ });
318
+ // DELETE tool
319
+ tools.push({
320
+ name: `delete_${name}`,
321
+ description: `Delete an entry from ${description} by key, or delete all entries`,
322
+ inputSchema: z.object({
323
+ key: z.string().optional().describe('The key of the entry to delete. Ignored if `all` is true.'),
324
+ all: z.literal(true).optional().describe(`Delete all entries in ${description}. This takes precedence over \`key\`.`),
325
+ }),
326
+ handler: async (input) => {
327
+ const record = get();
328
+ if (input.all) {
329
+ set({});
330
+ }
331
+ else if (input.key) {
332
+ const { [input.key]: _, ...rest } = record;
333
+ set(rest);
334
+ }
335
+ return { success: true };
336
+ },
337
+ });
338
+ return tools;
339
+ }
340
+ // ============================================================================
341
+ // Mixed Object Tools Generator
342
+ // ============================================================================
343
+ /**
344
+ * Generates tools for mixed objects (both fixed and dynamic props).
345
+ * Creates asymmetric get/set:
346
+ * - get() returns full state (including collections)
347
+ * - set() only updates fixed-shape props
348
+ * - Separate tools for each collection
349
+ */
350
+ function generateMixedObjectTools(options, _shape) {
351
+ const { name, description, get, set, schema } = options;
352
+ const tools = [];
353
+ const unwrapped = unwrapSchema(schema);
354
+ // Split into fixed and dynamic parts
355
+ const fixedShape = {};
356
+ const dynamicFields = [];
357
+ for (const [key, field] of Object.entries(unwrapped.shape)) {
358
+ const zodField = field;
359
+ const unwrappedField = unwrapSchema(zodField);
360
+ if (unwrappedField instanceof z.ZodArray || unwrappedField instanceof z.ZodRecord) {
361
+ dynamicFields.push({ key, field: zodField });
362
+ }
363
+ else {
364
+ fixedShape[key] = zodField;
365
+ }
366
+ }
367
+ const hasFixedProps = Object.keys(fixedShape).length > 0;
368
+ // ROOT GETTER - always returns full state
369
+ tools.push({
370
+ name: `get_${name}`,
371
+ description: `Get the current ${description} (full state including collections)`,
372
+ inputSchema: z.object({
373
+ excludeCollections: z.boolean().optional().describe('If true, excludes array and record collections from the response, returning only fixed-shape properties.'),
374
+ }),
375
+ handler: async (input) => {
376
+ const fullState = get();
377
+ if (input.excludeCollections) {
378
+ // Return only fixed-shape props
379
+ const fixedState = {};
380
+ for (const key of Object.keys(fixedShape)) {
381
+ fixedState[key] = fullState[key];
382
+ }
383
+ return fixedState;
384
+ }
385
+ return fullState;
386
+ },
387
+ });
388
+ // ROOT SETTER - only if there are fixed props
389
+ if (hasFixedProps) {
390
+ const setInputSchema = deriveSetInputSchema(z.object(fixedShape));
391
+ tools.push({
392
+ name: `set_${name}`,
393
+ description: `Update ${description} settings (fixed-shape props only, use collection tools for arrays/records)`,
394
+ inputSchema: setInputSchema,
395
+ handler: async (input) => {
396
+ const current = get();
397
+ // Merge only the fixed props
398
+ const fixedUpdate = {};
399
+ for (const key of Object.keys(fixedShape)) {
400
+ if (key in input) {
401
+ fixedUpdate[key] = input[key];
402
+ }
403
+ }
404
+ const merged = deepMerge(current, fixedUpdate);
405
+ const validated = schema.parse(merged);
406
+ set(validated);
407
+ // Return only the fixed props that were updated
408
+ const result = {};
409
+ for (const key of Object.keys(fixedShape)) {
410
+ result[key] = validated[key];
411
+ }
412
+ return { success: true, value: result };
413
+ },
414
+ });
415
+ }
416
+ // COLLECTION TOOLS - generate for each dynamic field
417
+ for (const { key, field } of dynamicFields) {
418
+ const unwrappedField = unwrapSchema(field);
419
+ const collectionOptions = {
420
+ name: `${name}_${key}`,
421
+ description: `${key} in ${description}`,
422
+ get: () => get()[key],
423
+ set: (value) => {
424
+ const current = get();
425
+ set({ ...current, [key]: value });
426
+ },
427
+ schema: field,
428
+ };
429
+ if (unwrappedField instanceof z.ZodArray) {
430
+ tools.push(...generateArrayTools(collectionOptions));
431
+ }
432
+ else if (unwrappedField instanceof z.ZodRecord) {
433
+ tools.push(...generateRecordTools(collectionOptions));
434
+ }
435
+ }
436
+ return tools;
437
+ }
@@ -0,0 +1,26 @@
1
+ import type { ToolDefinitionZod } from '@mcp-web/types';
2
+ import type { z } from 'zod';
3
+ export interface ToolGenerationOptions {
4
+ name: string;
5
+ description: string;
6
+ get: () => unknown;
7
+ set: (value: unknown) => void;
8
+ schema: z.ZodTypeAny;
9
+ }
10
+ export interface GeneratedTools {
11
+ tools: ToolDefinitionZod[];
12
+ warnings: string[];
13
+ }
14
+ export interface SchemaShape {
15
+ type: 'fixed' | 'dynamic' | 'mixed' | 'unsupported';
16
+ subtype: 'object' | 'array' | 'record' | 'primitive' | 'tuple' | 'unknown';
17
+ hasOptionalFields: boolean;
18
+ optionalPaths: string[];
19
+ fixedPaths: string[];
20
+ dynamicPaths: string[];
21
+ }
22
+ export interface KeyFieldResult {
23
+ type: 'explicit' | 'none';
24
+ field?: string;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tool-generators/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,OAAO,CAAC;IACnB,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;IACpD,OAAO,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3E,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Deep merge two objects recursively.
3
+ * Used for partial updates to state objects.
4
+ *
5
+ * @param target - The target object to merge into
6
+ * @param source - The source object to merge from
7
+ * @returns The merged result
8
+ *
9
+ * Key behaviors:
10
+ * - `undefined` in source → keep target value (no change)
11
+ * - `null` in source → set to null (explicit clear)
12
+ * - Nested objects → recursively merged
13
+ * - Arrays → replaced entirely (not merged)
14
+ */
15
+ export declare function deepMerge(target: unknown, source: unknown): unknown;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tool-generators/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAenE"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Deep merge two objects recursively.
3
+ * Used for partial updates to state objects.
4
+ *
5
+ * @param target - The target object to merge into
6
+ * @param source - The source object to merge from
7
+ * @returns The merged result
8
+ *
9
+ * Key behaviors:
10
+ * - `undefined` in source → keep target value (no change)
11
+ * - `null` in source → set to null (explicit clear)
12
+ * - Nested objects → recursively merged
13
+ * - Arrays → replaced entirely (not merged)
14
+ */
15
+ export function deepMerge(target, source) {
16
+ // Base cases
17
+ if (source === null)
18
+ return source; // Explicit null clears the value
19
+ if (source === undefined)
20
+ return target; // Undefined means "no change"
21
+ if (typeof source !== 'object')
22
+ return source;
23
+ if (Array.isArray(source))
24
+ return source; // Arrays are replaced, not merged
25
+ if (typeof target !== 'object' || target === null)
26
+ return source;
27
+ if (Array.isArray(target))
28
+ return source;
29
+ // Object merge: recursively merge properties
30
+ const result = { ...target };
31
+ for (const [key, value] of Object.entries(source)) {
32
+ result[key] = deepMerge(result[key], value);
33
+ }
34
+ return result;
35
+ }
@@ -0,0 +1,17 @@
1
+ import type { ToolDefinition } from '@mcp-web/types';
2
+ import type { z } from 'zod';
3
+ import type { ContextItemSchema, EphemeralContextSchema, QueryRequestSchema, QueryResponseResultAcceptedSchema, QueryResponseResultCompleteSchema, QueryResponseResultFailureSchema, QueryResponseResultProgressSchema, QueryResponseResultSchema } from './schemas.js';
4
+ export type QueryResponseResult = z.infer<typeof QueryResponseResultSchema>;
5
+ export type QueryRequest = Omit<z.input<typeof QueryRequestSchema>, 'responseTool' | 'tools' | 'context'> & {
6
+ responseTool?: ToolDefinition;
7
+ tools?: ToolDefinition[];
8
+ context?: (ToolDefinition | EphemeralContext)[];
9
+ };
10
+ export type QueryRequestOutput = z.infer<typeof QueryRequestSchema>;
11
+ export type QueryResponseResultAccepted = z.infer<typeof QueryResponseResultAcceptedSchema>;
12
+ export type QueryResponseResultProgress = z.infer<typeof QueryResponseResultProgressSchema>;
13
+ export type QueryResponseResultComplete = z.infer<typeof QueryResponseResultCompleteSchema>;
14
+ export type QueryResponseResultFailure = z.infer<typeof QueryResponseResultFailureSchema>;
15
+ export type ContextItem = z.infer<typeof ContextItemSchema>;
16
+ export type EphemeralContext = z.infer<typeof EphemeralContextSchema>;
17
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,iCAAiC,EACjC,iCAAiC,EACjC,gCAAgC,EAChC,iCAAiC,EACjC,yBAAyB,EAC1B,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,EAAE,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC,GAAG;IAC1G,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,cAAc,GAAG,gBAAgB,CAAC,EAAE,CAAC;CACjD,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACpE,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAC1F,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { ToolMetadataJson, ToolDefinition } from '@mcp-web/types';
2
+ import { z } from 'zod';
3
+ export declare function isZodSchema(schema: z.ZodType<unknown> | z.core.JSONSchema.JSONSchema): schema is z.ZodType<unknown>;
4
+ export declare function toJSONSchema(schema: z.ZodType | z.core.JSONSchema.JSONSchema): z.core.JSONSchema.JSONSchema;
5
+ export declare function toToolZodSchema<T>(schema?: z.ZodType<T> | z.core.JSONSchema.JSONSchema): z.ZodObject;
6
+ export declare function toToolSchema<T>(schema?: z.ZodType<T> | z.core.JSONSchema.JSONSchema): z.ZodObject | z.core.JSONSchema.JSONSchema;
7
+ export declare function validateInput<T>(input: unknown, schema: z.ZodType<T> | z.core.JSONSchema.JSONSchema): T;
8
+ /**
9
+ * Convert a ToolDefinition to ToolMetadataJson for wire transmission.
10
+ * Removes the handler and converts Zod schemas to JSON Schema.
11
+ *
12
+ * @param tool - The tool definition to convert
13
+ * @returns Serializable tool metadata without handler, with JSON Schema schemas
14
+ */
15
+ export declare function toToolMetadataJson(tool: ToolDefinition): ToolMetadataJson;
16
+ /**
17
+ * Deep merge two objects recursively.
18
+ * Used for partial updates to state objects.
19
+ *
20
+ * @param target - The target object to merge into
21
+ * @param source - The source object to merge from
22
+ * @returns The merged result
23
+ *
24
+ * Key behaviors:
25
+ * - `undefined` in source → keep target value (no change)
26
+ * - `null` in source → set to null (explicit clear)
27
+ * - Nested objects → recursively merged
28
+ * - Arrays → replaced entirely (not merged)
29
+ */
30
+ export declare function deepMerge(target: unknown, source: unknown): unknown;
31
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAEnH;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAY3G;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC,SAAS,CAOpG;AAGD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAWhI;AAED,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,GAClD,CAAC,CAqBH;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAOzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAenE"}