owner-clone-ai-mcp 1.78.0 → 1.78.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.
package/domains/video.js CHANGED
@@ -64,7 +64,7 @@ const tools = [
64
64
  title: { type: 'string' },
65
65
  timeline: { type: 'object', description: 'タイムラインJSON(全置換・形式は get_video_timeline_spec)' },
66
66
  deck_id: { type: 'string', description: '参照スライドデッキの差し替え。スライドを作り直した場合はここで新しいデッキIDを指定し、timeline 側の各クリップの deckId も同じ値に揃えてください(尺やタイミングはそのまま保てます)' },
67
- base_updated_at: { type: 'string', description: 'get_video_project で取得した updated_at。渡すと、その後にユーザーが編集画面で保存していた場合に上書きせずエラーを返します(推奨)' },
67
+ base_updated_at: { type: 'string', description: 'get_video_project で取得した updated_at。渡すと、その後にユーザーが編集画面で保存していた場合に上書きせずエラー(409)を返します(推奨)。⚠️ 409 の本文には最新の updated_at と changed(違いのある層: telop/audio/video など)、meta_only(違いが素材のメタ情報だけか)が入ります。meta_only:true は編集画面がファイルを割り当てただけの公算が高く、返ってきた updated_at をそのまま base_updated_at に入れて再送して差し支えありません。changed に自分が書き換える層が挙がっているときは、get_video_project で取り込んでから判断してください。なお同じ内容を再送した場合は 409 ではなく unchanged:true の成功が返るため、リトライは安全です' },
68
68
  }, required: ['id'] },
69
69
  },
70
70
  {
@@ -92,7 +92,7 @@ const tools = [
92
92
  gain: { type: 'number', description: '音量(1=100%・BGMは0.3程度)' },
93
93
  muted: { type: 'boolean', description: 'video のとき true でその動画の音を使わない' },
94
94
  media_id: { type: 'string', description: '複数の素材を使い分けるときの識別子(省略時は type から自動)' },
95
- base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)' },
95
+ base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)。⚠️ 409 の本文に最新 updated_at / changed(違いのある層)/ meta_only が入る。meta_only:true はそのまま再送可。同一内容の再送は 409 にならず unchanged:true の成功が返る' },
96
96
  }, required: ['id', 'type', 'file_name'] },
97
97
  },
98
98
  {
@@ -116,7 +116,7 @@ const tools = [
116
116
  lead_sec: { type: 'number', description: '発話より何秒早く出すか(既定0.4)' },
117
117
  max_chars: { type: 'number', description: '1行の最大文字数。超えたら自動で改行する(既定20)' },
118
118
  replace: { type: 'boolean', description: 'true=既存のテロップを置き換える(省略=追加)' },
119
- base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)' },
119
+ base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)。⚠️ 409 の本文に最新 updated_at / changed(違いのある層)/ meta_only が入る。meta_only:true はそのまま再送可。同一内容の再送は 409 にならず unchanged:true の成功が返る' },
120
120
  }, required: ['id', 'srt'] },
121
121
  },
122
122
  {
@@ -130,7 +130,7 @@ const tools = [
130
130
  media_id: { type: 'string', description: '同じ種類が複数あるときの絞り込み(mediaId)' },
131
131
  clips: { type: 'array', items: { type: 'object' }, description: 'そのトラックのクリップ配列(全置換)。形式は get_video_timeline_spec' },
132
132
  remove: { type: 'boolean', description: 'true=このトラックを削除する' },
133
- base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)' },
133
+ base_updated_at: { type: 'string', description: '競合防止(get_video_project の updated_at)。⚠️ 409 の本文に最新 updated_at / changed(違いのある層)/ meta_only が入る。meta_only:true はそのまま再送可。同一内容の再送は 409 にならず unchanged:true の成功が返る' },
134
134
  }, required: ['id', 'type'] },
135
135
  },
