owner-clone-ai-mcp 1.76.0 → 1.77.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/domains/projects.js +6 -1
- package/domains/schedulesAndTasks.js +39 -4
- package/domains/slides.js +24 -2
- package/lib/api.js +12 -0
- package/package.json +1 -1
package/domains/projects.js
CHANGED
|
@@ -83,7 +83,12 @@ const handlers = {
|
|
|
83
83
|
const lines = data.projects.map(p =>
|
|
84
84
|
`- [${p.id}] ${p.name} [${p.status}]` +
|
|
85
85
|
(p.due_date ? ` 期限:${p.due_date.slice(0, 10)}` : '') +
|
|
86
|
-
|
|
86
|
+
// ⚠️ APIが返すキーは total_tasks / done_tasks / pending_tasks。
|
|
87
|
+
// 以前は存在しない p.task_count を読んでいたため **常に「タスク:0件」** になっていた。
|
|
88
|
+
// COUNT() は bigint=文字列で返るので Number() で数値化する。
|
|
89
|
+
` タスク:${Number(p.total_tasks || 0)}件` +
|
|
90
|
+
(Number(p.total_tasks || 0) > 0
|
|
91
|
+
? `(完了${Number(p.done_tasks || 0)}/未対応${Number(p.pending_tasks || 0)})` : '') +
|
|
87
92
|
(p.approval_notify_setting_id ? ` Chatwork通知:あり` : '') +
|
|
88
93
|
(p.description ? `\n ${p.description}` : '')
|
|
89
94
|
);
|
|
@@ -63,9 +63,20 @@ const tools = [
|
|
|
63
63
|
required: ['id'],
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
name: 'list_team_members',
|
|
68
|
+
description: `チームメンバー(タスクの担当者に指定できる人)の一覧を取得します。
|
|
69
|
+
assignee_id を調べるために使います。先頭はオーナー自身(role: owner)です。
|
|
70
|
+
※ 通常は assignee_name に名前を渡せば自動で解決されるため、このツールは
|
|
71
|
+
同姓の人が複数いる等でIDを確定したいときに使います。`,
|
|
72
|
+
annotations: { readOnlyHint: true },
|
|
73
|
+
inputSchema: { type: 'object', properties: {} },
|
|
74
|
+
},
|
|
66
75
|
{
|
|
67
76
|
name: 'list_tasks',
|
|
68
|
-
description:
|
|
77
|
+
description: `タスク一覧を取得します。ステータス・タイプ・担当者で絞り込めます(複数指定はAND条件)。
|
|
78
|
+
「田中さんの未完了タスク」のような問いには status:"pending" と assignee_name:"田中" を併せて指定します。
|
|
79
|
+
各行の末尾に担当者名が付きます(未設定は表示なし)。`,
|
|
69
80
|
annotations: { readOnlyHint: true },
|
|
70
81
|
inputSchema: {
|
|
71
82
|
type: 'object',
|
|
@@ -73,12 +84,15 @@ const tools = [
|
|
|
73
84
|
status: { type: 'string', description: 'ステータス絞り込み: pending/done/dismissed/approved/rejected(省略時: 全件)' },
|
|
74
85
|
type: { type: 'string', description: 'タイプ絞り込み: manual/workflow_approval/workflow_review(省略時: 全件)' },
|
|
75
86
|
limit: { type: 'number', description: '取得件数(デフォルト: 50、最大: 200)' },
|
|
87
|
+
assignee_name: { type: 'string', description: '担当者名で絞り込み(部分一致。姓だけでも可)。オーナー自身の名前も可。"自分" でAPIキーの持ち主(オーナー)。該当者が居なければエラーではなく0件。複数該当するときは全員分を返す' },
|
|
88
|
+
assignee_id: { type: 'string', description: '担当者IDで絞り込み(list_team_members のUUID。オーナー自身のIDも可)。assignee_name より優先' },
|
|
89
|
+
unassigned: { type: 'boolean', description: 'true で「担当者が未設定のタスク」だけに絞る。assignee_name に "未割当"/"未設定"/"なし" を渡しても同じ' },
|
|
76
90
|
},
|
|
77
91
|
},
|
|
78
92
|
},
|
|
79
93
|
{
|
|
80
94
|
name: 'get_task',
|
|
81
|
-
description: '
|
|
95
|
+
description: '指定したタスクの詳細を取得します。担当者名も返します(未設定なら「なし」)。',
|
|
82
96
|
annotations: { readOnlyHint: true },
|
|
83
97
|
inputSchema: {
|
|
84
98
|
type: 'object',
|
|
@@ -98,7 +112,7 @@ const tools = [
|
|
|
98
112
|
priority: { type: 'string', description: '優先度: low/normal/high(デフォルト: normal)' },
|
|
99
113
|
due_date: { type: 'string', description: '期限日時 ISO8601(省略可)' },
|
|
100
114
|
assignee_id: { type: 'string', description: '担当者のメンバーID(list_team_members で取得したUUID)。オーナー自身のID指定も可。' },
|
|
101
|
-
assignee_name: { type: 'string', description: '担当者の名前(部分一致で検索)。assignee_id
|
|
115
|
+
assignee_name: { type: 'string', description: '担当者の名前(部分一致で検索)。assignee_id を指定しない場合に使用。オーナー(APIキーの持ち主)も名前で指定できる。"自分" と書けばオーナー。⚠️ オーナーとメンバーの両方に部分一致すると候補一覧のエラーになるため、フルネームか assignee_id を使う' },
|
|
102
116
|
project_id: { type: 'string', description: 'このタスクを紐づけるプロジェクトのID(list_projects で取得したUUID)' },
|
|
103
117
|
notify_assignee: { type: 'boolean', description: '担当者にChatworkで通知するか(デフォルト: false。trueで通知)' },
|
|
104
118
|
notify_chatwork_setting_id: { type: 'string', description: '担当者通知の送信先ChatworkグループID(list_chatwork_settings で取得。未指定時は自動選択)' },
|
|
@@ -120,7 +134,7 @@ const tools = [
|
|
|
120
134
|
due_date: { type: 'string', description: '新しい期限(manual のみ、null で期限解除)' },
|
|
121
135
|
status: { type: 'string', description: 'ステータス変更: pending/done/dismissed' },
|
|
122
136
|
assignee_id: { type: 'string', description: '担当者のメンバーID(list_team_members のUUID。空文字で担当者解除)' },
|
|
123
|
-
assignee_name: { type: 'string', description: '担当者の名前(部分一致)。"解除"
|
|
137
|
+
assignee_name: { type: 'string', description: '担当者の名前(部分一致)。"解除"で担当者を外す。オーナー(APIキーの持ち主)も名前で指定でき、"自分" と書けばオーナー。⚠️ オーナーとメンバーの両方に部分一致すると候補一覧のエラーになるため、フルネームか assignee_id を使う' },
|
|
124
138
|
project_id: { type: 'string', description: '紐づけるプロジェクトのID(list_projects のUUID。空文字で解除)' },
|
|
125
139
|
},
|
|
126
140
|
required: ['id'],
|
|
@@ -183,11 +197,28 @@ const handlers = {
|
|
|
183
197
|
return `✅ ${data.message}\nID: ${data.id}`;
|
|
184
198
|
},
|
|
185
199
|
|
|
200
|
+
async list_team_members() {
|
|
201
|
+
const data = await callApi('/team-members');
|
|
202
|
+
if (!data.members || data.members.length === 0) return 'チームメンバーが登録されていません。';
|
|
203
|
+
// ⚠️ オーナーとメンバーを見分けられるようにする(同姓のとき assignee_id を選ぶ手がかり)
|
|
204
|
+
const ROLE_JA = { owner: 'オーナー', member: 'メンバー', admin: '管理者' };
|
|
205
|
+
const lines = data.members.map(m =>
|
|
206
|
+
`${m.is_active ? '•' : '(無効)'} [${m.id}] ${m.name}(${ROLE_JA[m.role] || m.role || 'メンバー'})`
|
|
207
|
+
);
|
|
208
|
+
return `チームメンバー(${data.count}名):\n\n${lines.join('\n')}\n\n` +
|
|
209
|
+
'このIDを list_tasks / create_task / update_task の assignee_id に指定できます。\n' +
|
|
210
|
+
'※ 自分(APIキーの持ち主=オーナー)に割り当てるなら assignee_name:"自分" でも指定できます。';
|
|
211
|
+
},
|
|
212
|
+
|
|
186
213
|
async list_tasks(args) {
|
|
187
214
|
const params = new URLSearchParams();
|
|
188
215
|
if (args.status) params.set('status', args.status);
|
|
189
216
|
if (args.type) params.set('type', args.type);
|
|
190
217
|
if (args.limit) params.set('limit', String(args.limit));
|
|
218
|
+
// ⚠️ 未指定のものは1つもクエリに載せない=サーバー側も従来と同じSQLになる
|
|
219
|
+
if (args.assignee_id) params.set('assignee_id', String(args.assignee_id));
|
|
220
|
+
if (args.assignee_name) params.set('assignee_name', String(args.assignee_name));
|
|
221
|
+
if (args.unassigned === true) params.set('unassigned', 'true');
|
|
191
222
|
const data = await callApi(`/tasks?${params}`);
|
|
192
223
|
if (!data.tasks || data.tasks.length === 0) return 'タスクがありません。';
|
|
193
224
|
const statusIcon = { pending: '⏳', done: '✅', dismissed: '🚫', approved: '✔', rejected: '✗' };
|
|
@@ -196,6 +227,9 @@ const handlers = {
|
|
|
196
227
|
` [${t.status}/${t.priority}]` +
|
|
197
228
|
(t.due_date ? ` 期限:${t.due_date.slice(0, 10)}` : '') +
|
|
198
229
|
(t.type !== 'manual' ? ` (${t.type})` : '') +
|
|
230
|
+
// ⚠️ 未設定でも「なし」を出す(依頼の条件: どのタスクタイプでもエラーにせず「なし」と表示)。
|
|
231
|
+
// 行末への追記なので既存項目の並び・書式は変わらない
|
|
232
|
+
` 担当:${t.assignee_name || 'なし'}` +
|
|
199
233
|
(t.body ? `\n ${t.body.slice(0, 60)}${t.body.length > 60 ? '…' : ''}` : '')
|
|
200
234
|
);
|
|
201
235
|
return `タスク一覧(${data.count}件):\n\n${lines.join('\n')}`;
|
|
@@ -206,6 +240,7 @@ const handlers = {
|
|
|
206
240
|
const parts = [
|
|
207
241
|
`タイトル: ${data.title}`, `ステータス: ${data.status}`,
|
|
208
242
|
`優先度: ${data.priority}`, `タイプ: ${data.type}`,
|
|
243
|
+
`担当者: ${data.assignee_name || 'なし'}`,
|
|
209
244
|
data.due_date ? `期限: ${data.due_date.slice(0, 16)}` : '',
|
|
210
245
|
data.body ? `\n詳細:\n${data.body}` : '',
|
|
211
246
|
data.content ? `\n内容:\n${data.content}` : '',
|
package/domains/slides.js
CHANGED
|
@@ -213,12 +213,34 @@ const handlers = {
|
|
|
213
213
|
return `- ${b.name}(テーマ:${b.theme || '未指定'}${parts ? ' / ' + parts : ''})\n 更新: ${b.updated_at}`;
|
|
214
214
|
}).join('\n');
|
|
215
215
|
},
|
|
216
|
+
// ⚠️ 2026-09-30: この2つは `callApi(path, 'POST', body)` という別流儀で書かれており、
|
|
217
|
+
// 文字列が options として展開されて {0:'P',1:'O',2:'S',3:'T'} になり、
|
|
218
|
+
// method も body も付かず **GET が飛んでいた**(153箇所中この2箇所だけの誤用)。
|
|
219
|
+
// ・apply は GET のルートが無く 404 のHTMLを res.json() が食って
|
|
220
|
+
// 「Unexpected token '<'」になっていた
|
|
221
|
+
// ・save は GET /slides/brand-themes(一覧)が 200 を返すためエラーにならず、
|
|
222
|
+
// r.name が undefined のまま「保存しました」と**嘘の成功**を返していた(より危険)
|
|
223
|
+
// 正しい形は callApi(path, { method, body })。
|
|
216
224
|
save_brand_theme: async ({ name, theme, deck }) => {
|
|
217
|
-
const r = await callApi('/slides/brand-themes',
|
|
225
|
+
const r = await callApi('/slides/brand-themes', {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
body: JSON.stringify({ name, theme, deck: deck || {} }),
|
|
228
|
+
});
|
|
229
|
+
// ⚠️ 応答に name が無いのに成功を名乗らない(サイレント失敗の再発防止)
|
|
230
|
+
if (!r || !r.name) {
|
|
231
|
+
throw new Error('テーマテンプレートの保存結果を確認できませんでした(応答に name がありません)。list_brand_themes で保存されたか確認してください。');
|
|
232
|
+
}
|
|
218
233
|
return `テーマテンプレート「${r.name}」を保存しました。apply_brand_theme で他のスライドにも当てられます。`;
|
|
219
234
|
},
|
|
220
235
|
apply_brand_theme: async ({ name, deck_id }) => {
|
|
221
|
-
const r = await callApi(`/slides/brand-themes/${encodeURIComponent(name)}/apply`,
|
|
236
|
+
const r = await callApi(`/slides/brand-themes/${encodeURIComponent(name)}/apply`, {
|
|
237
|
+
method: 'POST',
|
|
238
|
+
body: JSON.stringify({ deck_id }),
|
|
239
|
+
});
|
|
240
|
+
// ⚠️ 同上。適用できていないのに「適用しました」と言わない
|
|
241
|
+
if (!r || !r.applied) {
|
|
242
|
+
throw new Error('テーマテンプレートの適用結果を確認できませんでした(応答に applied がありません)。get_slide_deck でデッキの見た目を確認してください。');
|
|
243
|
+
}
|
|
222
244
|
return `「${r.applied}」の見た目を「${r.deck?.title || deck_id}」に適用しました(本文は変更していません)。`;
|
|
223
245
|
},
|
|
224
246
|
get_slide_deck: async ({ id, with_narration }) => {
|
package/lib/api.js
CHANGED
|
@@ -7,6 +7,18 @@ const API_KEY = process.env.OCR_API_KEY;
|
|
|
7
7
|
const BASE_URL = (process.env.OCR_BASE_URL || 'https://api.ownercloneai.com').replace(/\/$/, '');
|
|
8
8
|
|
|
9
9
|
async function callApi(path, options = {}) {
|
|
10
|
+
// ⚠️ 2026-09-30: `callApi(path, 'POST', body)` という別流儀で書かれた2箇所が静かに壊れていた
|
|
11
|
+
// (domains/slides.js の save_brand_theme / apply_brand_theme)。文字列を options として
|
|
12
|
+
// 展開すると {0:'P',1:'O',2:'S',3:'T'} になり method も body も付かず GET が飛ぶ。
|
|
13
|
+
// GET が 200 を返すエンドポイントでは**嘘の成功**になるため、型で弾いて気づけるようにする。
|
|
14
|
+
// ⚠️ 3引数形を受け付ける案は採らない(2つの流儀が併存すると同じ事故が再発する)。
|
|
15
|
+
if (options === null || typeof options !== 'object' || Array.isArray(options)) {
|
|
16
|
+
const got = options === null ? 'null' : (Array.isArray(options) ? 'array' : typeof options);
|
|
17
|
+
throw new Error(
|
|
18
|
+
`callApi の第2引数は fetch のオプションオブジェクトです(受け取った型: ${got})。`
|
|
19
|
+
+ `callApi(path, { method: 'POST', body: JSON.stringify(...) }) の形で呼んでください。`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
10
22
|
const url = `${BASE_URL}/api/mcp/v1${path}`;
|
|
11
23
|
const res = await fetch(url, {
|
|
12
24
|
...options,
|