@lumibase/mcp-server 0.12.0 → 0.14.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 (3) hide show
  1. package/dist/index.cjs +464 -442
  2. package/dist/index.js +464 -442
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -109,10 +109,10 @@ function configFromEnv() {
109
109
  }
110
110
 
111
111
  // src/tools/access.ts
112
- import { z as z2 } from "zod";
112
+ import { z as z3 } from "zod";
113
113
 
114
114
  // src/tools/_crud.ts
115
- import { z } from "zod";
115
+ import { z as z2 } from "zod";
116
116
 
117
117
  // src/tools/_shared.ts
118
118
  function formatError(err) {
@@ -154,6 +154,20 @@ async function run(fn) {
154
154
  }
155
155
  var confirmDescription = "Must be true to confirm the destructive operation";
156
156
 
157
+ // src/tools/path.ts
158
+ import { z } from "zod";
159
+ var namePattern = /^[a-z][a-z0-9_]{0,62}$/;
160
+ var collectionNameSchema = z.string().min(1).max(63).regex(namePattern, "Must be lowercase, start with a letter, only a-z0-9_");
161
+ var fieldNameSchema = z.string().min(1).max(63).regex(namePattern, "Must be lowercase snake_case, start with a letter");
162
+ var idPathSegmentSchema = z.string().min(1).refine((value) => value !== "." && value !== "..", "Must not be . or ..").refine((value) => !/[\\/]/.test(value), "Must not contain path separators");
163
+ function encodePathSegment(segment) {
164
+ return encodeURIComponent(segment);
165
+ }
166
+ var mediaKeySchema = z.string().min(1).refine((value) => !value.includes(".."), 'Must not contain ".."').refine((value) => !value.startsWith("/"), 'Must not start with "/"').refine((value) => !value.includes("\\"), "Must not contain backslashes");
167
+ function encodeMediaKey(key) {
168
+ return key.split("/").map((segment) => encodeURIComponent(segment)).join("/");
169
+ }
170
+
157
171
  // src/tools/_crud.ts
158
172
  function registerCrud(server, client, opts) {
159
173
  const {
@@ -184,9 +198,9 @@ function registerCrud(server, client, opts) {
184
198
  `get_${namePrefix}`,
185
199
  {
186
200
  description: `Get a single ${resource} by ${idParam}.`,
187
- inputSchema: { [idParam]: z.string().min(1) }
201
+ inputSchema: { [idParam]: idPathSegmentSchema }
188
202
  },
189
- async (args) => run(() => client.get(`${basePath}/${String(args[idParam])}`))
203
+ async (args) => run(() => client.get(`${basePath}/${encodePathSegment(String(args[idParam]))}`))
190
204
  );
191
205
  }
192
206
  if (createSchema) {
@@ -204,11 +218,11 @@ function registerCrud(server, client, opts) {
204
218
  `update_${namePrefix}`,
205
219
  {
206
220
  description: `Update an existing ${resource} (partial PATCH).`,
207
- inputSchema: { [idParam]: z.string().min(1), ...updateSchema }
221
+ inputSchema: { [idParam]: idPathSegmentSchema, ...updateSchema }
208
222
  },
209
223
  async (args) => {
210
224
  const { [idParam]: id, ...patch } = args;
211
- return run(() => client.patch(`${basePath}/${String(id)}`, patch));
225
+ return run(() => client.patch(`${basePath}/${encodePathSegment(String(id))}`, patch));
212
226
  }
213
227
  );
214
228
  }
@@ -218,14 +232,14 @@ function registerCrud(server, client, opts) {
218
232
  {
219
233
  description: `Delete a ${resource}. DESTRUCTIVE \u2014 warn the user first and pass confirm=true.`,
220
234
  inputSchema: {
221
- [idParam]: z.string().min(1),
222
- confirm: z.literal(true).describe(confirmDescription)
235
+ [idParam]: idPathSegmentSchema,
236
+ confirm: z2.literal(true).describe(confirmDescription)
223
237
  }
224
238
  },
225
239
  async (args) => {
226
240
  const id = String(args[idParam]);
227
241
  return run(async () => {
228
- await client.delete(`${basePath}/${id}`);
242
+ await client.delete(`${basePath}/${encodePathSegment(id)}`);
229
243
  return okText(`${resource} "${id}" deleted.`);
230
244
  });
231
245
  }
@@ -234,37 +248,37 @@ function registerCrud(server, client, opts) {
234
248
  }
235
249
 
236
250
  // src/tools/access.ts
237
- var roleSchema = z2.object({
238
- key: z2.string().min(1).max(96).optional(),
239
- systemKey: z2.string().min(1).max(96).optional(),
240
- name: z2.string().min(1).max(64),
241
- description: z2.string().max(512).optional(),
242
- icon: z2.string().max(64).optional(),
243
- parentId: z2.string().nullable().optional(),
244
- adminAccess: z2.boolean().optional(),
245
- appAccess: z2.boolean().optional()
251
+ var roleSchema = z3.object({
252
+ key: z3.string().min(1).max(96).optional(),
253
+ systemKey: z3.string().min(1).max(96).optional(),
254
+ name: z3.string().min(1).max(64),
255
+ description: z3.string().max(512).optional(),
256
+ icon: z3.string().max(64).optional(),
257
+ parentId: z3.string().nullable().optional(),
258
+ adminAccess: z3.boolean().optional(),
259
+ appAccess: z3.boolean().optional()
246
260
  });
247
- var policySchema = z2.object({
248
- key: z2.string().min(1).max(96).optional(),
249
- name: z2.string().min(1).max(64),
250
- icon: z2.string().max(64).optional(),
251
- description: z2.string().max(512).optional(),
252
- adminAccess: z2.boolean().optional(),
253
- appAccess: z2.boolean().optional(),
254
- enforceTfa: z2.boolean().optional(),
255
- ipAllow: z2.array(z2.string()).optional(),
256
- ipDeny: z2.array(z2.string()).optional(),
257
- validFrom: z2.string().datetime().nullable().optional(),
258
- validUntil: z2.string().datetime().nullable().optional(),
259
- rules: z2.record(z2.unknown()).optional()
261
+ var policySchema = z3.object({
262
+ key: z3.string().min(1).max(96).optional(),
263
+ name: z3.string().min(1).max(64),
264
+ icon: z3.string().max(64).optional(),
265
+ description: z3.string().max(512).optional(),
266
+ adminAccess: z3.boolean().optional(),
267
+ appAccess: z3.boolean().optional(),
268
+ enforceTfa: z3.boolean().optional(),
269
+ ipAllow: z3.array(z3.string()).optional(),
270
+ ipDeny: z3.array(z3.string()).optional(),
271
+ validFrom: z3.string().datetime().nullable().optional(),
272
+ validUntil: z3.string().datetime().nullable().optional(),
273
+ rules: z3.record(z3.unknown()).optional()
260
274
  });
261
- var permissionSchema = z2.object({
262
- collection: z2.string().min(1).max(64),
263
- action: z2.enum(["create", "read", "update", "delete", "share"]),
264
- permissions: z2.record(z2.unknown()).optional(),
265
- validation: z2.record(z2.unknown()).optional(),
266
- presets: z2.record(z2.unknown()).optional(),
267
- fields: z2.array(z2.string()).optional()
275
+ var permissionSchema = z3.object({
276
+ collection: z3.string().min(1).max(64),
277
+ action: z3.enum(["create", "read", "update", "delete", "share"]),
278
+ permissions: z3.record(z3.unknown()).optional(),
279
+ validation: z3.record(z3.unknown()).optional(),
280
+ presets: z3.record(z3.unknown()).optional(),
281
+ fields: z3.array(z3.string()).optional()
268
282
  });
269
283
  function registerAccessTools(server, client) {
270
284
  registerCrud(server, client, {
@@ -279,26 +293,26 @@ function registerAccessTools(server, client) {
279
293
  {
280
294
  description: "Attach a policy to a role. Conflicts/warnings may require overrideWarnings=true.",
281
295
  inputSchema: {
282
- id: z2.string().min(1).describe("Role id."),
283
- policyId: z2.string().min(1),
284
- priority: z2.number().int().optional(),
285
- overrideWarnings: z2.boolean().optional()
296
+ id: idPathSegmentSchema.describe("Role id."),
297
+ policyId: idPathSegmentSchema,
298
+ priority: z3.number().int().optional(),
299
+ overrideWarnings: z3.boolean().optional()
286
300
  }
287
301
  },
288
- async ({ id, ...body }) => run(() => client.post(`/roles/${id}/policies`, body))
302
+ async ({ id, ...body }) => run(() => client.post(`/roles/${encodePathSegment(id)}/policies`, body))
289
303
  );
290
304
  server.registerTool(
291
305
  "detach_role_policy",
292
306
  {
293
307
  description: "Detach a policy from a role. DESTRUCTIVE \u2014 pass confirm=true.",
294
308
  inputSchema: {
295
- id: z2.string().min(1).describe("Role id."),
296
- policyId: z2.string().min(1),
297
- confirm: z2.literal(true).describe(confirmDescription)
309
+ id: idPathSegmentSchema.describe("Role id."),
310
+ policyId: idPathSegmentSchema,
311
+ confirm: z3.literal(true).describe(confirmDescription)
298
312
  }
299
313
  },
300
314
  async ({ id, policyId }) => run(async () => {
301
- await client.delete(`/roles/${id}/policies/${policyId}`);
315
+ await client.delete(`/roles/${encodePathSegment(id)}/policies/${encodePathSegment(policyId)}`);
302
316
  return okText(`Policy "${policyId}" detached from role "${id}".`);
303
317
  })
304
318
  );
@@ -306,22 +320,22 @@ function registerAccessTools(server, client) {
306
320
  "assign_role_user",
307
321
  {
308
322
  description: "Assign a role to a user (sets the user's primary role for this site).",
309
- inputSchema: { id: z2.string().min(1).describe("Role id."), userId: z2.string().min(1) }
323
+ inputSchema: { id: idPathSegmentSchema.describe("Role id."), userId: idPathSegmentSchema }
310
324
  },
311
- async ({ id, userId }) => run(() => client.post(`/roles/${id}/users`, { userId }))
325
+ async ({ id, userId }) => run(() => client.post(`/roles/${encodePathSegment(id)}/users`, { userId }))
312
326
  );
313
327
  server.registerTool(
314
328
  "remove_role_user",
315
329
  {
316
330
  description: "Remove a user's role assignment. DESTRUCTIVE \u2014 pass confirm=true.",
317
331
  inputSchema: {
318
- id: z2.string().min(1).describe("Role id."),
319
- userId: z2.string().min(1),
320
- confirm: z2.literal(true).describe(confirmDescription)
332
+ id: idPathSegmentSchema.describe("Role id."),
333
+ userId: idPathSegmentSchema,
334
+ confirm: z3.literal(true).describe(confirmDescription)
321
335
  }
322
336
  },
323
337
  async ({ id, userId }) => run(async () => {
324
- await client.delete(`/roles/${id}/users/${userId}`);
338
+ await client.delete(`/roles/${encodePathSegment(id)}/users/${encodePathSegment(userId)}`);
325
339
  return okText(`User "${userId}" removed from role "${id}".`);
326
340
  })
327
341
  );
@@ -337,34 +351,34 @@ function registerAccessTools(server, client) {
337
351
  "add_policy_permission",
338
352
  {
339
353
  description: "Add a permission row (collection + action) to a policy.",
340
- inputSchema: { id: z2.string().min(1).describe("Policy id."), ...permissionSchema.shape }
354
+ inputSchema: { id: idPathSegmentSchema.describe("Policy id."), ...permissionSchema.shape }
341
355
  },
342
- async ({ id, ...body }) => run(() => client.post(`/policies/${id}/permissions`, body))
356
+ async ({ id, ...body }) => run(() => client.post(`/policies/${encodePathSegment(id)}/permissions`, body))
343
357
  );
344
358
  server.registerTool(
345
359
  "update_policy_permission",
346
360
  {
347
361
  description: "Update a permission row on a policy (partial PATCH).",
348
362
  inputSchema: {
349
- id: z2.string().min(1).describe("Policy id."),
350
- permId: z2.string().min(1).describe("Permission row id."),
363
+ id: idPathSegmentSchema.describe("Policy id."),
364
+ permId: idPathSegmentSchema.describe("Permission row id."),
351
365
  ...permissionSchema.partial().shape
352
366
  }
353
367
  },
354
- async ({ id, permId, ...body }) => run(() => client.patch(`/policies/${id}/permissions/${permId}`, body))
368
+ async ({ id, permId, ...body }) => run(() => client.patch(`/policies/${encodePathSegment(id)}/permissions/${encodePathSegment(permId)}`, body))
355
369
  );
356
370
  server.registerTool(
357
371
  "delete_policy_permission",
358
372
  {
359
373
  description: "Delete a permission row from a policy. DESTRUCTIVE \u2014 pass confirm=true.",
360
374
  inputSchema: {
361
- id: z2.string().min(1).describe("Policy id."),
362
- permId: z2.string().min(1).describe("Permission row id."),
363
- confirm: z2.literal(true).describe(confirmDescription)
375
+ id: idPathSegmentSchema.describe("Policy id."),
376
+ permId: idPathSegmentSchema.describe("Permission row id."),
377
+ confirm: z3.literal(true).describe(confirmDescription)
364
378
  }
365
379
  },
366
380
  async ({ id, permId }) => run(async () => {
367
- await client.delete(`/policies/${id}/permissions/${permId}`);
381
+ await client.delete(`/policies/${encodePathSegment(id)}/permissions/${encodePathSegment(permId)}`);
368
382
  return okText(`Permission "${permId}" deleted from policy "${id}".`);
369
383
  })
370
384
  );
@@ -373,26 +387,26 @@ function registerAccessTools(server, client) {
373
387
  {
374
388
  description: "Attach a policy directly to a user. Warnings may require overrideWarnings=true.",
375
389
  inputSchema: {
376
- id: z2.string().min(1).describe("Policy id."),
377
- userId: z2.string().min(1),
378
- priority: z2.number().int().optional(),
379
- overrideWarnings: z2.boolean().optional()
390
+ id: idPathSegmentSchema.describe("Policy id."),
391
+ userId: idPathSegmentSchema,
392
+ priority: z3.number().int().optional(),
393
+ overrideWarnings: z3.boolean().optional()
380
394
  }
381
395
  },
382
- async ({ id, ...body }) => run(() => client.post(`/policies/${id}/users`, body))
396
+ async ({ id, ...body }) => run(() => client.post(`/policies/${encodePathSegment(id)}/users`, body))
383
397
  );
384
398
  server.registerTool(
385
399
  "detach_policy_user",
386
400
  {
387
401
  description: "Detach a policy from a user. DESTRUCTIVE \u2014 pass confirm=true.",
388
402
  inputSchema: {
389
- id: z2.string().min(1).describe("Policy id."),
390
- userId: z2.string().min(1),
391
- confirm: z2.literal(true).describe(confirmDescription)
403
+ id: idPathSegmentSchema.describe("Policy id."),
404
+ userId: idPathSegmentSchema,
405
+ confirm: z3.literal(true).describe(confirmDescription)
392
406
  }
393
407
  },
394
408
  async ({ id, userId }) => run(async () => {
395
- await client.delete(`/policies/${id}/users/${userId}`);
409
+ await client.delete(`/policies/${encodePathSegment(id)}/users/${encodePathSegment(userId)}`);
396
410
  return okText(`Policy "${id}" detached from user "${userId}".`);
397
411
  })
398
412
  );
@@ -405,7 +419,7 @@ function registerAccessTools(server, client) {
405
419
  "dry_run_access_import",
406
420
  {
407
421
  description: "Validate an RBAC manifest import without applying it. Returns the planned changes.",
408
- inputSchema: { manifest: z2.record(z2.unknown()).describe("RBAC manifest from export_access.") }
422
+ inputSchema: { manifest: z3.record(z3.unknown()).describe("RBAC manifest from export_access.") }
409
423
  },
410
424
  async ({ manifest }) => run(() => client.post("/access/import?dryRun=true", manifest))
411
425
  );
@@ -414,9 +428,9 @@ function registerAccessTools(server, client) {
414
428
  {
415
429
  description: "Apply an RBAC manifest import. High-impact \u2014 changes roles/policies/permissions. Pass confirm=true.",
416
430
  inputSchema: {
417
- manifest: z2.record(z2.unknown()).describe("RBAC manifest from export_access."),
418
- mode: z2.enum(["merge", "replace-managed", "replace-all"]).optional().describe("Import mode (default merge)."),
419
- confirm: z2.literal(true).describe(confirmDescription)
431
+ manifest: z3.record(z3.unknown()).describe("RBAC manifest from export_access."),
432
+ mode: z3.enum(["merge", "replace-managed", "replace-all"]).optional().describe("Import mode (default merge)."),
433
+ confirm: z3.literal(true).describe(confirmDescription)
420
434
  }
421
435
  },
422
436
  async ({ manifest, mode }) => run(() => client.post(`/access/import${mode ? `?mode=${mode}` : ""}`, manifest))
@@ -426,9 +440,9 @@ function registerAccessTools(server, client) {
426
440
  {
427
441
  description: "Check for permission conflicts before attaching/detaching policies on a target.",
428
442
  inputSchema: {
429
- target: z2.object({ type: z2.enum(["role", "user", "api_key"]), id: z2.string().min(1) }).describe("The role/user/api_key the policies would apply to."),
430
- addPolicies: z2.array(z2.string()).optional(),
431
- removePolicies: z2.array(z2.string()).optional()
443
+ target: z3.object({ type: z3.enum(["role", "user", "api_key"]), id: z3.string().min(1) }).describe("The role/user/api_key the policies would apply to."),
444
+ addPolicies: z3.array(z3.string()).optional(),
445
+ removePolicies: z3.array(z3.string()).optional()
432
446
  }
433
447
  },
434
448
  async (input) => run(() => client.post("/access/conflicts/check", input))
@@ -436,14 +450,14 @@ function registerAccessTools(server, client) {
436
450
  }
437
451
 
438
452
  // src/tools/admin.ts
439
- import { z as z3 } from "zod";
440
- var materializeSchema = z3.object({
441
- collection: z3.string().min(1).describe("Source collection name."),
442
- target: z3.string().regex(/^[a-z][a-z0-9_]{0,62}$/).describe("Physical table name (snake_case)."),
443
- refreshStrategy: z3.enum(["auto", "cron", "manual"]).optional(),
444
- refreshCron: z3.string().optional(),
445
- projection: z3.object({ fields: z3.array(z3.string()).optional(), orderBy: z3.string().optional() }).optional(),
446
- filter: z3.record(z3.unknown()).optional()
453
+ import { z as z4 } from "zod";
454
+ var materializeSchema = z4.object({
455
+ collection: z4.string().min(1).describe("Source collection name."),
456
+ target: z4.string().regex(/^[a-z][a-z0-9_]{0,62}$/).describe("Physical table name (snake_case)."),
457
+ refreshStrategy: z4.enum(["auto", "cron", "manual"]).optional(),
458
+ refreshCron: z4.string().optional(),
459
+ projection: z4.object({ fields: z4.array(z4.string()).optional(), orderBy: z4.string().optional() }).optional(),
460
+ filter: z4.record(z4.unknown()).optional()
447
461
  });
448
462
  function registerAdminTools(server, client) {
449
463
  server.registerTool(
@@ -459,8 +473,8 @@ function registerAdminTools(server, client) {
459
473
  {
460
474
  description: "Restore a site configuration from an NDJSON bundle (as produced by export_backup). Existing rows are skipped (idempotent). DESTRUCTIVE / high-impact \u2014 pass confirm=true.",
461
475
  inputSchema: {
462
- ndjson: z3.string().min(1).describe("Raw NDJSON backup content, one JSON object per line."),
463
- confirm: z3.literal(true).describe(confirmDescription)
476
+ ndjson: z4.string().min(1).describe("Raw NDJSON backup content, one JSON object per line."),
477
+ confirm: z4.literal(true).describe(confirmDescription)
464
478
  }
465
479
  },
466
480
  async ({ ndjson }) => run(() => client.postRaw("/admin/restore", ndjson))
@@ -482,58 +496,58 @@ function registerAdminTools(server, client) {
482
496
  "refresh_materialization",
483
497
  {
484
498
  description: "Refresh a materialized collection (truncate + re-insert from source).",
485
- inputSchema: { id: z3.string().min(1) }
499
+ inputSchema: { id: idPathSegmentSchema }
486
500
  },
487
- async ({ id }) => run(() => client.post(`/materialize/${id}/refresh`, {}))
501
+ async ({ id }) => run(() => client.post(`/materialize/${encodePathSegment(id)}/refresh`, {}))
488
502
  );
489
503
  server.registerTool(
490
504
  "query_materialization",
491
505
  {
492
506
  description: "Query the physical table of a materialized collection directly.",
493
- inputSchema: { id: z3.string().min(1) }
507
+ inputSchema: { id: idPathSegmentSchema }
494
508
  },
495
- async ({ id }) => run(() => client.get(`/materialize/${id}/data`))
509
+ async ({ id }) => run(() => client.get(`/materialize/${encodePathSegment(id)}/data`))
496
510
  );
497
511
  server.registerTool(
498
512
  "drop_materialization",
499
513
  {
500
514
  description: "Drop a materialized collection (physical table + metadata). DESTRUCTIVE \u2014 pass confirm=true.",
501
- inputSchema: { id: z3.string().min(1), confirm: z3.literal(true).describe(confirmDescription) }
515
+ inputSchema: { id: idPathSegmentSchema, confirm: z4.literal(true).describe(confirmDescription) }
502
516
  },
503
517
  async ({ id }) => run(async () => {
504
- await client.delete(`/materialize/${id}`);
518
+ await client.delete(`/materialize/${encodePathSegment(id)}`);
505
519
  return okText(`Materialization "${id}" dropped.`);
506
520
  })
507
521
  );
508
522
  }
509
523
 
510
524
  // src/tools/agent.ts
511
- import { z as z4 } from "zod";
512
- var intentSchema = z4.object({
513
- name: z4.string().min(1).max(120),
514
- collection: z4.string().min(1).max(120),
515
- rules: z4.array(z4.record(z4.unknown())).describe("SLO rules (required_fields, freshness, translations, link_health, \u2026)."),
516
- schedule: z4.string().describe("5-field cron expression for reconciliation cadence."),
517
- budget: z4.record(z4.unknown()).optional(),
518
- autonomyCap: z4.number().int().min(0).max(4).optional().describe("Max autonomy level L0\u2013L4 for this intent."),
519
- maintenanceWindow: z4.record(z4.unknown()).nullable().optional()
525
+ import { z as z5 } from "zod";
526
+ var intentSchema = z5.object({
527
+ name: z5.string().min(1).max(120),
528
+ collection: z5.string().min(1).max(120),
529
+ rules: z5.array(z5.record(z5.unknown())).describe("SLO rules (required_fields, freshness, translations, link_health, \u2026)."),
530
+ schedule: z5.string().describe("5-field cron expression for reconciliation cadence."),
531
+ budget: z5.record(z5.unknown()).optional(),
532
+ autonomyCap: z5.number().int().min(0).max(4).optional().describe("Max autonomy level L0\u2013L4 for this intent."),
533
+ maintenanceWindow: z5.record(z5.unknown()).nullable().optional()
520
534
  });
521
- var flowNode = z4.object({
522
- id: z4.string(),
523
- key: z4.string(),
524
- options: z4.record(z4.unknown()).optional(),
525
- next: z4.string().nullable().optional(),
526
- onError: z4.string().nullable().optional()
535
+ var flowNode = z5.object({
536
+ id: z5.string(),
537
+ key: z5.string(),
538
+ options: z5.record(z5.unknown()).optional(),
539
+ next: z5.string().nullable().optional(),
540
+ onError: z5.string().nullable().optional()
527
541
  });
528
- var flowSchema = z4.object({
529
- name: z4.string().min(1),
530
- description: z4.string().optional(),
531
- status: z4.enum(["active", "inactive", "draft"]).optional(),
532
- triggerType: z4.enum(["webhook", "event", "schedule", "manual"]),
533
- triggerOptions: z4.record(z4.unknown()).optional(),
534
- graph: z4.object({
535
- entry: z4.string().optional(),
536
- nodes: z4.array(flowNode).optional()
542
+ var flowSchema = z5.object({
543
+ name: z5.string().min(1),
544
+ description: z5.string().optional(),
545
+ status: z5.enum(["active", "inactive", "draft"]).optional(),
546
+ triggerType: z5.enum(["webhook", "event", "schedule", "manual"]),
547
+ triggerOptions: z5.record(z5.unknown()).optional(),
548
+ graph: z5.object({
549
+ entry: z5.string().optional(),
550
+ nodes: z5.array(flowNode).optional()
537
551
  })
538
552
  });
539
553
  function registerAgentTools(server, client) {
@@ -546,31 +560,31 @@ function registerAgentTools(server, client) {
546
560
  });
547
561
  server.registerTool(
548
562
  "pause_intent",
549
- { description: "Pause an intent (stops scheduled reconciliation).", inputSchema: { id: z4.string().min(1) } },
550
- async ({ id }) => run(() => client.post(`/agent/intents/${id}/pause`, {}))
563
+ { description: "Pause an intent (stops scheduled reconciliation).", inputSchema: { id: idPathSegmentSchema } },
564
+ async ({ id }) => run(() => client.post(`/agent/intents/${encodePathSegment(id)}/pause`, {}))
551
565
  );
552
566
  server.registerTool(
553
567
  "resume_intent",
554
- { description: "Resume a paused intent.", inputSchema: { id: z4.string().min(1) } },
555
- async ({ id }) => run(() => client.post(`/agent/intents/${id}/resume`, {}))
568
+ { description: "Resume a paused intent.", inputSchema: { id: idPathSegmentSchema } },
569
+ async ({ id }) => run(() => client.post(`/agent/intents/${encodePathSegment(id)}/resume`, {}))
556
570
  );
557
571
  server.registerTool(
558
572
  "list_intent_drifts",
559
- { description: "List detected drifts (intent violations) for an intent.", inputSchema: { id: z4.string().min(1) } },
560
- async ({ id }) => run(() => client.get(`/agent/intents/${id}/drifts`))
573
+ { description: "List detected drifts (intent violations) for an intent.", inputSchema: { id: idPathSegmentSchema } },
574
+ async ({ id }) => run(() => client.get(`/agent/intents/${encodePathSegment(id)}/drifts`))
561
575
  );
562
576
  server.registerTool(
563
577
  "scan_intent",
564
- { description: "Trigger an on-demand drift scan for an intent.", inputSchema: { id: z4.string().min(1) } },
565
- async ({ id }) => run(() => client.post(`/agent/intents/${id}/scan`, {}))
578
+ { description: "Trigger an on-demand drift scan for an intent.", inputSchema: { id: idPathSegmentSchema } },
579
+ async ({ id }) => run(() => client.post(`/agent/intents/${encodePathSegment(id)}/scan`, {}))
566
580
  );
567
581
  server.registerTool(
568
582
  "compile_intent",
569
583
  {
570
584
  description: "Compile a natural-language description into structured intent rules (preview, does not persist).",
571
585
  inputSchema: {
572
- description: z4.string().min(1).max(4e3),
573
- collection: z4.string().min(1).max(120)
586
+ description: z5.string().min(1).max(4e3),
587
+ collection: z5.string().min(1).max(120)
574
588
  }
575
589
  },
576
590
  async (input) => run(() => client.post("/agent/intents/compile", input))
@@ -587,21 +601,21 @@ function registerAgentTools(server, client) {
587
601
  {
588
602
  description: "Trigger a manual run of a flow with an optional input payload.",
589
603
  inputSchema: {
590
- id: z4.string().min(1),
591
- input: z4.record(z4.unknown()).optional().describe("Initial context passed to the flow.")
604
+ id: idPathSegmentSchema,
605
+ input: z5.record(z5.unknown()).optional().describe("Initial context passed to the flow.")
592
606
  }
593
607
  },
594
- async ({ id, input }) => run(() => client.post(`/flows/${id}/run`, input ?? {}))
608
+ async ({ id, input }) => run(() => client.post(`/flows/${encodePathSegment(id)}/run`, input ?? {}))
595
609
  );
596
610
  server.registerTool(
597
611
  "list_flow_runs",
598
- { description: "List recent runs of a flow.", inputSchema: { id: z4.string().min(1) } },
599
- async ({ id }) => run(() => client.get(`/flows/${id}/runs`))
612
+ { description: "List recent runs of a flow.", inputSchema: { id: idPathSegmentSchema } },
613
+ async ({ id }) => run(() => client.get(`/flows/${encodePathSegment(id)}/runs`))
600
614
  );
601
615
  }
602
616
 
603
617
  // src/tools/api-keys.ts
604
- import { z as z5 } from "zod";
618
+ import { z as z6 } from "zod";
605
619
  function registerApiKeyTools(server, client) {
606
620
  server.registerTool(
607
621
  "list_api_keys",
@@ -610,18 +624,18 @@ function registerApiKeyTools(server, client) {
610
624
  );
611
625
  server.registerTool(
612
626
  "get_api_key",
613
- { description: "Get a single API key by id.", inputSchema: { id: z5.string().min(1) } },
614
- async ({ id }) => run(() => client.get(`/api-keys/${id}`))
627
+ { description: "Get a single API key by id.", inputSchema: { id: idPathSegmentSchema } },
628
+ async ({ id }) => run(() => client.get(`/api-keys/${encodePathSegment(id)}`))
615
629
  );
616
630
  server.registerTool(
617
631
  "create_api_key",
618
632
  {
619
633
  description: "Create a new API key. The plaintext token is returned ONCE in the response \u2014 surface it to the user and tell them it cannot be retrieved again.",
620
634
  inputSchema: {
621
- name: z5.string().min(1).max(96),
622
- description: z5.string().max(512).optional(),
623
- expiresAt: z5.string().datetime().nullable().optional(),
624
- metadata: z5.record(z5.unknown()).optional()
635
+ name: z6.string().min(1).max(96),
636
+ description: z6.string().max(512).optional(),
637
+ expiresAt: z6.string().datetime().nullable().optional(),
638
+ metadata: z6.record(z6.unknown()).optional()
625
639
  }
626
640
  },
627
641
  async (input) => run(() => client.post("/api-keys", input))
@@ -631,46 +645,46 @@ function registerApiKeyTools(server, client) {
631
645
  {
632
646
  description: "Rotate an API key \u2014 issues a new token (returned once) and invalidates the old one. DESTRUCTIVE for existing integrations \u2014 pass confirm=true.",
633
647
  inputSchema: {
634
- id: z5.string().min(1),
635
- expiresAt: z5.string().datetime().nullable().optional(),
636
- confirm: z5.literal(true).describe(confirmDescription)
648
+ id: idPathSegmentSchema,
649
+ expiresAt: z6.string().datetime().nullable().optional(),
650
+ confirm: z6.literal(true).describe(confirmDescription)
637
651
  }
638
652
  },
639
- async ({ id, expiresAt }) => run(() => client.post(`/api-keys/${id}/rotate`, expiresAt !== void 0 ? { expiresAt } : {}))
653
+ async ({ id, expiresAt }) => run(() => client.post(`/api-keys/${encodePathSegment(id)}/rotate`, expiresAt !== void 0 ? { expiresAt } : {}))
640
654
  );
641
655
  server.registerTool(
642
656
  "revoke_api_key",
643
657
  {
644
658
  description: "Revoke an API key permanently. DESTRUCTIVE \u2014 pass confirm=true.",
645
- inputSchema: { id: z5.string().min(1), confirm: z5.literal(true).describe(confirmDescription) }
659
+ inputSchema: { id: idPathSegmentSchema, confirm: z6.literal(true).describe(confirmDescription) }
646
660
  },
647
- async ({ id }) => run(() => client.post(`/api-keys/${id}/revoke`, {}))
661
+ async ({ id }) => run(() => client.post(`/api-keys/${encodePathSegment(id)}/revoke`, {}))
648
662
  );
649
663
  server.registerTool(
650
664
  "attach_api_key_role",
651
665
  {
652
666
  description: "Attach a role to an API key (grants the role\u2019s policies to the key).",
653
667
  inputSchema: {
654
- id: z5.string().min(1).describe("API key id."),
655
- roleId: z5.string().min(1),
656
- priority: z5.number().int().optional(),
657
- overrideWarnings: z5.boolean().optional()
668
+ id: idPathSegmentSchema.describe("API key id."),
669
+ roleId: idPathSegmentSchema,
670
+ priority: z6.number().int().optional(),
671
+ overrideWarnings: z6.boolean().optional()
658
672
  }
659
673
  },
660
- async ({ id, ...body }) => run(() => client.post(`/api-keys/${id}/roles`, body))
674
+ async ({ id, ...body }) => run(() => client.post(`/api-keys/${encodePathSegment(id)}/roles`, body))
661
675
  );
662
676
  server.registerTool(
663
677
  "detach_api_key_role",
664
678
  {
665
679
  description: "Detach a role from an API key. DESTRUCTIVE \u2014 pass confirm=true.",
666
680
  inputSchema: {
667
- id: z5.string().min(1).describe("API key id."),
668
- roleId: z5.string().min(1),
669
- confirm: z5.literal(true).describe(confirmDescription)
681
+ id: idPathSegmentSchema.describe("API key id."),
682
+ roleId: idPathSegmentSchema,
683
+ confirm: z6.literal(true).describe(confirmDescription)
670
684
  }
671
685
  },
672
686
  async ({ id, roleId }) => run(async () => {
673
- await client.delete(`/api-keys/${id}/roles/${roleId}`);
687
+ await client.delete(`/api-keys/${encodePathSegment(id)}/roles/${encodePathSegment(roleId)}`);
674
688
  return okText(`Role "${roleId}" detached from API key "${id}".`);
675
689
  })
676
690
  );
@@ -679,73 +693,72 @@ function registerApiKeyTools(server, client) {
679
693
  {
680
694
  description: "Attach a policy directly to an API key.",
681
695
  inputSchema: {
682
- id: z5.string().min(1).describe("API key id."),
683
- policyId: z5.string().min(1),
684
- priority: z5.number().int().optional(),
685
- overrideWarnings: z5.boolean().optional()
696
+ id: idPathSegmentSchema.describe("API key id."),
697
+ policyId: idPathSegmentSchema,
698
+ priority: z6.number().int().optional(),
699
+ overrideWarnings: z6.boolean().optional()
686
700
  }
687
701
  },
688
- async ({ id, ...body }) => run(() => client.post(`/api-keys/${id}/policies`, body))
702
+ async ({ id, ...body }) => run(() => client.post(`/api-keys/${encodePathSegment(id)}/policies`, body))
689
703
  );
690
704
  server.registerTool(
691
705
  "detach_api_key_policy",
692
706
  {
693
707
  description: "Detach a policy from an API key. DESTRUCTIVE \u2014 pass confirm=true.",
694
708
  inputSchema: {
695
- id: z5.string().min(1).describe("API key id."),
696
- policyId: z5.string().min(1),
697
- confirm: z5.literal(true).describe(confirmDescription)
709
+ id: idPathSegmentSchema.describe("API key id."),
710
+ policyId: idPathSegmentSchema,
711
+ confirm: z6.literal(true).describe(confirmDescription)
698
712
  }
699
713
  },
700
714
  async ({ id, policyId }) => run(async () => {
701
- await client.delete(`/api-keys/${id}/policies/${policyId}`);
715
+ await client.delete(`/api-keys/${encodePathSegment(id)}/policies/${encodePathSegment(policyId)}`);
702
716
  return okText(`Policy "${policyId}" detached from API key "${id}".`);
703
717
  })
704
718
  );
705
719
  }
706
720
 
707
721
  // src/tools/collections.ts
708
- import { z as z6 } from "zod";
709
- var collectionNameSchema = z6.string().min(1).max(63).regex(/^[a-z][a-z0-9_]{0,62}$/, "Must be lowercase, start with a letter, only a-z0-9_");
710
- var collectionInputSchema = z6.object({
722
+ import { z as z7 } from "zod";
723
+ var collectionInputSchema = z7.object({
711
724
  name: collectionNameSchema,
712
- label: z6.string().optional(),
713
- pluralLabel: z6.string().optional(),
714
- hidden: z6.boolean().optional(),
715
- singleton: z6.boolean().optional(),
716
- icon: z6.string().optional(),
717
- color: z6.string().optional(),
718
- note: z6.string().optional(),
719
- primaryKeyField: z6.string().optional(),
720
- primaryKeyType: z6.enum(["nanoid", "uuid", "integer", "bigInteger", "string"]).optional(),
721
- storageMode: z6.enum(["jsonb", "materialized", "physical", "external"]).optional(),
722
- displayTemplate: z6.string().optional(),
723
- sortField: z6.string().optional(),
724
- archiveField: z6.string().optional(),
725
- archiveValue: z6.string().optional(),
726
- unarchiveValue: z6.string().optional(),
727
- accountability: z6.enum(["all", "activity", "none"]).optional(),
728
- versioning: z6.boolean().optional()
725
+ label: z7.string().optional(),
726
+ pluralLabel: z7.string().optional(),
727
+ hidden: z7.boolean().optional(),
728
+ singleton: z7.boolean().optional(),
729
+ icon: z7.string().optional(),
730
+ color: z7.string().optional(),
731
+ note: z7.string().optional(),
732
+ primaryKeyField: z7.string().optional(),
733
+ primaryKeyType: z7.enum(["nanoid", "uuid", "integer", "bigInteger", "string"]).optional(),
734
+ storageMode: z7.enum(["jsonb", "materialized", "physical", "external"]).optional(),
735
+ displayTemplate: z7.string().optional(),
736
+ sortField: z7.string().optional(),
737
+ archiveField: z7.string().optional(),
738
+ archiveValue: z7.string().optional(),
739
+ unarchiveValue: z7.string().optional(),
740
+ accountability: z7.enum(["all", "activity", "none"]).optional(),
741
+ versioning: z7.boolean().optional()
729
742
  });
730
- var fieldInputSchema = z6.object({
731
- name: collectionNameSchema,
732
- type: z6.string().min(1),
733
- interface: z6.string().min(1),
734
- display: z6.string().optional(),
735
- label: z6.string().optional(),
736
- note: z6.string().optional(),
737
- nullable: z6.boolean().optional(),
738
- unique: z6.boolean().optional(),
739
- indexed: z6.boolean().optional(),
740
- searchable: z6.boolean().optional(),
741
- required: z6.boolean().optional(),
742
- readonly: z6.boolean().optional(),
743
- hidden: z6.boolean().optional(),
744
- width: z6.enum(["half", "full", "fill"]).optional(),
745
- sortOrder: z6.number().int().optional(),
746
- group: z6.string().optional(),
747
- options: z6.record(z6.unknown()).optional(),
748
- defaultValue: z6.unknown().optional()
743
+ var fieldInputSchema = z7.object({
744
+ name: fieldNameSchema,
745
+ type: z7.string().min(1),
746
+ interface: z7.string().min(1),
747
+ display: z7.string().optional(),
748
+ label: z7.string().optional(),
749
+ note: z7.string().optional(),
750
+ nullable: z7.boolean().optional(),
751
+ unique: z7.boolean().optional(),
752
+ indexed: z7.boolean().optional(),
753
+ searchable: z7.boolean().optional(),
754
+ required: z7.boolean().optional(),
755
+ readonly: z7.boolean().optional(),
756
+ hidden: z7.boolean().optional(),
757
+ width: z7.enum(["half", "full", "fill"]).optional(),
758
+ sortOrder: z7.number().int().optional(),
759
+ group: z7.string().optional(),
760
+ options: z7.record(z7.unknown()).optional(),
761
+ defaultValue: z7.unknown().optional()
749
762
  });
750
763
  function formatError2(err) {
751
764
  if (err instanceof LumiBaseApiError) {
@@ -773,11 +786,11 @@ function registerCollectionTools(server, client) {
773
786
  "get_collection",
774
787
  {
775
788
  description: "Get a collection with its compiled schema (all fields, system fields, meta).",
776
- inputSchema: { name: z6.string().min(1) }
789
+ inputSchema: { name: collectionNameSchema }
777
790
  },
778
791
  async ({ name }) => {
779
792
  try {
780
- const data = await client.get(`/collections/${name}/compiled`);
793
+ const data = await client.get(`/collections/${encodePathSegment(name)}/compiled`);
781
794
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
782
795
  } catch (err) {
783
796
  return { content: [{ type: "text", text: `Error: ${formatError2(err)}` }], isError: true };
@@ -804,13 +817,13 @@ function registerCollectionTools(server, client) {
804
817
  {
805
818
  description: "Update metadata on an existing collection (label, icon, note, accountability, etc.).",
806
819
  inputSchema: {
807
- name: z6.string().min(1),
820
+ name: collectionNameSchema,
808
821
  patch: collectionInputSchema.omit({ name: true }).partial()
809
822
  }
810
823
  },
811
824
  async ({ name, patch }) => {
812
825
  try {
813
- const data = await client.patch(`/collections/${name}`, patch);
826
+ const data = await client.patch(`/collections/${encodePathSegment(name)}`, patch);
814
827
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
815
828
  } catch (err) {
816
829
  return { content: [{ type: "text", text: `Error: ${formatError2(err)}` }], isError: true };
@@ -822,13 +835,13 @@ function registerCollectionTools(server, client) {
822
835
  {
823
836
  description: "Delete a collection and all its items. DESTRUCTIVE \u2014 cannot be undone. You MUST pass confirm=true explicitly after warning the user.",
824
837
  inputSchema: {
825
- name: z6.string().min(1),
826
- confirm: z6.literal(true).describe("Must be true to confirm destructive operation")
838
+ name: collectionNameSchema,
839
+ confirm: z7.literal(true).describe("Must be true to confirm destructive operation")
827
840
  }
828
841
  },
829
842
  async ({ name, confirm: _ }) => {
830
843
  try {
831
- await client.delete(`/collections/${name}`);
844
+ await client.delete(`/collections/${encodePathSegment(name)}`);
832
845
  return { content: [{ type: "text", text: `Collection "${name}" deleted.` }] };
833
846
  } catch (err) {
834
847
  return { content: [{ type: "text", text: `Error: ${formatError2(err)}` }], isError: true };
@@ -840,10 +853,10 @@ function registerCollectionTools(server, client) {
840
853
  {
841
854
  description: "Preview what would change if you applied a schema update. Returns added/modified/removed fields and any risky changes. Always call this before apply_schema to check for breaking changes.",
842
855
  inputSchema: {
843
- name: z6.string().min(1),
844
- fields: z6.array(fieldInputSchema).optional(),
845
- label: z6.string().optional(),
846
- note: z6.string().optional()
856
+ name: collectionNameSchema,
857
+ fields: z7.array(fieldInputSchema).optional(),
858
+ label: z7.string().optional(),
859
+ note: z7.string().optional()
847
860
  }
848
861
  },
849
862
  async (input) => {
@@ -860,23 +873,26 @@ function registerCollectionTools(server, client) {
860
873
  {
861
874
  description: "Atomically apply a schema migration to a collection (add/update/remove fields and relations). Call diff_schema first to preview changes. Pass confirmRiskyChange=true on field inputs that have risky changes.",
862
875
  inputSchema: {
863
- name: z6.string().min(1),
864
- fields: z6.array(
876
+ name: collectionNameSchema,
877
+ fields: z7.array(
865
878
  fieldInputSchema.extend({
866
- renameFrom: z6.string().optional(),
867
- confirmRiskyChange: z6.boolean().optional()
879
+ renameFrom: fieldNameSchema.optional(),
880
+ confirmRiskyChange: z7.boolean().optional()
868
881
  })
869
882
  ).optional(),
870
- label: z6.string().optional(),
871
- note: z6.string().optional(),
872
- accountability: z6.enum(["all", "activity", "none"]).optional(),
873
- versioning: z6.boolean().optional()
883
+ label: z7.string().optional(),
884
+ note: z7.string().optional(),
885
+ accountability: z7.enum(["all", "activity", "none"]).optional(),
886
+ versioning: z7.boolean().optional()
874
887
  }
875
888
  },
876
889
  async (input) => {
877
890
  try {
878
891
  const { name, ...rest } = input;
879
- const data = await client.put(`/collections/${name}/schema`, rest);
892
+ const data = await client.put(
893
+ `/collections/${encodePathSegment(name)}/schema`,
894
+ rest
895
+ );
880
896
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
881
897
  } catch (err) {
882
898
  return { content: [{ type: "text", text: `Error: ${formatError2(err)}` }], isError: true };
@@ -886,34 +902,34 @@ function registerCollectionTools(server, client) {
886
902
  }
887
903
 
888
904
  // src/tools/content-config.ts
889
- import { z as z7 } from "zod";
890
- var presetSchema = z7.object({
891
- bookmark: z7.string().nullable().optional(),
892
- collection: z7.string(),
893
- userId: z7.string().nullable().optional(),
894
- roleId: z7.string().nullable().optional(),
895
- layout: z7.string().optional(),
896
- layoutQuery: z7.record(z7.unknown()).optional(),
897
- layoutOptions: z7.record(z7.unknown()).optional(),
898
- search: z7.string().nullable().optional(),
899
- filter: z7.record(z7.unknown()).optional(),
900
- icon: z7.string().nullable().optional(),
901
- color: z7.string().nullable().optional(),
902
- refreshInterval: z7.number().int().min(0).optional()
905
+ import { z as z8 } from "zod";
906
+ var presetSchema = z8.object({
907
+ bookmark: z8.string().nullable().optional(),
908
+ collection: z8.string(),
909
+ userId: z8.string().nullable().optional(),
910
+ roleId: z8.string().nullable().optional(),
911
+ layout: z8.string().optional(),
912
+ layoutQuery: z8.record(z8.unknown()).optional(),
913
+ layoutOptions: z8.record(z8.unknown()).optional(),
914
+ search: z8.string().nullable().optional(),
915
+ filter: z8.record(z8.unknown()).optional(),
916
+ icon: z8.string().nullable().optional(),
917
+ color: z8.string().nullable().optional(),
918
+ refreshInterval: z8.number().int().min(0).optional()
903
919
  });
904
- var translationSchema = z7.object({
905
- language: z7.string(),
906
- namespace: z7.string(),
907
- key: z7.string(),
908
- value: z7.string(),
909
- status: z7.string().optional()
920
+ var translationSchema = z8.object({
921
+ language: z8.string(),
922
+ namespace: z8.string(),
923
+ key: z8.string(),
924
+ value: z8.string(),
925
+ status: z8.string().optional()
910
926
  });
911
927
  function registerContentConfigTools(server, client) {
912
928
  registerCrud(server, client, {
913
929
  basePath: "/presets",
914
930
  resource: "preset",
915
931
  namePrefix: "preset",
916
- listQuery: { collection: z7.string().optional() },
932
+ listQuery: { collection: z8.string().optional() },
917
933
  createSchema: presetSchema.shape,
918
934
  updateSchema: presetSchema.partial().shape
919
935
  });
@@ -922,8 +938,8 @@ function registerContentConfigTools(server, client) {
922
938
  resource: "translation",
923
939
  namePrefix: "translation",
924
940
  listQuery: {
925
- namespace: z7.string().optional(),
926
- language: z7.string().optional()
941
+ namespace: z8.string().optional(),
942
+ language: z8.string().optional()
927
943
  },
928
944
  createSchema: translationSchema.shape,
929
945
  updateSchema: translationSchema.partial().shape
@@ -932,7 +948,7 @@ function registerContentConfigTools(server, client) {
932
948
  "list_settings",
933
949
  {
934
950
  description: "List site settings, optionally filtered by scope.",
935
- inputSchema: { scope: z7.string().optional() }
951
+ inputSchema: { scope: z8.string().optional() }
936
952
  },
937
953
  async ({ scope }) => run(() => client.get(`/settings${scope ? `?scope=${encodeURIComponent(scope)}` : ""}`))
938
954
  );
@@ -940,18 +956,18 @@ function registerContentConfigTools(server, client) {
940
956
  "get_setting",
941
957
  {
942
958
  description: "Get a single setting by key.",
943
- inputSchema: { key: z7.string().min(1) }
959
+ inputSchema: { key: idPathSegmentSchema }
944
960
  },
945
- async ({ key }) => run(() => client.get(`/settings/${encodeURIComponent(key)}`))
961
+ async ({ key }) => run(() => client.get(`/settings/${encodePathSegment(key)}`))
946
962
  );
947
963
  server.registerTool(
948
964
  "upsert_setting",
949
965
  {
950
966
  description: "Create or update a setting (upsert by key).",
951
967
  inputSchema: {
952
- key: z7.string().min(1),
953
- value: z7.record(z7.unknown()).describe("Arbitrary JSON value object for the setting."),
954
- scope: z7.string().optional()
968
+ key: idPathSegmentSchema,
969
+ value: z8.record(z8.unknown()).describe("Arbitrary JSON value object for the setting."),
970
+ scope: z8.string().optional()
955
971
  }
956
972
  },
957
973
  async (input) => run(() => client.post("/settings", input))
@@ -961,29 +977,29 @@ function registerContentConfigTools(server, client) {
961
977
  {
962
978
  description: "Delete a setting by key. DESTRUCTIVE \u2014 warn the user first and pass confirm=true.",
963
979
  inputSchema: {
964
- key: z7.string().min(1),
965
- confirm: z7.literal(true).describe(confirmDescription)
980
+ key: idPathSegmentSchema,
981
+ confirm: z8.literal(true).describe(confirmDescription)
966
982
  }
967
983
  },
968
984
  async ({ key }) => run(async () => {
969
- await client.delete(`/settings/${encodeURIComponent(key)}`);
985
+ await client.delete(`/settings/${encodePathSegment(key)}`);
970
986
  return okText(`Setting "${key}" deleted.`);
971
987
  })
972
988
  );
973
989
  }
974
990
 
975
991
  // src/tools/extensions.ts
976
- import { z as z8 } from "zod";
992
+ import { z as z9 } from "zod";
977
993
  var EXTENSION_TYPES = ["interface", "display", "layout", "panel", "module", "hook", "endpoint"];
978
- var extensionSchema = z8.object({
979
- key: z8.string().regex(/^[a-z0-9_:-]+$/).optional(),
980
- name: z8.string().min(1),
981
- version: z8.string().min(1),
982
- type: z8.enum(EXTENSION_TYPES),
983
- enabled: z8.boolean().optional(),
984
- bundleUrl: z8.string().min(1).describe("https:, http:, or data:text/javascript bundle URL."),
985
- manifest: z8.record(z8.string()).optional(),
986
- capabilities: z8.array(z8.string()).optional()
994
+ var extensionSchema = z9.object({
995
+ key: z9.string().regex(/^[a-z0-9_:-]+$/).optional(),
996
+ name: z9.string().min(1),
997
+ version: z9.string().min(1),
998
+ type: z9.enum(EXTENSION_TYPES),
999
+ enabled: z9.boolean().optional(),
1000
+ bundleUrl: z9.string().min(1).describe("https:, http:, or data:text/javascript bundle URL."),
1001
+ manifest: z9.record(z9.string()).optional(),
1002
+ capabilities: z9.array(z9.string()).optional()
987
1003
  });
988
1004
  function registerExtensionTools(server, client) {
989
1005
  server.registerTool(
@@ -1003,18 +1019,18 @@ function registerExtensionTools(server, client) {
1003
1019
  "update_extension",
1004
1020
  {
1005
1021
  description: "Update an installed extension (enable/disable, version, config). Partial PATCH.",
1006
- inputSchema: { id: z8.string().min(1), ...extensionSchema.partial().shape }
1022
+ inputSchema: { id: idPathSegmentSchema, ...extensionSchema.partial().shape }
1007
1023
  },
1008
- async ({ id, ...patch }) => run(() => client.patch(`/extensions/${id}`, patch))
1024
+ async ({ id, ...patch }) => run(() => client.patch(`/extensions/${encodePathSegment(id)}`, patch))
1009
1025
  );
1010
1026
  server.registerTool(
1011
1027
  "uninstall_extension",
1012
1028
  {
1013
1029
  description: "Uninstall an extension from the site. DESTRUCTIVE \u2014 pass confirm=true.",
1014
- inputSchema: { id: z8.string().min(1), confirm: z8.literal(true).describe(confirmDescription) }
1030
+ inputSchema: { id: idPathSegmentSchema, confirm: z9.literal(true).describe(confirmDescription) }
1015
1031
  },
1016
1032
  async ({ id }) => run(async () => {
1017
- await client.delete(`/extensions/${id}`);
1033
+ await client.delete(`/extensions/${encodePathSegment(id)}`);
1018
1034
  return okText(`Extension "${id}" uninstalled.`);
1019
1035
  })
1020
1036
  );
@@ -1023,12 +1039,12 @@ function registerExtensionTools(server, client) {
1023
1039
  {
1024
1040
  description: "Browse the published extension marketplace.",
1025
1041
  inputSchema: {
1026
- q: z8.string().optional(),
1027
- category: z8.string().optional(),
1028
- tags: z8.string().optional().describe("Comma-separated tags."),
1029
- sort: z8.string().optional(),
1030
- page: z8.number().int().min(1).optional(),
1031
- perPage: z8.number().int().min(1).optional()
1042
+ q: z9.string().optional(),
1043
+ category: z9.string().optional(),
1044
+ tags: z9.string().optional().describe("Comma-separated tags."),
1045
+ sort: z9.string().optional(),
1046
+ page: z9.number().int().min(1).optional(),
1047
+ perPage: z9.number().int().min(1).optional()
1032
1048
  }
1033
1049
  },
1034
1050
  async (args) => run(
@@ -1041,9 +1057,9 @@ function registerExtensionTools(server, client) {
1041
1057
  "get_marketplace_extension",
1042
1058
  {
1043
1059
  description: "Get a single marketplace extension by slug.",
1044
- inputSchema: { slug: z8.string().min(1) }
1060
+ inputSchema: { slug: idPathSegmentSchema }
1045
1061
  },
1046
- async ({ slug }) => run(() => client.get(`/marketplace/extensions/${slug}`))
1062
+ async ({ slug }) => run(() => client.get(`/marketplace/extensions/${encodePathSegment(slug)}`))
1047
1063
  );
1048
1064
  server.registerTool(
1049
1065
  "list_marketplace_updates",
@@ -1054,22 +1070,22 @@ function registerExtensionTools(server, client) {
1054
1070
  "install_marketplace_extension",
1055
1071
  {
1056
1072
  description: "Install a published marketplace extension onto the active site by slug.",
1057
- inputSchema: { slug: z8.string().min(1) }
1073
+ inputSchema: { slug: idPathSegmentSchema }
1058
1074
  },
1059
- async ({ slug }) => run(() => client.post(`/marketplace/extensions/${slug}/install`, {}))
1075
+ async ({ slug }) => run(() => client.post(`/marketplace/extensions/${encodePathSegment(slug)}/install`, {}))
1060
1076
  );
1061
1077
  server.registerTool(
1062
1078
  "publish_extension",
1063
1079
  {
1064
1080
  description: "Publish an extension to the marketplace (signs + marks it published).",
1065
1081
  inputSchema: {
1066
- extensionId: z8.string().min(1),
1067
- marketplaceSlug: z8.string().min(1),
1068
- publisher: z8.record(z8.unknown()).optional(),
1069
- signature: z8.string().optional(),
1070
- signatureAlg: z8.string().optional(),
1071
- publisherKeyId: z8.string().optional(),
1072
- bundleSha256: z8.string().optional()
1082
+ extensionId: z9.string().min(1),
1083
+ marketplaceSlug: z9.string().min(1),
1084
+ publisher: z9.record(z9.unknown()).optional(),
1085
+ signature: z9.string().optional(),
1086
+ signatureAlg: z9.string().optional(),
1087
+ publisherKeyId: z9.string().optional(),
1088
+ bundleSha256: z9.string().optional()
1073
1089
  }
1074
1090
  },
1075
1091
  async (input) => run(() => client.post("/marketplace/publish", input))
@@ -1077,40 +1093,39 @@ function registerExtensionTools(server, client) {
1077
1093
  }
1078
1094
 
1079
1095
  // src/tools/fields.ts
1080
- import { z as z9 } from "zod";
1081
- var namePattern = /^[a-z][a-z0-9_]{0,62}$/;
1082
- var fieldInputSchema2 = z9.object({
1083
- type: z9.string().min(1).describe(
1096
+ import { z as z10 } from "zod";
1097
+ var fieldInputSchema2 = z10.object({
1098
+ type: z10.string().min(1).describe(
1084
1099
  "Storage type: string, text, integer, bigInteger, float, decimal, boolean, date, dateTime, time, json, uuid, csv, hash, alias"
1085
1100
  ),
1086
- interface: z9.string().min(1).describe(
1101
+ interface: z10.string().min(1).describe(
1087
1102
  "UI widget: input, textarea, select, toggle, datetime, file, image, repeater, relation-m2o, relation-o2m, relation-m2m, code, markdown, wysiwyg, \u2026"
1088
1103
  ),
1089
- display: z9.string().optional(),
1090
- label: z9.string().optional(),
1091
- note: z9.string().optional(),
1092
- defaultValue: z9.unknown().optional(),
1093
- nullable: z9.boolean().optional().default(true),
1094
- unique: z9.boolean().optional().default(false),
1095
- indexed: z9.boolean().optional().default(false),
1096
- searchable: z9.boolean().optional().default(false),
1097
- length: z9.number().int().positive().optional(),
1098
- precision: z9.number().int().positive().optional(),
1099
- scale: z9.number().int().min(0).optional(),
1100
- special: z9.array(z9.string()).optional(),
1101
- options: z9.record(z9.unknown()).optional(),
1102
- displayOptions: z9.record(z9.unknown()).optional(),
1103
- conditions: z9.array(z9.unknown()).optional(),
1104
- required: z9.boolean().optional().default(false),
1105
- readonly: z9.boolean().optional().default(false),
1106
- hidden: z9.boolean().optional().default(false),
1107
- encrypted: z9.boolean().optional().default(false),
1108
- versioned: z9.boolean().optional().default(false),
1109
- width: z9.enum(["half", "full", "fill"]).optional().default("full"),
1110
- group: z9.string().optional(),
1111
- sortOrder: z9.number().int().optional(),
1112
- renameFrom: z9.string().regex(namePattern).optional().describe("Previous field name if this is a rename operation"),
1113
- confirmRiskyChange: z9.boolean().optional().describe("Set true to confirm type-change or destructive migration")
1104
+ display: z10.string().optional(),
1105
+ label: z10.string().optional(),
1106
+ note: z10.string().optional(),
1107
+ defaultValue: z10.unknown().optional(),
1108
+ nullable: z10.boolean().optional().default(true),
1109
+ unique: z10.boolean().optional().default(false),
1110
+ indexed: z10.boolean().optional().default(false),
1111
+ searchable: z10.boolean().optional().default(false),
1112
+ length: z10.number().int().positive().optional(),
1113
+ precision: z10.number().int().positive().optional(),
1114
+ scale: z10.number().int().min(0).optional(),
1115
+ special: z10.array(z10.string()).optional(),
1116
+ options: z10.record(z10.unknown()).optional(),
1117
+ displayOptions: z10.record(z10.unknown()).optional(),
1118
+ conditions: z10.array(z10.unknown()).optional(),
1119
+ required: z10.boolean().optional().default(false),
1120
+ readonly: z10.boolean().optional().default(false),
1121
+ hidden: z10.boolean().optional().default(false),
1122
+ encrypted: z10.boolean().optional().default(false),
1123
+ versioned: z10.boolean().optional().default(false),
1124
+ width: z10.enum(["half", "full", "fill"]).optional().default("full"),
1125
+ group: z10.string().optional(),
1126
+ sortOrder: z10.number().int().optional(),
1127
+ renameFrom: fieldNameSchema.optional().describe("Previous field name if this is a rename operation"),
1128
+ confirmRiskyChange: z10.boolean().optional().describe("Set true to confirm type-change or destructive migration")
1114
1129
  });
1115
1130
  function formatError3(err) {
1116
1131
  if (err instanceof LumiBaseApiError) {
@@ -1124,12 +1139,12 @@ function registerFieldTools(server, client) {
1124
1139
  {
1125
1140
  description: "List all fields in a collection, including system fields.",
1126
1141
  inputSchema: {
1127
- collection: z9.string().min(1)
1142
+ collection: collectionNameSchema
1128
1143
  }
1129
1144
  },
1130
1145
  async ({ collection }) => {
1131
1146
  try {
1132
- const data = await client.get(`/collections/${collection}/fields`);
1147
+ const data = await client.get(`/collections/${encodePathSegment(collection)}/fields`);
1133
1148
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1134
1149
  } catch (err) {
1135
1150
  return { content: [{ type: "text", text: `Error: ${formatError3(err)}` }], isError: true };
@@ -1141,15 +1156,15 @@ function registerFieldTools(server, client) {
1141
1156
  {
1142
1157
  description: 'Create or update a field in a collection. field_name must be lowercase snake_case (e.g. "published_at"). Common types: string (varchar), text (longtext), integer, boolean, dateTime, json, uuid. Common interfaces: input, textarea, datetime, toggle, select, file, markdown.',
1143
1158
  inputSchema: {
1144
- collection: z9.string().min(1),
1145
- field_name: z9.string().regex(namePattern, "Must be lowercase snake_case, start with a letter").describe("Machine name of the field"),
1159
+ collection: collectionNameSchema,
1160
+ field_name: fieldNameSchema.describe("Machine name of the field"),
1146
1161
  ...fieldInputSchema2.shape
1147
1162
  }
1148
1163
  },
1149
1164
  async ({ collection, field_name, ...fieldInput }) => {
1150
1165
  try {
1151
1166
  const data = await client.put(
1152
- `/collections/${collection}/fields/${field_name}`,
1167
+ `/collections/${encodePathSegment(collection)}/fields/${encodePathSegment(field_name)}`,
1153
1168
  fieldInput
1154
1169
  );
1155
1170
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
@@ -1163,16 +1178,18 @@ function registerFieldTools(server, client) {
1163
1178
  {
1164
1179
  description: "Delete a field from a collection. Data stored in this field will be lost. Pass confirm=true to confirm the destructive operation.",
1165
1180
  inputSchema: {
1166
- collection: z9.string().min(1),
1167
- field_name: z9.string().min(1),
1168
- confirm: z9.literal(true).describe("Must be true to confirm destructive operation"),
1169
- force: z9.boolean().optional().describe("Force deletion even if risky (foreign keys, etc.)")
1181
+ collection: collectionNameSchema,
1182
+ field_name: fieldNameSchema,
1183
+ confirm: z10.literal(true).describe("Must be true to confirm destructive operation"),
1184
+ force: z10.boolean().optional().describe("Force deletion even if risky (foreign keys, etc.)")
1170
1185
  }
1171
1186
  },
1172
1187
  async ({ collection, field_name, force }) => {
1173
1188
  try {
1174
1189
  const qs = force ? "?force=true" : "";
1175
- await client.delete(`/collections/${collection}/fields/${field_name}${qs}`);
1190
+ await client.delete(
1191
+ `/collections/${encodePathSegment(collection)}/fields/${encodePathSegment(field_name)}${qs}`
1192
+ );
1176
1193
  return {
1177
1194
  content: [{ type: "text", text: `Field "${field_name}" deleted from "${collection}".` }]
1178
1195
  };
@@ -1184,7 +1201,7 @@ function registerFieldTools(server, client) {
1184
1201
  }
1185
1202
 
1186
1203
  // src/tools/items.ts
1187
- import { z as z10 } from "zod";
1204
+ import { z as z11 } from "zod";
1188
1205
  function formatError4(err) {
1189
1206
  if (err instanceof LumiBaseApiError) {
1190
1207
  return err.errors.map((e) => `[${e.code}] ${e.message}`).join("; ");
@@ -1205,19 +1222,19 @@ function registerItemTools(server, client) {
1205
1222
  {
1206
1223
  description: "List items from a collection with optional filtering, sorting, and pagination.",
1207
1224
  inputSchema: {
1208
- collection: z10.string().min(1),
1209
- limit: z10.number().int().min(1).max(200).optional().default(25),
1210
- offset: z10.number().int().min(0).optional().default(0),
1211
- status: z10.enum(["draft", "published", "archived"]).optional(),
1212
- sort: z10.string().optional().describe('Comma-separated field names; prefix with - for descending (e.g. "-created_at")'),
1213
- fields: z10.string().optional().describe('Comma-separated field names to return (e.g. "id,title,status")'),
1214
- search: z10.string().optional().describe("Full-text search across searchable fields")
1225
+ collection: collectionNameSchema,
1226
+ limit: z11.number().int().min(1).max(200).optional().default(25),
1227
+ offset: z11.number().int().min(0).optional().default(0),
1228
+ status: z11.enum(["draft", "published", "archived"]).optional(),
1229
+ sort: z11.string().optional().describe('Comma-separated field names; prefix with - for descending (e.g. "-created_at")'),
1230
+ fields: z11.string().optional().describe('Comma-separated field names to return (e.g. "id,title,status")'),
1231
+ search: z11.string().optional().describe("Full-text search across searchable fields")
1215
1232
  }
1216
1233
  },
1217
1234
  async ({ collection, ...params }) => {
1218
1235
  try {
1219
1236
  const qs = buildQs2(params);
1220
- const data = await client.get(`/items/${collection}${qs}`);
1237
+ const data = await client.get(`/items/${encodePathSegment(collection)}${qs}`);
1221
1238
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1222
1239
  } catch (err) {
1223
1240
  return { content: [{ type: "text", text: `Error: ${formatError4(err)}` }], isError: true };
@@ -1229,15 +1246,17 @@ function registerItemTools(server, client) {
1229
1246
  {
1230
1247
  description: "Get a single item by ID from a collection.",
1231
1248
  inputSchema: {
1232
- collection: z10.string().min(1),
1233
- id: z10.string().min(1),
1234
- fields: z10.string().optional().describe("Comma-separated field names to return")
1249
+ collection: collectionNameSchema,
1250
+ id: idPathSegmentSchema,
1251
+ fields: z11.string().optional().describe("Comma-separated field names to return")
1235
1252
  }
1236
1253
  },
1237
1254
  async ({ collection, id, fields }) => {
1238
1255
  try {
1239
1256
  const qs = buildQs2({ fields });
1240
- const data = await client.get(`/items/${collection}/${id}${qs}`);
1257
+ const data = await client.get(
1258
+ `/items/${encodePathSegment(collection)}/${encodePathSegment(id)}${qs}`
1259
+ );
1241
1260
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1242
1261
  } catch (err) {
1243
1262
  return { content: [{ type: "text", text: `Error: ${formatError4(err)}` }], isError: true };
@@ -1249,14 +1268,14 @@ function registerItemTools(server, client) {
1249
1268
  {
1250
1269
  description: "Create a new item in a collection.",
1251
1270
  inputSchema: {
1252
- collection: z10.string().min(1),
1253
- data: z10.record(z10.unknown()).describe("Field values for the new item"),
1254
- status: z10.enum(["draft", "published"]).optional().default("draft")
1271
+ collection: collectionNameSchema,
1272
+ data: z11.record(z11.unknown()).describe("Field values for the new item"),
1273
+ status: z11.enum(["draft", "published"]).optional().default("draft")
1255
1274
  }
1256
1275
  },
1257
1276
  async ({ collection, data: itemData, status }) => {
1258
1277
  try {
1259
- const data = await client.post(`/items/${collection}`, {
1278
+ const data = await client.post(`/items/${encodePathSegment(collection)}`, {
1260
1279
  ...itemData,
1261
1280
  status
1262
1281
  });
@@ -1271,14 +1290,17 @@ function registerItemTools(server, client) {
1271
1290
  {
1272
1291
  description: "Partially update an item (PATCH \u2014 only provided fields are changed).",
1273
1292
  inputSchema: {
1274
- collection: z10.string().min(1),
1275
- id: z10.string().min(1),
1276
- data: z10.record(z10.unknown()).describe("Fields to update")
1293
+ collection: collectionNameSchema,
1294
+ id: idPathSegmentSchema,
1295
+ data: z11.record(z11.unknown()).describe("Fields to update")
1277
1296
  }
1278
1297
  },
1279
1298
  async ({ collection, id, data: itemData }) => {
1280
1299
  try {
1281
- const data = await client.patch(`/items/${collection}/${id}`, itemData);
1300
+ const data = await client.patch(
1301
+ `/items/${encodePathSegment(collection)}/${encodePathSegment(id)}`,
1302
+ itemData
1303
+ );
1282
1304
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1283
1305
  } catch (err) {
1284
1306
  return { content: [{ type: "text", text: `Error: ${formatError4(err)}` }], isError: true };
@@ -1290,14 +1312,14 @@ function registerItemTools(server, client) {
1290
1312
  {
1291
1313
  description: "Soft-delete an item (sets deleted_at, recoverable). Pass confirm=true.",
1292
1314
  inputSchema: {
1293
- collection: z10.string().min(1),
1294
- id: z10.string().min(1),
1295
- confirm: z10.literal(true).describe("Must be true to confirm deletion")
1315
+ collection: collectionNameSchema,
1316
+ id: idPathSegmentSchema,
1317
+ confirm: z11.literal(true).describe("Must be true to confirm deletion")
1296
1318
  }
1297
1319
  },
1298
1320
  async ({ collection, id }) => {
1299
1321
  try {
1300
- await client.delete(`/items/${collection}/${id}`);
1322
+ await client.delete(`/items/${encodePathSegment(collection)}/${encodePathSegment(id)}`);
1301
1323
  return { content: [{ type: "text", text: `Item "${id}" deleted from "${collection}".` }] };
1302
1324
  } catch (err) {
1303
1325
  return { content: [{ type: "text", text: `Error: ${formatError4(err)}` }], isError: true };
@@ -1307,15 +1329,15 @@ function registerItemTools(server, client) {
1307
1329
  }
1308
1330
 
1309
1331
  // src/tools/ops.ts
1310
- import { z as z11 } from "zod";
1332
+ import { z as z12 } from "zod";
1311
1333
  function registerOpsTools(server, client) {
1312
1334
  server.registerTool(
1313
1335
  "list_activity",
1314
1336
  {
1315
1337
  description: "List the site activity / audit trail (most recent first).",
1316
1338
  inputSchema: {
1317
- limit: z11.number().int().min(1).max(500).optional(),
1318
- offset: z11.number().int().min(0).optional()
1339
+ limit: z12.number().int().min(1).max(500).optional(),
1340
+ offset: z12.number().int().min(0).optional()
1319
1341
  }
1320
1342
  },
1321
1343
  async (args) => run(
@@ -1337,7 +1359,7 @@ function registerOpsTools(server, client) {
1337
1359
  }
1338
1360
 
1339
1361
  // src/tools/permissions.ts
1340
- import { z as z12 } from "zod";
1362
+ import { z as z13 } from "zod";
1341
1363
  function registerPermissionTools(server, client) {
1342
1364
  server.registerTool(
1343
1365
  "get_my_permissions",
@@ -1352,9 +1374,9 @@ function registerPermissionTools(server, client) {
1352
1374
  {
1353
1375
  description: "Evaluate whether the current principal may perform an action on a collection (optionally against a specific item), returning { allowed, reason, fields }.",
1354
1376
  inputSchema: {
1355
- collection: z12.string().min(1),
1356
- action: z12.enum(["create", "read", "update", "delete", "share"]),
1357
- item: z12.record(z12.unknown()).optional().describe("Item payload to evaluate row-level rules against.")
1377
+ collection: z13.string().min(1),
1378
+ action: z13.enum(["create", "read", "update", "delete", "share"]),
1379
+ item: z13.record(z13.unknown()).optional().describe("Item payload to evaluate row-level rules against.")
1358
1380
  }
1359
1381
  },
1360
1382
  async (input) => run(() => client.post("/permissions/check", input))
@@ -1362,21 +1384,21 @@ function registerPermissionTools(server, client) {
1362
1384
  }
1363
1385
 
1364
1386
  // src/tools/relations.ts
1365
- import { z as z13 } from "zod";
1387
+ import { z as z14 } from "zod";
1366
1388
  var relationInputSchema = {
1367
- manyCollection: z13.string().min(1).describe('Collection that holds the foreign key (the "many" side).'),
1368
- manyField: z13.string().min(1).describe("Field on manyCollection that stores the relation."),
1369
- oneCollection: z13.string().min(1).describe('Related collection (the "one" side).'),
1370
- oneField: z13.string().nullable().optional(),
1371
- junctionCollection: z13.string().nullable().optional().describe("Junction table for m2m relations."),
1372
- type: z13.enum(["m2o", "o2m", "m2m", "m2a"]).optional(),
1373
- aliasField: z13.string().nullable().optional(),
1374
- relatedDisplayTemplate: z13.string().nullable().optional(),
1375
- junctionManyField: z13.string().nullable().optional(),
1376
- junctionOneField: z13.string().nullable().optional(),
1377
- sortField: z13.string().nullable().optional(),
1378
- onDelete: z13.enum(["restrict", "cascade", "set null", "no action"]).optional(),
1379
- meta: z13.record(z13.unknown()).optional()
1389
+ manyCollection: z14.string().min(1).describe('Collection that holds the foreign key (the "many" side).'),
1390
+ manyField: z14.string().min(1).describe("Field on manyCollection that stores the relation."),
1391
+ oneCollection: z14.string().min(1).describe('Related collection (the "one" side).'),
1392
+ oneField: z14.string().nullable().optional(),
1393
+ junctionCollection: z14.string().nullable().optional().describe("Junction table for m2m relations."),
1394
+ type: z14.enum(["m2o", "o2m", "m2m", "m2a"]).optional(),
1395
+ aliasField: z14.string().nullable().optional(),
1396
+ relatedDisplayTemplate: z14.string().nullable().optional(),
1397
+ junctionManyField: z14.string().nullable().optional(),
1398
+ junctionOneField: z14.string().nullable().optional(),
1399
+ sortField: z14.string().nullable().optional(),
1400
+ onDelete: z14.enum(["restrict", "cascade", "set null", "no action"]).optional(),
1401
+ meta: z14.record(z14.unknown()).optional()
1380
1402
  };
1381
1403
  function registerRelationTools(server, client) {
1382
1404
  server.registerTool(
@@ -1397,31 +1419,31 @@ function registerRelationTools(server, client) {
1397
1419
  {
1398
1420
  description: "Delete a relation by id. DESTRUCTIVE \u2014 warn the user first and pass confirm=true.",
1399
1421
  inputSchema: {
1400
- id: z13.string().min(1),
1401
- confirm: z13.literal(true).describe(confirmDescription)
1422
+ id: idPathSegmentSchema,
1423
+ confirm: z14.literal(true).describe(confirmDescription)
1402
1424
  }
1403
1425
  },
1404
1426
  async ({ id }) => run(async () => {
1405
- await client.delete(`/relations/${id}`);
1427
+ await client.delete(`/relations/${encodePathSegment(id)}`);
1406
1428
  return okText(`Relation "${id}" deleted.`);
1407
1429
  })
1408
1430
  );
1409
1431
  }
1410
1432
 
1411
1433
  // src/tools/search-media.ts
1412
- import { z as z14 } from "zod";
1434
+ import { z as z15 } from "zod";
1413
1435
  function registerSearchMediaTools(server, client) {
1414
1436
  server.registerTool(
1415
1437
  "search",
1416
1438
  {
1417
1439
  description: "Full-text search within a collection. Returns ranked hits from the search backend. The `collection` parameter is required.",
1418
1440
  inputSchema: {
1419
- q: z14.string().min(1).describe("Query string."),
1420
- collection: z14.string().min(1).describe("Collection to search."),
1421
- filter: z14.string().optional().describe("Backend filter expression."),
1422
- sort: z14.string().optional().describe("Comma-separated sort fields."),
1423
- limit: z14.number().int().min(1).max(200).optional(),
1424
- offset: z14.number().int().min(0).optional()
1441
+ q: z15.string().min(1).describe("Query string."),
1442
+ collection: z15.string().min(1).describe("Collection to search."),
1443
+ filter: z15.string().optional().describe("Backend filter expression."),
1444
+ sort: z15.string().optional().describe("Comma-separated sort fields."),
1445
+ limit: z15.number().int().min(1).max(200).optional(),
1446
+ offset: z15.number().int().min(0).optional()
1425
1447
  }
1426
1448
  },
1427
1449
  async (args) => run(
@@ -1434,7 +1456,7 @@ function registerSearchMediaTools(server, client) {
1434
1456
  "list_media",
1435
1457
  {
1436
1458
  description: "List media asset keys, optionally filtered by key prefix.",
1437
- inputSchema: { prefix: z14.string().optional() }
1459
+ inputSchema: { prefix: z15.string().optional() }
1438
1460
  },
1439
1461
  async ({ prefix }) => run(() => client.get(`/media${prefix ? `?prefix=${encodeURIComponent(prefix)}` : ""}`))
1440
1462
  );
@@ -1443,27 +1465,27 @@ function registerSearchMediaTools(server, client) {
1443
1465
  {
1444
1466
  description: "Delete a media asset by key. DESTRUCTIVE \u2014 warn the user first and pass confirm=true.",
1445
1467
  inputSchema: {
1446
- key: z14.string().min(1).describe("Full storage key of the asset."),
1447
- confirm: z14.literal(true).describe(confirmDescription)
1468
+ key: mediaKeySchema.describe("Full storage key of the asset."),
1469
+ confirm: z15.literal(true).describe(confirmDescription)
1448
1470
  }
1449
1471
  },
1450
1472
  async ({ key }) => run(async () => {
1451
- await client.delete(`/media/${key}`);
1473
+ await client.delete(`/media/${encodeMediaKey(key)}`);
1452
1474
  return okText(`Media asset "${key}" deleted.`);
1453
1475
  })
1454
1476
  );
1455
1477
  }
1456
1478
 
1457
1479
  // src/tools/translation-memory.ts
1458
- import { z as z15 } from "zod";
1480
+ import { z as z16 } from "zod";
1459
1481
  function registerTranslationMemoryTools(server, client) {
1460
1482
  server.registerTool(
1461
1483
  "list_tm",
1462
1484
  {
1463
1485
  description: "List translation-memory entries, optionally filtered by source/target language.",
1464
1486
  inputSchema: {
1465
- source: z15.string().optional().describe("Source language code."),
1466
- target: z15.string().optional().describe("Target language code.")
1487
+ source: z16.string().optional().describe("Source language code."),
1488
+ target: z16.string().optional().describe("Target language code.")
1467
1489
  }
1468
1490
  },
1469
1491
  async (args) => run(
@@ -1475,14 +1497,14 @@ function registerTranslationMemoryTools(server, client) {
1475
1497
  {
1476
1498
  description: "Add or update a translation-memory entry.",
1477
1499
  inputSchema: {
1478
- sourceLang: z15.string().min(2),
1479
- targetLang: z15.string().min(2),
1480
- sourceText: z15.string().min(1),
1481
- targetText: z15.string().min(1),
1482
- context: z15.string().optional(),
1483
- quality: z15.number().min(0).max(100).optional(),
1484
- source: z15.enum(["human", "mt", "imported"]).optional(),
1485
- provider: z15.string().optional()
1500
+ sourceLang: z16.string().min(2),
1501
+ targetLang: z16.string().min(2),
1502
+ sourceText: z16.string().min(1),
1503
+ targetText: z16.string().min(1),
1504
+ context: z16.string().optional(),
1505
+ quality: z16.number().min(0).max(100).optional(),
1506
+ source: z16.enum(["human", "mt", "imported"]).optional(),
1507
+ provider: z16.string().optional()
1486
1508
  }
1487
1509
  },
1488
1510
  async (input) => run(() => client.post("/tm", input))
@@ -1492,10 +1514,10 @@ function registerTranslationMemoryTools(server, client) {
1492
1514
  {
1493
1515
  description: "Fuzzy-match a query string against translation memory for a language pair.",
1494
1516
  inputSchema: {
1495
- query: z15.string().min(1),
1496
- sourceLang: z15.string().min(2),
1497
- targetLang: z15.string().min(2),
1498
- threshold: z15.number().min(0).max(100).optional().describe("Minimum match score (default 75).")
1517
+ query: z16.string().min(1),
1518
+ sourceLang: z16.string().min(2),
1519
+ targetLang: z16.string().min(2),
1520
+ threshold: z16.number().min(0).max(100).optional().describe("Minimum match score (default 75).")
1499
1521
  }
1500
1522
  },
1501
1523
  async (input) => run(() => client.post("/tm/lookup", input))
@@ -1505,10 +1527,10 @@ function registerTranslationMemoryTools(server, client) {
1505
1527
  {
1506
1528
  description: "Run the full translation pipeline (TM + glossary + MT provider) for a text.",
1507
1529
  inputSchema: {
1508
- text: z15.string().min(1),
1509
- from: z15.string().min(2),
1510
- to: z15.string().min(2),
1511
- provider: z15.string().optional()
1530
+ text: z16.string().min(1),
1531
+ from: z16.string().min(2),
1532
+ to: z16.string().min(2),
1533
+ provider: z16.string().optional()
1512
1534
  }
1513
1535
  },
1514
1536
  async (input) => run(() => client.post("/tm/translate", input))
@@ -1516,7 +1538,7 @@ function registerTranslationMemoryTools(server, client) {
1516
1538
  }
1517
1539
 
1518
1540
  // src/tools/users-teams.ts
1519
- import { z as z16 } from "zod";
1541
+ import { z as z17 } from "zod";
1520
1542
  function registerUsersTeamsTools(server, client) {
1521
1543
  server.registerTool(
1522
1544
  "list_users",
@@ -1525,16 +1547,16 @@ function registerUsersTeamsTools(server, client) {
1525
1547
  );
1526
1548
  server.registerTool(
1527
1549
  "get_user",
1528
- { description: "Get a single user in the active site by id.", inputSchema: { id: z16.string().min(1) } },
1529
- async ({ id }) => run(() => client.get(`/users/${id}`))
1550
+ { description: "Get a single user in the active site by id.", inputSchema: { id: idPathSegmentSchema } },
1551
+ async ({ id }) => run(() => client.get(`/users/${encodePathSegment(id)}`))
1530
1552
  );
1531
1553
  server.registerTool(
1532
1554
  "invite_user",
1533
1555
  {
1534
1556
  description: "Invite a user to the site by email, optionally assigning a role. Sends an invite email.",
1535
1557
  inputSchema: {
1536
- email: z16.string().email(),
1537
- roleId: z16.string().optional()
1558
+ email: z17.string().email(),
1559
+ roleId: z17.string().optional()
1538
1560
  }
1539
1561
  },
1540
1562
  async (input) => run(() => client.post("/users/invite", input))
@@ -1544,21 +1566,21 @@ function registerUsersTeamsTools(server, client) {
1544
1566
  {
1545
1567
  description: "Update a user's site membership (role and/or status).",
1546
1568
  inputSchema: {
1547
- id: z16.string().min(1),
1548
- roleId: z16.string().nullable().optional(),
1549
- status: z16.string().optional().describe("e.g. active, suspended.")
1569
+ id: idPathSegmentSchema,
1570
+ roleId: z17.string().nullable().optional(),
1571
+ status: z17.string().optional().describe("e.g. active, suspended.")
1550
1572
  }
1551
1573
  },
1552
- async ({ id, ...patch }) => run(() => client.patch(`/users/${id}`, patch))
1574
+ async ({ id, ...patch }) => run(() => client.patch(`/users/${encodePathSegment(id)}`, patch))
1553
1575
  );
1554
1576
  server.registerTool(
1555
1577
  "remove_user",
1556
1578
  {
1557
1579
  description: "Remove a user from the site. DESTRUCTIVE \u2014 pass confirm=true.",
1558
- inputSchema: { id: z16.string().min(1), confirm: z16.literal(true).describe(confirmDescription) }
1580
+ inputSchema: { id: idPathSegmentSchema, confirm: z17.literal(true).describe(confirmDescription) }
1559
1581
  },
1560
1582
  async ({ id }) => run(async () => {
1561
- await client.delete(`/users/${id}`);
1583
+ await client.delete(`/users/${encodePathSegment(id)}`);
1562
1584
  return okText(`User "${id}" removed from the site.`);
1563
1585
  })
1564
1586
  );
@@ -1569,14 +1591,14 @@ function registerUsersTeamsTools(server, client) {
1569
1591
  );
1570
1592
  server.registerTool(
1571
1593
  "get_team",
1572
- { description: "Get a single team by id.", inputSchema: { id: z16.string().min(1) } },
1573
- async ({ id }) => run(() => client.get(`/teams/${id}`))
1594
+ { description: "Get a single team by id.", inputSchema: { id: idPathSegmentSchema } },
1595
+ async ({ id }) => run(() => client.get(`/teams/${encodePathSegment(id)}`))
1574
1596
  );
1575
1597
  server.registerTool(
1576
1598
  "create_team",
1577
1599
  {
1578
1600
  description: "Create a team.",
1579
- inputSchema: { name: z16.string().min(1).max(128), description: z16.string().nullable().optional() }
1601
+ inputSchema: { name: z17.string().min(1).max(128), description: z17.string().nullable().optional() }
1580
1602
  },
1581
1603
  async (input) => run(() => client.post("/teams", input))
1582
1604
  );
@@ -1585,64 +1607,64 @@ function registerUsersTeamsTools(server, client) {
1585
1607
  {
1586
1608
  description: "Update a team (partial PATCH).",
1587
1609
  inputSchema: {
1588
- id: z16.string().min(1),
1589
- name: z16.string().min(1).max(128).optional(),
1590
- description: z16.string().nullable().optional()
1610
+ id: idPathSegmentSchema,
1611
+ name: z17.string().min(1).max(128).optional(),
1612
+ description: z17.string().nullable().optional()
1591
1613
  }
1592
1614
  },
1593
- async ({ id, ...patch }) => run(() => client.patch(`/teams/${id}`, patch))
1615
+ async ({ id, ...patch }) => run(() => client.patch(`/teams/${encodePathSegment(id)}`, patch))
1594
1616
  );
1595
1617
  server.registerTool(
1596
1618
  "delete_team",
1597
1619
  {
1598
1620
  description: "Delete a team. DESTRUCTIVE \u2014 pass confirm=true.",
1599
- inputSchema: { id: z16.string().min(1), confirm: z16.literal(true).describe(confirmDescription) }
1621
+ inputSchema: { id: idPathSegmentSchema, confirm: z17.literal(true).describe(confirmDescription) }
1600
1622
  },
1601
1623
  async ({ id }) => run(async () => {
1602
- await client.delete(`/teams/${id}`);
1624
+ await client.delete(`/teams/${encodePathSegment(id)}`);
1603
1625
  return okText(`Team "${id}" deleted.`);
1604
1626
  })
1605
1627
  );
1606
1628
  server.registerTool(
1607
1629
  "list_team_members",
1608
- { description: "List members of a team.", inputSchema: { id: z16.string().min(1).describe("Team id.") } },
1609
- async ({ id }) => run(() => client.get(`/teams/${id}/members`))
1630
+ { description: "List members of a team.", inputSchema: { id: idPathSegmentSchema.describe("Team id.") } },
1631
+ async ({ id }) => run(() => client.get(`/teams/${encodePathSegment(id)}/members`))
1610
1632
  );
1611
1633
  server.registerTool(
1612
1634
  "add_team_member",
1613
1635
  {
1614
1636
  description: "Add a user to a team.",
1615
- inputSchema: { id: z16.string().min(1).describe("Team id."), userId: z16.string().min(1) }
1637
+ inputSchema: { id: idPathSegmentSchema.describe("Team id."), userId: idPathSegmentSchema }
1616
1638
  },
1617
- async ({ id, userId }) => run(() => client.post(`/teams/${id}/members`, { userId }))
1639
+ async ({ id, userId }) => run(() => client.post(`/teams/${encodePathSegment(id)}/members`, { userId }))
1618
1640
  );
1619
1641
  server.registerTool(
1620
1642
  "remove_team_member",
1621
1643
  {
1622
1644
  description: "Remove a user from a team. DESTRUCTIVE \u2014 pass confirm=true.",
1623
1645
  inputSchema: {
1624
- id: z16.string().min(1).describe("Team id."),
1625
- userId: z16.string().min(1),
1626
- confirm: z16.literal(true).describe(confirmDescription)
1646
+ id: idPathSegmentSchema.describe("Team id."),
1647
+ userId: idPathSegmentSchema,
1648
+ confirm: z17.literal(true).describe(confirmDescription)
1627
1649
  }
1628
1650
  },
1629
1651
  async ({ id, userId }) => run(async () => {
1630
- await client.delete(`/teams/${id}/members/${userId}`);
1652
+ await client.delete(`/teams/${encodePathSegment(id)}/members/${encodePathSegment(userId)}`);
1631
1653
  return okText(`User "${userId}" removed from team "${id}".`);
1632
1654
  })
1633
1655
  );
1634
1656
  }
1635
1657
 
1636
1658
  // src/tools/webhooks.ts
1637
- import { z as z17 } from "zod";
1638
- var webhookSchema = z17.object({
1639
- name: z17.string().min(1).max(255),
1640
- url: z17.string().url(),
1641
- actions: z17.array(z17.string()).optional().describe("Item events that trigger the webhook (e.g. create, update, delete)."),
1642
- collections: z17.array(z17.string()).optional().describe("Collections to filter on (empty = all)."),
1643
- headers: z17.record(z17.string()).optional(),
1644
- status: z17.enum(["active", "inactive"]).optional(),
1645
- secret: z17.string().nullable().optional()
1659
+ import { z as z18 } from "zod";
1660
+ var webhookSchema = z18.object({
1661
+ name: z18.string().min(1).max(255),
1662
+ url: z18.string().url(),
1663
+ actions: z18.array(z18.string()).optional().describe("Item events that trigger the webhook (e.g. create, update, delete)."),
1664
+ collections: z18.array(z18.string()).optional().describe("Collections to filter on (empty = all)."),
1665
+ headers: z18.record(z18.string()).optional(),
1666
+ status: z18.enum(["active", "inactive"]).optional(),
1667
+ secret: z18.string().nullable().optional()
1646
1668
  });
1647
1669
  function registerWebhookTools(server, client) {
1648
1670
  registerCrud(server, client, {