136
136
  {
package/lib/api.js CHANGED
@@ -6,6 +6,43 @@ const API_KEY = process.env.OCR_API_KEY;
6
6
  // (配布物 manifest.json / install.ps1 / 案内はすべて api.ownercloneai.com 指定済み)。
7
7
  const BASE_URL = (process.env.OCR_BASE_URL || 'https://api.ownercloneai.com').replace(/\/$/, '');
8
8
 
9
+ // ── エラー応答を「AIがそのまま読んで判断できる」文に組み立てる ────────────────
10
+ // ⚠️ 2026-10-01: 以前は json.error と json.detail しか見ておらず、
11
+ // サーバーが返している updated_at / changed / meta_only / hint を**捨てていた**。
12
+ // そのため B-5(409の往復をなくす)の情報が AI に届かず、
13
+ // 結局 get_video_project を取り直す往復が必要なままだった。
14
+ // ⚠️ 汎用にする: 既知のキーだけでなく、未知の付加フィールドも落とさず載せる。
15
+ // 将来サーバーが新しい手がかりを足しても、パッケージの改修なしで AI に届く。
16
+ const PRIMARY_KEYS = ['error', 'message', 'detail'];
17
+ // 値が長くなりすぎるもの(本体データ)は載せない。手がかりだけを渡す
18
+ const SKIP_KEYS = ['timeline', 'deck', 'tracks', 'slides', 'chapters', 'items', 'rows'];
19
+
20
+ function formatValue(v) {
21
+ if (Array.isArray(v)) return v.length ? v.join(', ') : '(なし)';
22
+ if (v && typeof v === 'object') return JSON.stringify(v);
23
+ return String(v);
24
+ }
25
+
26
+ function buildApiError(json, status) {
27
+ if (!json || typeof json !== 'object') return new Error(`API error ${status}`);
28
+ const head = json.detail
29
+ ? `${json.error || 'エラー'}: ${json.detail}`
30
+ : (json.error || json.message || `API error ${status}`);
31
+ const extras = [];
32
+ for (const [k, v] of Object.entries(json)) {
33
+ if (PRIMARY_KEYS.includes(k) || SKIP_KEYS.includes(k)) continue;
34
+ if (v === undefined || v === null || v === '') continue;
35
+ extras.push(`${k}: ${formatValue(v)}`);
36
+ }
37
+ // hint はサーバーが「次に何をすべきか」を書いた文。最後に独立した行で出す
38
+ const hint = typeof json.hint === 'string' ? json.hint : null;
39
+ const body = extras.filter(x => !x.startsWith('hint: ')).join(' / ');
40
+ const err = new Error([head, body, hint].filter(Boolean).join('\n'));
41
+ err.status = status;
42
+ err.body = json; // プログラムから読みたいときのために原本も残す
43
+ return err;
44
+ }
45
+
9
46
  async function callApi(path, options = {}) {
10
47
  // ⚠️ 2026-09-30: `callApi(path, 'POST', body)` という別流儀で書かれた2箇所が静かに壊れていた
11
48
  // (domains/slides.js の save_brand_theme / apply_brand_theme)。文字列を options として
@@ -30,11 +67,9 @@ async function callApi(path, options = {}) {
30
67
  });
31
68
  const json = await res.json();
32
69
  if (!res.ok) {
33
- // バックエンドが detail(DB例外メッセージ等)を返す場合は原因特定のため併記
34
- const msg = json && json.detail ? `${json.error || 'エラー'}: ${json.detail}` : (json.error || `API error ${res.status}`);
35
- throw new Error(msg);
70
+ throw buildApiError(json, res.status);
36
71
  }
37
72
  return json;
38
73
  }
39
74
 
40
- module.exports = { callApi };
75
+ module.exports = { callApi, buildApiError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owner-clone-ai-mcp",
3
- "version": "1.78.0",
3
+ "version": "1.78.2",
4
4
  "description": "MCP server for Owner Clone AI — exposes knowledge and agent presets to Claude Code",
5
5
  "main": "index.js",
6
6
  "bin": {