@craftstory/mcp 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -61,7 +61,7 @@ Plus the prompt `talking_video_from_photo` (script + photo) that walks the model
61
61
 
62
62
  The assistant will: `list_voices` -> `create_audio_clip` -> `wait_for_job(audio-clip)` -> `preview_cost` -> `create_craftstory2_video` (resolution `720_1280`, gestures `calm`) -> `wait_for_job(craftstory-2)` a few times -> `get_job_result` -> the video URL.
63
63
 
64
- Long jobs: `wait_for_job` never blocks longer than `timeout_s` (max 55 s, under the 60 s tool-call limit of most clients). A CraftStory 2.0 video needs several calls; that is by design so agent runtimes do not time out.
64
+ Long jobs: `wait_for_job` returns after at most `timeout_s` (max 55 s) plus one in-flight status request (25 s HTTP timeout), which keeps it under the 60 s tool-call limit of most clients in normal conditions. A CraftStory 2.0 video needs several calls; that is by design so agent runtimes do not time out.
65
65
 
66
66
  ## Local files vs URLs
67
67
 
package/dist/client.js CHANGED
@@ -5,7 +5,9 @@
5
5
  import { readFile } from "node:fs/promises";
6
6
  import { basename } from "node:path";
7
7
  export const DEFAULT_BASE = "https://api.craftstory.com/api/v1";
8
- export const USER_AGENT = "craftstory-mcp/0.1.0";
8
+ export const USER_AGENT = "craftstory-mcp/0.1.1";
9
+ /** Per-request HTTP timeout; keeps every tool call well under MCP clients' ~60 s limit. */
10
+ export const REQUEST_TIMEOUT_MS = 25_000;
9
11
  export class ApiError extends Error {
10
12
  status;
11
13
  body;
@@ -47,10 +49,12 @@ export class CraftStoryClient {
47
49
  }
48
50
  let res;
49
51
  try {
50
- res = await this.fetchImpl(`${this.base}${path}`, { method, headers, body: payload });
52
+ res = await this.fetchImpl(`${this.base}${path}`, { method, headers, body: payload, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
51
53
  }
52
54
  catch (e) {
53
55
  // Node's fetch hides the reason behind "fetch failed"; surface the cause (DNS, TLS, reset...).
56
+ if (e.name === "TimeoutError")
57
+ throw new Error(`Timeout after ${REQUEST_TIMEOUT_MS / 1000}s calling ${method} ${path}`);
54
58
  const cause = e.cause;
55
59
  throw new Error(`Network error calling ${method} ${path}: ${cause?.code ?? ""} ${cause?.message ?? e.message}`.trim());
56
60
  }
package/dist/server.js CHANGED
@@ -14,7 +14,7 @@ const JOB_KINDS = ["craftstory-2", "minimax-h3", "audio-clip"];
14
14
  const text = (data) => ({ content: [{ type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) }] });
15
15
  const fail = (err) => ({ isError: true, content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }] });
16
16
  export function buildServer(client) {
17
- const server = new McpServer({ name: "craftstory", version: "0.1.0" });
17
+ const server = new McpServer({ name: "craftstory", version: "0.1.1" });
18
18
  server.registerTool("list_models", {
19
19
  title: "List CraftStory video models",
20
20
  description: "Catalogue of the video models behind this server with their status, modes, limits and credit prices. " +
@@ -222,6 +222,8 @@ export function buildServer(client) {
222
222
  let last;
223
223
  try {
224
224
  while (true) {
225
+ if (Date.now() >= deadline && last)
226
+ return text({ state: "running", ...last, hint: "still running - call wait_for_job again" });
225
227
  last = await client.getStatus(model, id);
226
228
  const state = classify(last.status);
227
229
  if (token !== undefined) {
@@ -234,9 +236,10 @@ export function buildServer(client) {
234
236
  const result = state === "done" ? await client.getResult(model, id) : undefined;
235
237
  return text({ state, ...last, ...(result ? { result } : {}) });
236
238
  }
237
- if (Date.now() >= deadline)
239
+ const remaining = deadline - Date.now();
240
+ if (remaining <= 0)
238
241
  return text({ state: "running", ...last, hint: "still running - call wait_for_job again" });
239
- await new Promise((r) => setTimeout(r, model === "audio-clip" ? 2000 : 5000));
242
+ await new Promise((r) => setTimeout(r, Math.min(model === "audio-clip" ? 2000 : 5000, remaining)));
240
243
  }
241
244
  }
242
245
  catch (e) {
package/package.json CHANGED
@@ -1,21 +1,37 @@
1
1
  {
2
2
  "name": "@craftstory/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server for the CraftStory API: talking-avatar videos from a photo (CraftStory 2.0) and short clips with generated sound (MiniMax H3), from Claude, Cursor and other MCP clients.",
5
5
  "type": "module",
6
- "bin": { "craftstory-mcp": "dist/index.js" },
6
+ "bin": {
7
+ "craftstory-mcp": "dist/index.js"
8
+ },
7
9
  "main": "dist/index.js",
8
- "files": ["dist", "README.md", "LICENSE"],
9
- "engines": { "node": ">=18" },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
10
18
  "scripts": {
11
19
  "build": "tsc -p tsconfig.json && chmod +x dist/index.js",
12
20
  "start": "node dist/index.js",
13
21
  "test": "node --import tsx --test test/*.test.ts",
14
22
  "smoke": "tsx test/smoke.ts"
15
23
  },
16
- "keywords": ["mcp", "model-context-protocol", "craftstory", "ai-video", "talking-avatar", "lip-sync", "text-to-video", "minimax"],
24
+ "keywords": [
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "craftstory",
28
+ "ai-video",
29
+ "talking-avatar",
30
+ "lip-sync",
31
+ "text-to-video",
32
+ "minimax"
33
+ ],
17
34
  "license": "MIT",
18
- "repository": { "type": "git", "url": "https://gitlab.itseez3d.com/craftstory/craftstory-mcp" },
19
35
  "homepage": "https://craftstory.com",
20
36
  "dependencies": {
21
37
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -25,5 +41,8 @@
25
41
  "@types/node": "^22.0.0",
26
42
  "tsx": "^4.19.0",
27
43
  "typescript": "^5.6.0"
44
+ },
45
+ "bugs": {
46
+ "url": "https://api.craftstory.com/api/v1/docs/public/"
28
47
  }
29
48
  }