owner-clone-ai-mcp 1.29.2 → 1.31.0

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.
@@ -43,6 +43,16 @@ const tools = [
43
43
  required: ['id'],
44
44
  },
45
45
  },
46
+ {
47
+ name: 'get_knowledge_image',
48
+ description: '画像カテゴリ(image)のナレッジの画像そのものを取得して内容を視覚的に確認します。画像に何が写っているか・見た目を判断する必要があるときに使います。',
49
+ annotations: { readOnlyHint: true },
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: { id: { type: 'string', description: '画像ナレッジのID' } },
53
+ required: ['id'],
54
+ },
55
+ },
46
56
  {
47
57
  name: 'list_knowledge_folders',
48
58
  description: 'ナレッジのフォルダ一覧を取得します。',
@@ -51,27 +61,42 @@ const tools = [
51
61
  },
52
62
  {
53
63
  name: 'create_knowledge',
54
- description: 'Owner Clone AIにナレッジを新規作成します。【書き込み権限付きキーが必要】作成後、自動的にベクトル化されセマンティック検索に対応します。',
64
+ description: `Owner Clone AIにナレッジを新規作成します。【書き込み権限付きキーが必要】作成後、自動的にベクトル化されセマンティック検索に対応します。
65
+
66
+ text_content(自由テキスト)か、content(カテゴリ別の構造化オブジェクト)のどちらかを渡してください。
67
+ content を渡すと各入力項目が正しく構造化保存され、本文テキストは自動生成されます(UIフォームと同じ扱い)。
68
+
69
+ ■ content のカテゴリ別フィールド(カテゴリに合わせて設定):
70
+ persona: persona_name(お名前) / age_gender(年齢) / gender(性別:男性|女性|その他) / job / concerns(緊急性のある悩みTOP3) / level(初級者|中級者|上級者) / level_reason / special_words / past_failures / wants(具体的ゴール) / info_source / purchase_behavior / why_instant_sell
71
+ profile: company_name / founded(設立年・事業開始年) / business / achievements / media / expertise(専門性) / representative
72
+ testimonial: publish_consent(掲載可否:全て掲載可|名前を伏せて掲載可|掲載不可) / service_name / customer_name / industry / self_intro / before_reason / results / how_achieved / beyond_expect / surrounding / recommend_for
73
+ tone: base_style / expressions / avoid / length_style / layout("mobile"=スマホ対応|"pc"=PC最適化) / sample
74
+ product: product_name / category(商品ジャンル) / price / urgent_problems / why_created / for_whom / services(★配列)
75
+ offer: offer_name / offer_type(メルマガ・公式LINEの登録誘導|資料・サンプル請求|個別相談・セミナー開催|チャレンジ企画|ウェビナー|その他) / price / duration / urgent_problems / why_created / for_whom / bonuses / guarantee / services(★配列) / objections / cta
76
+ other / note_manual / note_research / note_hint / note_article / note_meeting: body
77
+ ■ product/offer の services(繰り返し複合・オブジェクト配列):
78
+ services: [{ name:"サービス名", value:"いくらの価値があるか", result_1m:"1ヶ月で見込める結果", result_3m:"3ヶ月で見込める結果", result_6m:"6ヶ月で見込める結果", advantages:"競合より優れている3つの理由" }, ...複数可]`,
55
79
  annotations: { destructiveHint: false, idempotentHint: false },
56
80
  inputSchema: {
57
81
  type: 'object',
58
82
  properties: {
59
83
  title: { type: 'string', description: 'ナレッジのタイトル' },
60
- text_content: { type: 'string', description: 'ナレッジの本文テキスト' },
84
+ text_content: { type: 'string', description: 'ナレッジの本文テキスト(content を渡す場合は不要)' },
85
+ content: { type: 'object', description: 'カテゴリ別の構造化オブジェクト(上記参照)。渡すと本文テキストは自動生成される。product/offer の services は配列' },
61
86
  category: {
62
87
  type: 'string',
63
- description: 'カテゴリ(product / profile / testimonial / persona / tone / other)',
64
- enum: ['product', 'profile', 'testimonial', 'persona', 'tone', 'other'],
88
+ description: 'カテゴリ',
89
+ enum: ['offer', 'product', 'profile', 'testimonial', 'persona', 'tone', 'other', 'note_manual', 'note_research', 'note_hint', 'note_article', 'note_meeting'],
65
90
  },
66
91
  folder_id: { type: 'string', description: 'フォルダID(省略可)' },
67
92
  tags: { type: 'array', items: { type: 'string' }, description: 'タグの配列(例: ["マーケティング", "集客"])省略可。省略時はタグなしで登録。' },
68
93
  },
69
- required: ['title', 'text_content'],
94
+ required: ['title'],
70
95
  },
71
96
  },
72
97
  {
73
98
  name: 'update_knowledge',
74
- description: '既存のナレッジを更新します。【書き込み権限付きキーが必要】本文を変更した場合は自動的に再ベクトル化されます。folder_id でフォルダ移動も可能(null で未分類に移動)。',
99
+ description: '既存のナレッジを更新します。【書き込み権限付きキーが必要】本文を変更した場合は自動的に再ベクトル化されます。folder_id でフォルダ移動も可能(null で未分類に移動)。content(カテゴリ別構造化オブジェクト)を渡すと構造化保存+本文自動生成(フィールド仕様は create_knowledge 参照)。',
75
100
  annotations: { destructiveHint: false, idempotentHint: true },
76
101
  inputSchema: {
77
102
  type: 'object',
@@ -79,6 +104,7 @@ const tools = [
79
104
  id: { type: 'string', description: '更新するナレッジのID' },
80
105
  title: { type: 'string', description: '新しいタイトル(省略時は変更なし)' },
81
106
  text_content: { type: 'string', description: '新しい本文(省略時は変更なし)' },
107
+ content: { type: 'object', description: 'カテゴリ別の構造化オブジェクト(create_knowledge参照)。渡すと本文を自動再生成。product/offer の services は配列' },
82
108
  category: { type: 'string', description: '新しいカテゴリ(省略時は変更なし)' },
83
109
  folder_id: { type: 'string', description: '移動先フォルダID(null または省略で変更なし、空文字列で未分類へ移動)' },
84
110
  tags: { type: 'array', items: { type: 'string' }, description: 'タグの配列(省略時は変更なし、空配列[]でタグを全削除)' },
@@ -291,7 +317,19 @@ const handlers = {
291
317
  const data = await callApi(`/knowledge/${args.id}`);
292
318
  const tagLine = data.tags?.length ? `\nタグ: ${data.tags.join(', ')}` : '\nタグ: なし';
293
319
  const folderLine = data.folder_id ? `\nフォルダID: ${data.folder_id}` : '\nフォルダ: 未分類';
294
- return `タイトル: ${data.title}\nカテゴリ: ${data.category || 'その他'}${tagLine}${folderLine}\n作成日: ${data.created_at?.slice(0, 10)} / 更新日: ${data.updated_at?.slice(0, 10)}\n\n--- 内容 ---\n${data.text_content}`;
320
+ const imgHint = data.category === 'image' ? '\n\n※これは画像ナレッジです。画像そのものを見るには get_knowledge_image を使ってください。' : '';
321
+ return `タイトル: ${data.title}\nカテゴリ: ${data.category || 'その他'}${tagLine}${folderLine}\n作成日: ${data.created_at?.slice(0, 10)} / 更新日: ${data.updated_at?.slice(0, 10)}${imgHint}\n\n--- 内容 ---\n${data.text_content}`;
322
+ },
323
+
324
+ // 画像本体を取得して MCP の image コンテンツとして返す(Claude Desktop等でAIが視認できる)
325
+ async get_knowledge_image(args) {
326
+ const data = await callApi(`/knowledge/${args.id}/image`);
327
+ return {
328
+ content: [
329
+ { type: 'text', text: `画像「${data.title || ''}」` },
330
+ { type: 'image', data: data.base64, mimeType: data.mimeType || 'image/png' },
331
+ ],
332
+ };
295
333
  },
296
334
 
297
335
  async list_knowledge_folders(args) {
@@ -311,6 +349,7 @@ const handlers = {
311
349
  body: JSON.stringify({
312
350
  title: args.title, text_content: args.text_content,
313
351
  category: args.category || 'other', folder_id: args.folder_id,
352
+ ...(args.content !== undefined ? { content: args.content } : {}),
314
353
  ...(args.tags !== undefined ? { tags: args.tags } : {}),
315
354
  }),
316
355
  });
@@ -322,6 +361,7 @@ const handlers = {
322
361
  const body = {};
323
362
  if (args.title) body.title = args.title;
324
363
  if (args.text_content) body.text_content = args.text_content;
364
+ if (args.content !== undefined) body.content = args.content;
325
365
  if (args.category) body.category = args.category;
326
366
  if (args.folder_id !== undefined) body.folder_id = args.folder_id || null;
327
367
  if (args.tags !== undefined) body.tags = args.tags;
@@ -88,7 +88,7 @@ const tools = [
88
88
  },
89
89
  {
90
90
  name: 'create_task',
91
- description: 'タスクを新規作成します(type=manual)。【書き込み権限付きキーが必要】',
91
+ description: 'タスクを新規作成します(type=manual)。担当者(assignee)とプロジェクト(project)も指定できます。【書き込み権限付きキーが必要】',
92
92
  annotations: { destructiveHint: false, idempotentHint: false },
93
93
  inputSchema: {
94
94
  type: 'object',
@@ -97,13 +97,18 @@ const tools = [
97
97
  body: { type: 'string', description: '詳細・内容(省略可)' },
98
98
  priority: { type: 'string', description: '優先度: low/normal/high(デフォルト: normal)' },
99
99
  due_date: { type: 'string', description: '期限日時 ISO8601(省略可)' },
100
+ assignee_id: { type: 'string', description: '担当者のメンバーID(list_team_members で取得したUUID)。オーナー自身のID指定も可。' },
101
+ assignee_name: { type: 'string', description: '担当者の名前(部分一致で検索)。assignee_id を指定しない場合に使用。' },
102
+ project_id: { type: 'string', description: 'このタスクを紐づけるプロジェクトのID(list_projects で取得したUUID)' },
103
+ notify_assignee: { type: 'boolean', description: '担当者にChatworkで通知するか(デフォルト: false。trueで通知)' },
104
+ notify_chatwork_setting_id: { type: 'string', description: '担当者通知の送信先ChatworkグループID(list_chatwork_settings で取得。未指定時は自動選択)' },
100
105
  },
101
106
  required: ['title'],
102
107
  },
103
108
  },
104
109
  {
105
110
  name: 'update_task',
106
- description: 'タスクを更新します。【書き込み権限付きキーが必要】manual タスクは全フィールド更新可。他タイプはステータス変更のみ可。',
111
+ description: 'タスクを更新します。担当者(assignee)・プロジェクト(project)も変更できます。【書き込み権限付きキーが必要】manual タスクは全フィールド更新可。他タイプはステータス変更のみ可。',
107
112
  annotations: { destructiveHint: false, idempotentHint: true },
108
113
  inputSchema: {
109
114
  type: 'object',
@@ -114,6 +119,9 @@ const tools = [
114
119
  priority: { type: 'string', description: '新しい優先度: low/normal/high(manual のみ)' },
115
120
  due_date: { type: 'string', description: '新しい期限(manual のみ、null で期限解除)' },
116
121
  status: { type: 'string', description: 'ステータス変更: pending/done/dismissed' },
122
+ assignee_id: { type: 'string', description: '担当者のメンバーID(list_team_members のUUID。空文字で担当者解除)' },
123
+ assignee_name: { type: 'string', description: '担当者の名前(部分一致)。"解除"で担当者を外す。' },
124
+ project_id: { type: 'string', description: '紐づけるプロジェクトのID(list_projects のUUID。空文字で解除)' },
117
125
  },
118
126
  required: ['id'],
119
127
  },
@@ -207,18 +215,20 @@ const handlers = {
207
215
  },
208
216
 
209
217
  async create_task(args) {
210
- const data = await callApi('/tasks', {
211
- method: 'POST',
212
- body: JSON.stringify({ title: args.title, body: args.body, priority: args.priority, due_date: args.due_date }),
213
- });
218
+ const body = {};
219
+ ['title','body','priority','due_date','assignee_id','assignee_name','project_id','notify_assignee','notify_chatwork_setting_id']
220
+ .forEach(f => { if (args[f] !== undefined) body[f] = args[f]; });
221
+ const data = await callApi('/tasks', { method: 'POST', body: JSON.stringify(body) });
214
222
  return `✅ ${data.message}\nID: ${data.id}\nタイトル: ${data.title}\n優先度: ${data.priority}` +
215
- (data.due_date ? `\n期限: ${data.due_date.slice(0, 10)}` : '');
223
+ (data.due_date ? `\n期限: ${data.due_date.slice(0, 10)}` : '') +
224
+ (data.assignee_id ? `\n担当者ID: ${data.assignee_id}` : '') +
225
+ (data.project_id ? `\nプロジェクトID: ${data.project_id}` : '');
216
226
  },
217
227
 
218
228
  async update_task(args) {
219
229
  const body = {};
220
- const tFields = ['title','body','priority','due_date','status'];
221
- tFields.forEach(f => { if (args[f] !== undefined) body[f] = args[f]; });
230
+ ['title','body','priority','due_date','status','assignee_id','assignee_name','project_id']
231
+ .forEach(f => { if (args[f] !== undefined) body[f] = args[f]; });
222
232
  const data = await callApi(`/tasks/${args.id}`, { method: 'PATCH', body: JSON.stringify(body) });
223
233
  return `✅ ${data.message}\nID: ${data.id}`;
224
234
  },
package/index.js CHANGED
@@ -45,6 +45,10 @@ async function main() {
45
45
  const { name, arguments: args } = request.params;
46
46
  try {
47
47
  const result = await executeTool(name, args || {});
48
+ // ハンドラが MCP コンテンツ(画像など)を直接返した場合はそのまま返す
49
+ if (result && typeof result === 'object' && Array.isArray(result.content)) {
50
+ return result;
51
+ }
48
52
  return { content: [{ type: 'text', text: result }] };
49
53
  } catch (err) {
50
54
  return {
package/lib/api.js CHANGED
@@ -14,7 +14,11 @@ async function callApi(path, options = {}) {
14
14
  },
15
15
  });
16
16
  const json = await res.json();
17
- if (!res.ok) throw new Error(json.error || `API error ${res.status}`);
17
+ if (!res.ok) {
18
+ // バックエンドが detail(DB例外メッセージ等)を返す場合は原因特定のため併記
19
+ const msg = json && json.detail ? `${json.error || 'エラー'}: ${json.detail}` : (json.error || `API error ${res.status}`);
20
+ throw new Error(msg);
21
+ }
18
22
  return json;
19
23
  }
20
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owner-clone-ai-mcp",
3
- "version": "1.29.2",
3
+ "version": "1.31.0",
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": {