@rolino/mcp 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @rolino/mcp
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4e123ef: Add exact storage usage, bounded workspace media listing with required project context, and entitlement-checked, confirmation-bound Media Library cleanup across the shared contracts, SDK, and MCP tools. Cleanup status now reports a safe changed-asset skip when the confirmed workspace, project, storage identity, or asset version no longer matches.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [4e123ef]
12
+ - @rolino/contracts@0.8.0
13
+ - @rolino/sdk@0.8.0
14
+ - @rolino/local-auth@0.8.0
15
+
16
+ ## 0.7.1
17
+
18
+ ### Patch Changes
19
+
20
+ - @rolino/contracts@0.7.1
21
+ - @rolino/sdk@0.7.1
22
+ - @rolino/local-auth@0.7.1
23
+
3
24
  ## 0.7.0
4
25
 
5
26
  ### Minor Changes
package/README.md CHANGED
@@ -28,8 +28,12 @@ Initial tools:
28
28
  - `list_projects`
29
29
  - `get_project`
30
30
  - `create_project`
31
- - `list_media_assets`
32
- - `upload_media_asset`
31
+ - `list_media_assets`
32
+ - `get_storage_usage`
33
+ - `preview_media_cleanup`
34
+ - `execute_media_cleanup`
35
+ - `get_media_cleanup_status`
36
+ - `upload_media_asset`
33
37
  - `list_posts`
34
38
  - `get_post`
35
39
  - `create_draft_post`
@@ -62,7 +66,17 @@ The media tools discover reusable project assets and stream one explicitly
62
66
  approved local JPEG, PNG, WebP, MP4, or MOV file directly to configured
63
67
  storage. Rolino derives dimensions and video duration from the stored object.
64
68
  Uploading never creates, schedules, or publishes a post; the returned
65
- asset ID is passed into a later draft operation.
69
+ asset ID is passed into a later draft operation.
70
+
71
+ `get_storage_usage` and `list_media_assets` are bounded closed-world reads.
72
+ Cleanup requires the separate `media:delete` permission. Preview creates an
73
+ exact confirmation but deletes nothing. Before execute, show the upload-age
74
+ cutoff, candidate count, excluded in-use count, unknown-size count, and estimated
75
+ space. A general request to free space is not execute approval. Execute queues
76
+ only the unchanged confirmed files with an idempotency key. In-use files are
77
+ never detached, and queued bytes remain counted until physical deletion succeeds.
78
+ The deletion worker skips a file without contacting storage if its confirmed
79
+ workspace, project, asset, storage identity, or version changed.
66
80
 
67
81
  The tool set includes discovery and inspection tools that are read-only.
68
82
  The four SEO tools require the exact optional `seo:read` capability. They are
package/dist/bin.cjs CHANGED
@@ -52,7 +52,7 @@ var z = __toESM(require("zod/v4"), 1);
52
52
  // package.json
