@vprop/mcp 1.0.4 → 1.0.6

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/README.md +1 -0
  2. package/dist/index.js +16 -12
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -237,6 +237,7 @@ When calling `create_project`, you can customize:
237
237
  | `theme` | `modern` `classic` `bold` `elegant` | `modern` |
238
238
  | `resolution` | `portrait` (9:16) `landscape` (16:9) | `portrait` |
239
239
  | `caption` | `none` `keyword` `description` | `keyword` |
240
+ | `language` | `en` `es` `ko` `zh-Hans` | `en` |
240
241
  | `voice_id` | ID from `list_voices` | agent default |
241
242
  | `bgm_id` | ID from `list_bgms` | theme default |
242
243
  | `use_agent_avatar` | `true` `false` | `true` |
package/dist/index.js CHANGED
@@ -2,8 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { readFileSync } from "node:fs";
5
- import { extname } from "node:path";
6
- import { randomUUID } from "node:crypto";
5
+ import { extname, basename } from "node:path";
7
6
  import { z } from "zod";
8
7
  const BASE_URL = process.env.VPROP_BASE_URL ?? "https://vprop.ai/api/v1";
9
8
  function getApiKey() {
@@ -169,7 +168,7 @@ server.registerTool("upload_listing_image", {
169
168
  if (!content_type) {
170
169
  throw new Error(`Unsupported extension '${ext}'. Use .jpg, .jpeg, .png, or .webp`);
171
170
  }
172
- const filename = file_path.split("/").pop() ?? "image.jpg";
171
+ const filename = basename(file_path) || "image.jpg";
173
172
  // Read file before any API call — if the file is missing or unreadable
174
173
  // we fail here without touching remote state.
175
174
  const fileData = readFileSync(file_path);
@@ -184,11 +183,16 @@ server.registerTool("upload_listing_image", {
184
183
  await uploadBuffer(upload_url, fileData, content_type);
185
184
  }
186
185
  catch (err) {
187
- await vpropFetch(`/listings/${listing_id}/images/${image_id}`, {
188
- method: "DELETE",
189
- }).catch(() => { });
186
+ let cleanedUp = false;
187
+ try {
188
+ await vpropFetch(`/listings/${listing_id}/images/${image_id}`, { method: "DELETE" });
189
+ cleanedUp = true;
190
+ }
191
+ catch { }
190
192
  throw new Error(`Upload failed: ${err instanceof Error ? err.message : String(err)}. ` +
191
- `The image record (${image_id}) has been cleaned up.`);
193
+ (cleanedUp
194
+ ? `The image record (${image_id}) has been cleaned up.`
195
+ : `The image record (${image_id}) may still exist — call delete_listing_image to remove it.`));
192
196
  }
193
197
  return toText({ image_id, image_url, expires_at });
194
198
  });
@@ -274,7 +278,7 @@ server.registerTool("upload_agent_photo", {
274
278
  if (!content_type) {
275
279
  throw new Error(`Unsupported extension '${ext}'. Use .jpg, .jpeg, .png, or .webp`);
276
280
  }
277
- const filename = file_path.split("/").pop() ?? "photo.jpg";
281
+ const filename = basename(file_path) || "photo.jpg";
278
282
  // Read file before any API call — the presigned-URL endpoint immediately
279
283
  // stamps agents.photo in the DB, so a missing/unreadable file must be
280
284
  // caught here before we mutate remote state.
@@ -308,19 +312,19 @@ server.registerTool("create_project", {
308
312
  theme: z.enum(["modern", "classic", "bold", "elegant"]).optional().describe("Visual theme (default: modern)"),
309
313
  resolution: z.enum(["portrait", "landscape"]).optional().describe("Video orientation (default: portrait/9:16)"),
310
314
  caption: z.enum(["none", "keyword", "description"]).optional().describe("Caption style (default: keyword)"),
315
+ language: z.enum(["en", "es", "ko", "zh-Hans"]).optional().describe("Narration + caption language (default: en). Use list_voices to find a voice whose verified_languages includes your chosen language."),
311
316
  voice_id: z.string().optional().describe("Per-project voice override (from list_voices)"),
312
317
  bgm_id: z.string().optional().describe("Background music ID (from list_bgms)"),
313
318
  use_agent_avatar: z.boolean().optional().describe("Show animated agent avatar (default: true, needs agent photo)"),
314
319
  use_lipsync: z.boolean().optional().describe("Lipsync the avatar (default: false)"),
315
320
  metadata: z.record(z.string(), z.unknown()).optional(),
316
321
  },
317
- }, async ({ listing_id, agent_id, title, cta, theme, resolution, caption, voice_id, bgm_id, use_agent_avatar, use_lipsync, metadata, }) => {
318
- const options = theme || resolution || caption || voice_id || bgm_id || use_agent_avatar !== undefined || use_lipsync !== undefined
319
- ? { theme, resolution, caption, voice_id, bgm_id, use_agent_avatar, use_lipsync }
322
+ }, async ({ listing_id, agent_id, title, cta, theme, resolution, caption, language, voice_id, bgm_id, use_agent_avatar, use_lipsync, metadata, }) => {
323
+ const options = theme || resolution || caption || language || voice_id || bgm_id || use_agent_avatar !== undefined || use_lipsync !== undefined
324
+ ? { theme, resolution, caption, language, voice_id, bgm_id, use_agent_avatar, use_lipsync }
320
325
  : undefined;
321
326
  return toText(await vpropFetch("/projects", {
322
327
  method: "POST",
323
- headers: { "Idempotency-Key": randomUUID() },
324
328
  body: JSON.stringify({ listing_id, agent_id, title, cta, options, metadata }),
325
329
  }));
326
330
  });
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@vprop/mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "MCP server for the vProp Public API — generate real-estate listing videos with AI",
5
5
  "type": "module",
6
6
  "bin": {
7
+ "mcp": "dist/index.js",
7
8
  "vprop-mcp": "dist/index.js"
8
9
  },
9
10
  "files": [
@@ -11,7 +12,7 @@
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "tsc",
14
- "prepare": "tsc",
15
+ "prepack": "tsc",
15
16
  "dev": "node --experimental-strip-types src/index.ts",
16
17
  "start": "node dist/index.js"
17
18
  },