@visionengine/remotion-file-bridge 1.0.1 → 1.0.2

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 (2) hide show
  1. package/dist/server.js +83 -12
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1,3 +1,4 @@
1
+ import * as path from 'node:path';
1
2
  import { FastMCP } from 'fastmcp';
2
3
  import { z } from 'zod';
3
4
  import { deleteProjectFile, downloadProjectFile, listProjectFiles, readProjectFile, saveProjectFile } from './client.js';
@@ -44,21 +45,91 @@ server.addTool({
44
45
  }),
45
46
  execute: async (args) => JSON.stringify(await downloadProjectFile(args), null, 2),
46
47
  });
48
+ const saveFileParameters = z.object({
49
+ userId: z.string().optional(),
50
+ sourceType: z.enum(['local_path', 'text', 'base64', 'remote_url']).describe('Choose local_path for local files. Choose text/base64/remote_url when content is provided in content field.'),
51
+ localPath: z.string().trim().min(1).optional().describe('Required when sourceType=local_path. Can be an absolute path or a path relative to WORKDIR.'),
52
+ content: z.string().optional().describe('Required for sourceType=text, base64, or remote_url. For remote_url, put URL in this field.'),
53
+ fileName: z.string().min(1).describe('Final file name in remote workspace, including extension, e.g. baidu_ai.png.'),
54
+ targetPath: z.string().optional().describe('Target directory path only, workspace-relative, e.g. public/images/photos. Do not include fileName.'),
55
+ contentType: z.string().optional(),
56
+ overwrite: z.boolean().optional(),
57
+ }).superRefine((value, ctx) => {
58
+ if (value.sourceType === 'local_path' && !value.localPath) {
59
+ ctx.addIssue({
60
+ code: z.ZodIssueCode.custom,
61
+ path: ['localPath'],
62
+ message: 'localPath is required when sourceType=local_path',
63
+ });
64
+ }
65
+ if (value.sourceType !== 'local_path' && typeof value.content !== 'string') {
66
+ ctx.addIssue({
67
+ code: z.ZodIssueCode.custom,
68
+ path: ['content'],
69
+ message: `content is required when sourceType=${value.sourceType}`,
70
+ });
71
+ }
72
+ });
73
+ function normalizeTargetDirectory(targetPath, fileName) {
74
+ if (!targetPath?.trim()) {
75
+ return { targetPath };
76
+ }
77
+ const normalized = targetPath.trim().replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+/g, '/');
78
+ const parts = normalized.split('/').filter(Boolean);
79
+ if (parts.length === 0) {
80
+ return { targetPath: undefined };
81
+ }
82
+ const last = parts[parts.length - 1];
83
+ const normalizedFileName = path.posix.basename(fileName).toLowerCase();
84
+ const hasSameFileNameAsTail = last.toLowerCase() === normalizedFileName;
85
+ if (hasSameFileNameAsTail) {
86
+ parts.pop();
87
+ }
88
+ const normalizedTargetPath = parts.join('/') || undefined;
89
+ const looksLikeFilePath = /\.[^/.]+$/.test(last);
90
+ if (hasSameFileNameAsTail || looksLikeFilePath) {
91
+ return {
92
+ targetPath: normalizedTargetPath,
93
+ warning: {
94
+ code: 'target_path_should_be_directory',
95
+ message: 'targetPath should be a directory path and should not include fileName. The value was normalized before saving.',
96
+ source: 'save_file',
97
+ details: {
98
+ fileName,
99
+ originalTargetPath: targetPath,
100
+ normalizedTargetPath,
101
+ },
102
+ },
103
+ };
104
+ }
105
+ return { targetPath: normalizedTargetPath };
106
+ }
47
107
  server.addTool({
48
108
  name: 'save_file',
49
- description: 'Use only when the user explicitly wants to save or upload content into the remote Remotion/VEC cloud workspace. Saves local files, text, base64 payloads, or remote URLs into the current user remote workspace.',
109
+ description: 'Use only when the user explicitly wants to save or upload content into the remote Remotion/VEC cloud workspace. Saves local files, text, base64 payloads, or remote URLs into the current user remote workspace. Always provide localPath when sourceType=local_path. targetPath should be a directory, and fileName should contain the final file name.',
50
110
  annotations: { title: 'Save Remotion Project File', readOnlyHint: false, openWorldHint: true },
51
- parameters: z.object({
52
- userId: z.string().optional(),
53
- sourceType: z.enum(['local_path', 'text', 'base64', 'remote_url']),
54
- localPath: z.string().optional(),
55
- content: z.string().optional(),
56
- fileName: z.string().min(1),
57
- targetPath: z.string().optional(),
58
- contentType: z.string().optional(),
59
- overwrite: z.boolean().optional(),
60
- }),
61
- execute: async (args) => JSON.stringify(await saveProjectFile(args), null, 2),
111
+ parameters: saveFileParameters,
112
+ execute: async (args) => {
113
+ const normalized = normalizeTargetDirectory(args.targetPath, args.fileName);
114
+ const normalizedArgs = {
115
+ ...args,
116
+ targetPath: normalized.targetPath,
117
+ };
118
+ const result = await saveProjectFile(normalizedArgs);
119
+ if (!normalized.warning) {
120
+ return JSON.stringify(result, null, 2);
121
+ }
122
+ const metadata = result.metadata && typeof result.metadata === 'object'
123
+ ? result.metadata
124
+ : {};
125
+ return JSON.stringify({
126
+ ...result,
127
+ metadata: {
128
+ ...metadata,
129
+ warnings: [...(Array.isArray(metadata.warnings) ? metadata.warnings : []), normalized.warning],
130
+ },
131
+ }, null, 2);
132
+ },
62
133
  });
63
134
  server.addTool({
64
135
  name: 'delete_file',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visionengine/remotion-file-bridge",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "VisionEngine Remotion File Bridge MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",