53
53
  var package_default = {
54
54
  name: "@rolino/mcp",
55
- version: "0.7.0",
55
+ version: "0.8.0",
56
56
  description: "Model Context Protocol server for Rolino",
57
57
  type: "module",
58
58
  license: "MIT",
@@ -111,9 +111,9 @@ var package_default = {
111
111
  dependencies: {
112
112
  "@hono/node-server": "2.0.12",
113
113
  "@modelcontextprotocol/server": "2.0.0",
114
- "@rolino/contracts": "0.7.0",
115
- "@rolino/local-auth": "0.7.0",
116
- "@rolino/sdk": "0.7.0",
114
+ "@rolino/contracts": "0.8.0",
115
+ "@rolino/local-auth": "0.8.0",
116
+ "@rolino/sdk": "0.8.0",
117
117
  zod: "^4.4.3"
118
118
  },
119
119
  devDependencies: {
@@ -252,6 +252,8 @@ var readOnlyAnnotations = {
252
252
  };
253
253
  var backlinkOpenWorldAnnotations = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true };
254
254
  var backlinkWriteAnnotations = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
255
+ var cleanupPreviewAnnotations = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
256
+ var cleanupExecuteAnnotations = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true };
255
257
  var blogPublicationWeekdaysSchema = z.array(z.number().int().min(0).max(6)).min(1).max(7).superRefine((weekdays, context) => {
256
258
  if (new Set(weekdays).size !== weekdays.length) {
257
259
  context.addIssue({ code: "custom", message: "Weekdays must be unique." });
@@ -432,20 +434,83 @@ function createRolinoMcpServer(options = {}) {
432
434
  });
433
435
  server.registerTool("list_media_assets", {
434
436
  title: "List Rolino media assets",
435
- description: "List reusable images and videos in one exact project. Use this before uploading to avoid duplicates and pass returned asset IDs when creating or updating drafts. This tool never changes Rolino data.",
437
+ description: "List reusable images and videos in one exact project or across the selected workspace. Filters use upload time, not last-use time. Results state whether each file is in use and can be deleted. Use this before uploading to avoid duplicates and pass returned asset IDs when creating or updating drafts. This tool never changes Rolino data.",
436
438
  inputSchema: z.object({
437
- projectId: z.string().min(1).describe("Exact Rolino project ID."),
439
+ projectId: z.string().min(1).optional().describe("Exact Rolino project ID. Omit for the selected workspace."),
438
440
  limit: z.number().int().min(1).max(100).default(50),
439
441
  cursor: z.string().min(1).optional(),
440
442
  type: z.enum(["IMAGE", "VIDEO"]).optional(),
441
- query: z.string().trim().min(1).max(100).optional()
443
+ query: z.string().trim().min(1).max(100).optional(),
444
+ createdBefore: z.string().datetime({ offset: true }).optional().describe("Return files uploaded before this absolute instant."),
445
+ createdAfter: z.string().datetime({ offset: true }).optional().describe("Return files uploaded after this absolute instant."),
446
+ unusedOnly: z.boolean().optional(),
447
+ order: z.enum(["OLDEST_FIRST", "NEWEST_FIRST"]).default("NEWEST_FIRST")
442
448
  }),
443
449
  outputSchema: import_contracts.MediaAssetListDataSchema,
444
450
  annotations: readOnlyAnnotations
445
- }, async ({ projectId, limit, cursor, type, query }) => {
451
+ }, async ({ projectId, limit, cursor, type, query, createdBefore, createdAfter, unusedOnly, order }) => {
446
452
  try {
447
453
  if (!api.media) throw new TypeError("This Rolino client does not support media operations.");
448
- return jsonResult(await api.media.list(projectId, { limit, cursor, type, query }));
454
+ const options2 = { limit, cursor, type, query, createdBefore, createdAfter, unusedOnly, order };
455
+ if (projectId) return jsonResult(await api.media.list(projectId, options2));
456
+ if (!api.media.listWorkspace) throw new TypeError("This Rolino client does not support workspace media listing.");
457
+ return jsonResult(await api.media.listWorkspace(options2));
458
+ } catch (error) {
459
+ return errorResult(error);
460
+ }
461
+ });
462
+ server.registerTool("get_storage_usage", {
463
+ title: "Get Rolino storage usage",
464
+ description: "Read exact stored bytes, active upload reservations, available upload capacity, pending deletion bytes, and the source breakdown for the selected workspace. Unlimited workspaces still report actual stored bytes. This tool never changes Rolino data.",
465
+ inputSchema: z.object({}),
466
+ outputSchema: import_contracts.StorageUsageSchema,
467
+ annotations: readOnlyAnnotations
468
+ }, async () => {
469
+ try {
470
+ if (!api.storage) throw new TypeError("This Rolino client does not support storage operations.");
471
+ return jsonResult(await api.storage.getUsage());
472
+ } catch (error) {
473
+ return errorResult(error);
474
+ }
475
+ });
476
+ server.registerTool("preview_media_cleanup", {
477
+ title: "Preview a Rolino media cleanup",
478
+ description: "Find at most 500 exact unused Media Library files and create a short-lived confirmation. The age filter means upload age. This deletes nothing. Show the cutoff, candidate count, excluded in-use count, unknown-size count, and estimated reclaimable space to the user before asking for confirmation.",
479
+ inputSchema: import_contracts.MediaCleanupPreviewRequestSchema,
480
+ outputSchema: import_contracts.MediaCleanupPreviewSchema,
481
+ annotations: cleanupPreviewAnnotations
482
+ }, async (input) => {
483
+ try {
484
+ if (!api.media?.previewCleanup) throw new TypeError("This Rolino client does not support media cleanup preview.");
485
+ return jsonResult(await api.media.previewCleanup(input));
486
+ } catch (error) {
487
+ return errorResult(error);
488
+ }
489
+ });
490
+ server.registerTool("execute_media_cleanup", {
491
+ title: "Execute a confirmed Rolino media cleanup",
492
+ description: "Permanently delete only the exact unchanged candidates from preview through Rolino's durable deletion queue. A general request such as 'free some space' is not approval. Call this only after the user explicitly confirms the shown cutoff, candidate count, excluded count, unknown-size count, and estimated space. Use one stable idempotency key for retries.",
493
+ inputSchema: import_contracts.MediaCleanupExecuteRequestSchema.extend({ idempotencyKey: z.string().min(1).max(200) }),
494
+ outputSchema: import_contracts.MediaCleanupExecuteResultSchema,
495
+ annotations: cleanupExecuteAnnotations
496
+ }, async ({ idempotencyKey, ...input }) => {
497
+ try {
498
+ if (!api.media?.executeCleanup) throw new TypeError("This Rolino client does not support media cleanup execution.");
499
+ return jsonResult(await api.media.executeCleanup(input, { idempotencyKey }));
500
+ } catch (error) {
501
+ return errorResult(error);
502
+ }
503
+ });
504
+ server.registerTool("get_media_cleanup_status", {
505
+ title: "Get Rolino media cleanup status",
506
+ description: "Read bounded progress for one cleanup. Queued and retrying bytes still count as stored until object deletion succeeds. This tool never returns storage keys or provider errors.",
507
+ inputSchema: import_contracts.MediaCleanupStatusQuerySchema.extend({ cleanupId: z.string().min(1).max(200) }),
508
+ outputSchema: import_contracts.MediaCleanupStatusSchema,
509
+ annotations: readOnlyAnnotations
510
+ }, async ({ cleanupId, ...query }) => {
511
+ try {
512
+ if (!api.media?.getCleanupStatus) throw new TypeError("This Rolino client does not support media cleanup status.");
513
+ return jsonResult(await api.media.getCleanupStatus(cleanupId, query));
449
514
  } catch (error) {
450
515
  return errorResult(error);
451
516
  }