@mac-claw/mcp 1.1.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.
package/server.js ADDED
@@ -0,0 +1,964 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Mac Claw MCP Server
5
+ * AI Mac Marketplace - MCP Tools (47 tools)
6
+ *
7
+ * Supports both stdio and HTTP transport:
8
+ * stdio: MACCLAW_API_KEY=mc_xxx node server.js
9
+ * HTTP: MACCLAW_API_KEY=mc_xxx MACCLAW_TRANSPORT=http MACCLAW_PORT=3006 node server.js
10
+ */
11
+
12
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
15
+ import { MacClawApiClient } from './lib/api-client.js';
16
+
17
+ const API_KEY = process.env.MACCLAW_API_KEY || '';
18
+ const BASE_URL = process.env.MACCLAW_BASE_URL || 'https://macclaw.jp';
19
+ const TRANSPORT = process.env.MACCLAW_TRANSPORT || 'stdio';
20
+ const PORT = parseInt(process.env.MACCLAW_PORT || '3006', 10);
21
+
22
+ if (!API_KEY) {
23
+ console.error('[macclaw-mcp] Warning: MACCLAW_API_KEY is not set.');
24
+ console.error('[macclaw-mcp] Get your API key at: https://macclaw.jp/admin/');
25
+ }
26
+
27
+ const client = new MacClawApiClient(API_KEY, BASE_URL);
28
+
29
+ const server = new Server(
30
+ { name: 'macclaw-mcp-server', version: '1.1.0' },
31
+ { capabilities: { tools: {} } }
32
+ );
33
+
34
+ // ==========================================
35
+ // Tool definitions (47 tools)
36
+ // ==========================================
37
+
38
+ const TOOLS = [
39
+ // ------------------------------------------
40
+ // 商品 (7)
41
+ // ------------------------------------------
42
+ {
43
+ name: 'items_list',
44
+ description: 'Mac Clawの商品一覧を取得。デバイス種別・チップ・メモリ・価格帯・ステータスでフィルタリング可能。ページネーション対応',
45
+ inputSchema: {
46
+ type: 'object',
47
+ properties: {
48
+ device_type: { type: 'string', enum: ['mac_mini', 'mac_studio'], description: 'デバイス種別 (mac_mini=Mac Mini, mac_studio=Mac Studio)' },
49
+ chip: { type: 'string', description: 'チップ名で絞り込み (例: M4 Pro, M2 Ultra)' },
50
+ memory_min: { type: 'integer', description: '最小メモリ容量 (GB)' },
51
+ price_min: { type: 'integer', description: '最低価格 (円)' },
52
+ price_max: { type: 'integer', description: '最高価格 (円)' },
53
+ status: { type: 'string', enum: ['active', 'sold', 'draft', 'cancelled'], description: '出品ステータス (active=出品中, sold=売却済, draft=下書き, cancelled=キャンセル)' },
54
+ sort: { type: 'string', enum: ['newest', 'price_asc', 'price_desc'], description: '並び順 (newest=新着順, price_asc=価格安順, price_desc=価格高順)' },
55
+ page: { type: 'integer', description: 'ページ番号 (デフォルト: 1)' },
56
+ per_page: { type: 'integer', description: '1ページあたりの件数 (デフォルト: 20)' },
57
+ },
58
+ },
59
+ },
60
+ {
61
+ name: 'items_get',
62
+ description: '商品の詳細情報を取得。スペック・価格・コンディション・出品者情報・いいね数・コメントを含む',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ id: { type: 'integer', description: '商品ID' },
67
+ },
68
+ required: ['id'],
69
+ },
70
+ },
71
+ {
72
+ name: 'items_search',
73
+ description: 'キーワードで商品をテキスト検索。タイトル・説明文・チップ名などを横断検索する',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ query: { type: 'string', description: '検索キーワード (例: M4 Pro 48GB)' },
78
+ page: { type: 'integer', description: 'ページ番号' },
79
+ per_page: { type: 'integer', description: '1ページあたりの件数' },
80
+ },
81
+ required: ['query'],
82
+ },
83
+ },
84
+ {
85
+ name: 'items_create',
86
+ description: '新しい商品を出品する(下書き状態で作成)。items_publishで公開する',
87
+ inputSchema: {
88
+ type: 'object',
89
+ properties: {
90
+ title: { type: 'string', description: '出品タイトル (例: Mac Mini M4 Pro / 24GB / 512GB)' },
91
+ description: { type: 'string', description: '商品説明文 (コンディション詳細・付属品・使用状況など)' },
92
+ device_type: { type: 'string', description: 'デバイス種別 (mac_mini / mac_studio)' },
93
+ model_year: { type: 'string', description: 'モデル年 (例: 2024, 2023)' },
94
+ chip: { type: 'string', description: 'チップ名 (例: M4 Pro, M2 Ultra)' },
95
+ memory_gb: { type: 'integer', description: '統合メモリ容量 (GB)' },
96
+ storage_gb: { type: 'integer', description: 'SSD容量 (GB)' },
97
+ price: { type: 'integer', description: '販売価格 (円)' },
98
+ has_box: { type: 'boolean', description: '元箱あり/なし' },
99
+ has_10gbe: { type: 'boolean', description: '10GbEオプション搭載 (Mac Miniのみ)' },
100
+ purchase_period: { type: 'string', description: '購入時期 (例: 2024年12月)' },
101
+ sell_reason: { type: 'string', description: '売却理由' },
102
+ condition_grade: { type: 'string', description: 'コンディショングレード (S=未使用/新品同様, A=美品, B=良品, C=並品)' },
103
+ condition_note: { type: 'string', description: 'コンディション補足説明 (傷・汚れの詳細など)' },
104
+ shipping_payer: { type: 'string', description: '送料負担 (seller=出品者負担, buyer=購入者負担)' },
105
+ shipping_from_pref: { type: 'string', description: '発送元都道府県 (例: 東京都)' },
106
+ shipping_days: { type: 'string', description: '発送までの日数 (例: 1〜2日, 3〜7日)' },
107
+ },
108
+ required: ['title', 'description', 'device_type', 'model_year', 'chip', 'memory_gb', 'storage_gb', 'price'],
109
+ },
110
+ },
111
+ {
112
+ name: 'items_update',
113
+ description: '出品中または下書きの商品情報を編集する。指定したフィールドのみ更新',
114
+ inputSchema: {
115
+ type: 'object',
116
+ properties: {
117
+ id: { type: 'integer', description: '商品ID' },
118
+ title: { type: 'string', description: '出品タイトル' },
119
+ description: { type: 'string', description: '商品説明文' },
120
+ device_type: { type: 'string', description: 'デバイス種別' },
121
+ model_year: { type: 'string', description: 'モデル年' },
122
+ chip: { type: 'string', description: 'チップ名' },
123
+ memory_gb: { type: 'integer', description: '統合メモリ容量 (GB)' },
124
+ storage_gb: { type: 'integer', description: 'SSD容量 (GB)' },
125
+ price: { type: 'integer', description: '販売価格 (円)' },
126
+ has_box: { type: 'boolean', description: '元箱あり/なし' },
127
+ has_10gbe: { type: 'boolean', description: '10GbEオプション搭載' },
128
+ purchase_period: { type: 'string', description: '購入時期' },
129
+ sell_reason: { type: 'string', description: '売却理由' },
130
+ condition_grade: { type: 'string', description: 'コンディショングレード (S/A/B/C)' },
131
+ condition_note: { type: 'string', description: 'コンディション補足説明' },
132
+ shipping_payer: { type: 'string', description: '送料負担 (seller/buyer)' },
133
+ shipping_from_pref: { type: 'string', description: '発送元都道府県' },
134
+ shipping_days: { type: 'string', description: '発送までの日数' },
135
+ },
136
+ required: ['id'],
137
+ },
138
+ },
139
+ {
140
+ name: 'items_publish',
141
+ description: '下書き状態の商品を公開する。公開後は出品中(active)ステータスになり購入可能になる',
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ id: { type: 'integer', description: '公開する商品ID' },
146
+ },
147
+ required: ['id'],
148
+ },
149
+ },
150
+ {
151
+ name: 'items_cancel',
152
+ description: '出品中の商品をキャンセル(取り下げ)する。取引成立前の商品のみキャンセル可能',
153
+ inputSchema: {
154
+ type: 'object',
155
+ properties: {
156
+ id: { type: 'integer', description: 'キャンセルする商品ID' },
157
+ },
158
+ required: ['id'],
159
+ },
160
+ },
161
+
162
+ // ------------------------------------------
163
+ // 相場 (5)
164
+ // ------------------------------------------
165
+ {
166
+ name: 'market_price_range',
167
+ description: 'チップ別・デバイス別の価格レンジ(最安値・最高値・中央値・平均値)を取得。相場把握に使用',
168
+ inputSchema: {
169
+ type: 'object',
170
+ properties: {
171
+ chip: { type: 'string', description: 'チップ名で絞り込み (例: M4 Pro)' },
172
+ device_type: { type: 'string', description: 'デバイス種別で絞り込み (mac_mini / mac_studio)' },
173
+ },
174
+ },
175
+ },
176
+ {
177
+ name: 'market_recent_sales',
178
+ description: '直近の成約データ一覧を取得。実際に売れた価格・コンディション・成約日時を確認できる',
179
+ inputSchema: {
180
+ type: 'object',
181
+ properties: {
182
+ limit: { type: 'integer', description: '取得件数 (デフォルト: 20)' },
183
+ chip: { type: 'string', description: 'チップ名で絞り込み' },
184
+ },
185
+ },
186
+ },
187
+ {
188
+ name: 'market_price_suggest',
189
+ description: '指定スペックの推定相場価格を算出。成約データをもとにAIが適正価格を提案する',
190
+ inputSchema: {
191
+ type: 'object',
192
+ properties: {
193
+ chip: { type: 'string', description: 'チップ名 (例: M4 Pro)' },
194
+ memory_gb: { type: 'integer', description: '統合メモリ容量 (GB)' },
195
+ device_type: { type: 'string', description: 'デバイス種別 (mac_mini / mac_studio)' },
196
+ condition_grade: { type: 'string', description: 'コンディショングレード (S/A/B/C)' },
197
+ },
198
+ required: ['chip', 'memory_gb'],
199
+ },
200
+ },
201
+ {
202
+ name: 'market_retail_compare',
203
+ description: '指定スペックの新品Apple公式価格との比較情報を取得。中古割引率・お得度を算出',
204
+ inputSchema: {
205
+ type: 'object',
206
+ properties: {
207
+ chip: { type: 'string', description: 'チップ名 (例: M4 Pro)' },
208
+ memory_gb: { type: 'integer', description: '統合メモリ容量 (GB)' },
209
+ storage_gb: { type: 'integer', description: 'SSD容量 (GB)' },
210
+ device_type: { type: 'string', description: 'デバイス種別 (mac_mini / mac_studio)' },
211
+ price: { type: 'integer', description: '比較する価格 (円)' },
212
+ },
213
+ required: ['chip', 'memory_gb', 'storage_gb', 'device_type', 'price'],
214
+ },
215
+ },
216
+ {
217
+ name: 'market_demand_score',
218
+ description: '商品の需要スコア(0〜100)を取得。スコアが高いほど早期成約が期待できる',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ item_id: { type: 'integer', description: '需要スコアを調べる商品ID' },
223
+ },
224
+ required: ['item_id'],
225
+ },
226
+ },
227
+
228
+ // ------------------------------------------
229
+ // 決済 (1)
230
+ // ------------------------------------------
231
+ {
232
+ name: 'checkout_create',
233
+ description: '商品のStripe Checkout URLを生成。購入者はこのURLでクレジットカード決済を行う',
234
+ inputSchema: {
235
+ type: 'object',
236
+ properties: {
237
+ item_id: { type: 'integer', description: '購入する商品ID' },
238
+ },
239
+ required: ['item_id'],
240
+ },
241
+ },
242
+
243
+ // ------------------------------------------
244
+ // 取引 (3)
245
+ // ------------------------------------------
246
+ {
247
+ name: 'transactions_list',
248
+ description: '自分が関わる取引一覧を取得。buyer(購入者)またはseller(出品者)でフィルタリング可能',
249
+ inputSchema: {
250
+ type: 'object',
251
+ properties: {
252
+ role: { type: 'string', enum: ['buyer', 'seller'], description: '役割フィルタ (buyer=購入者として, seller=出品者として)' },
253
+ status: { type: 'string', description: '取引ステータスで絞り込み (例: payment_pending, paid, shipped, completed)' },
254
+ page: { type: 'integer', description: 'ページ番号' },
255
+ },
256
+ },
257
+ },
258
+ {
259
+ name: 'transactions_get',
260
+ description: '取引の詳細情報を取得。決済状況・発送情報・メッセージ履歴のサマリーを含む',
261
+ inputSchema: {
262
+ type: 'object',
263
+ properties: {
264
+ id: { type: 'integer', description: '取引ID' },
265
+ },
266
+ required: ['id'],
267
+ },
268
+ },
269
+ {
270
+ name: 'transactions_ship',
271
+ description: '取引に発送情報を登録する。追跡番号を入力すると購入者に発送通知が送られる',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ id: { type: 'integer', description: '取引ID' },
276
+ tracking_number: { type: 'string', description: '配送追跡番号 (例: 1234-5678-9012)' },
277
+ },
278
+ required: ['id', 'tracking_number'],
279
+ },
280
+ },
281
+
282
+ // ------------------------------------------
283
+ // メッセージ (2)
284
+ // ------------------------------------------
285
+ {
286
+ name: 'messages_list',
287
+ description: '取引に紐づくメッセージ一覧を時系列で取得。出品者・購入者間のやり取りを確認できる',
288
+ inputSchema: {
289
+ type: 'object',
290
+ properties: {
291
+ transaction_id: { type: 'integer', description: '取引ID' },
292
+ },
293
+ required: ['transaction_id'],
294
+ },
295
+ },
296
+ {
297
+ name: 'messages_send',
298
+ description: '取引相手にメッセージを送信。質問・交渉・発送連絡などに使用する',
299
+ inputSchema: {
300
+ type: 'object',
301
+ properties: {
302
+ transaction_id: { type: 'integer', description: '取引ID' },
303
+ body: { type: 'string', description: 'メッセージ本文' },
304
+ },
305
+ required: ['transaction_id', 'body'],
306
+ },
307
+ },
308
+
309
+ // ------------------------------------------
310
+ // ユーザー (3)
311
+ // ------------------------------------------
312
+ {
313
+ name: 'users_me',
314
+ description: '自分のプロフィール・出品数・成約数・評価スコアなどの統計情報を取得する',
315
+ inputSchema: {
316
+ type: 'object',
317
+ properties: {},
318
+ },
319
+ },
320
+ {
321
+ name: 'users_get',
322
+ description: '指定ユーザーのプロフィール・出品履歴・レビュー評価を取得。信頼性確認に使用',
323
+ inputSchema: {
324
+ type: 'object',
325
+ properties: {
326
+ id: { type: 'integer', description: 'ユーザーID' },
327
+ },
328
+ required: ['id'],
329
+ },
330
+ },
331
+ {
332
+ name: 'users_update_profile',
333
+ description: '自分のプロフィール情報(表示名・自己紹介)を更新する',
334
+ inputSchema: {
335
+ type: 'object',
336
+ properties: {
337
+ display_name: { type: 'string', description: '表示名' },
338
+ bio: { type: 'string', description: '自己紹介文' },
339
+ },
340
+ },
341
+ },
342
+
343
+ // ------------------------------------------
344
+ // ソーシャル (3)
345
+ // ------------------------------------------
346
+ {
347
+ name: 'social_like',
348
+ description: '商品にいいね / いいね解除(トグル)。ウォッチリスト的に使用可能',
349
+ inputSchema: {
350
+ type: 'object',
351
+ properties: {
352
+ item_id: { type: 'integer', description: 'いいねする商品ID' },
353
+ },
354
+ required: ['item_id'],
355
+ },
356
+ },
357
+ {
358
+ name: 'social_comment',
359
+ description: '商品ページにコメントを投稿。質問・値下げ交渉・感想などを公開メッセージとして投稿',
360
+ inputSchema: {
361
+ type: 'object',
362
+ properties: {
363
+ item_id: { type: 'integer', description: 'コメントする商品ID' },
364
+ body: { type: 'string', description: 'コメント本文' },
365
+ },
366
+ required: ['item_id', 'body'],
367
+ },
368
+ },
369
+ {
370
+ name: 'social_follow',
371
+ description: 'ユーザーをフォロー / フォロー解除(トグル)。フォロー中のユーザーの新着出品を追跡できる',
372
+ inputSchema: {
373
+ type: 'object',
374
+ properties: {
375
+ user_id: { type: 'integer', description: 'フォローするユーザーID' },
376
+ },
377
+ required: ['user_id'],
378
+ },
379
+ },
380
+
381
+ // ------------------------------------------
382
+ // レビュー (2)
383
+ // ------------------------------------------
384
+ {
385
+ name: 'reviews_list',
386
+ description: 'ユーザーまたは商品に紐づくレビュー一覧を取得。評価・コメント・投稿日時を含む',
387
+ inputSchema: {
388
+ type: 'object',
389
+ properties: {
390
+ user_id: { type: 'integer', description: 'ユーザーIDで絞り込み' },
391
+ item_id: { type: 'integer', description: '商品IDで絞り込み' },
392
+ page: { type: 'integer', description: 'ページ番号' },
393
+ },
394
+ },
395
+ },
396
+ {
397
+ name: 'reviews_post',
398
+ description: '取引完了後にレビューを投稿。評価(1〜5)とコメントで取引相手を評価する',
399
+ inputSchema: {
400
+ type: 'object',
401
+ properties: {
402
+ transaction_id: { type: 'integer', description: 'レビュー対象の取引ID' },
403
+ rating: { type: 'integer', description: '評価 (1=最低 〜 5=最高)' },
404
+ comment: { type: 'string', description: 'レビューコメント' },
405
+ },
406
+ required: ['transaction_id', 'rating'],
407
+ },
408
+ },
409
+
410
+ // ------------------------------------------
411
+ // ウォッチ (5)
412
+ // ------------------------------------------
413
+ {
414
+ name: 'watch_create',
415
+ description: '希望スペック・価格条件を登録。条件に合う商品が出品されたら自動通知を受け取る',
416
+ inputSchema: {
417
+ type: 'object',
418
+ properties: {
419
+ label: { type: 'string', description: 'ウォッチ名 (例: M4 Pro 48GB 予算15万円以下)' },
420
+ conditions: {
421
+ type: 'object',
422
+ description: 'ウォッチ条件 (device_type, chip, memory_gb_min, price_max, condition_gradeを指定)',
423
+ properties: {
424
+ device_type: { type: 'string', description: 'デバイス種別 (mac_mini / mac_studio)' },
425
+ chip: { type: 'string', description: 'チップ名 (例: M4 Pro)' },
426
+ memory_gb_min: { type: 'integer', description: '最小メモリ容量 (GB)' },
427
+ price_max: { type: 'integer', description: '最高価格 (円)' },
428
+ condition_grade: { type: 'string', description: 'コンディショングレード以上 (S/A/B/C)' },
429
+ },
430
+ },
431
+ notify_method: { type: 'string', enum: ['api', 'email'], description: '通知方法 (api=API通知, email=メール通知)' },
432
+ },
433
+ required: ['label', 'conditions'],
434
+ },
435
+ },
436
+ {
437
+ name: 'watch_list',
438
+ description: '登録中のウォッチ一覧を取得。条件・通知方法・マッチ件数を確認できる',
439
+ inputSchema: {
440
+ type: 'object',
441
+ properties: {},
442
+ },
443
+ },
444
+ {
445
+ name: 'watch_delete',
446
+ description: 'ウォッチを削除する。不要になった条件通知を解除する',
447
+ inputSchema: {
448
+ type: 'object',
449
+ properties: {
450
+ id: { type: 'integer', description: '削除するウォッチID' },
451
+ },
452
+ required: ['id'],
453
+ },
454
+ },
455
+ {
456
+ name: 'watch_check',
457
+ description: '全アクティブウォッチの条件チェックを実行。新着マッチがあれば自動通知を送る',
458
+ inputSchema: {
459
+ type: 'object',
460
+ properties: {},
461
+ },
462
+ },
463
+ {
464
+ name: 'watch_matches',
465
+ description: 'ウォッチにマッチした商品の履歴を取得。通知済み/未通知のステータスを含む',
466
+ inputSchema: {
467
+ type: 'object',
468
+ properties: {
469
+ watch_id: { type: 'integer', description: '特定ウォッチIDで絞り込み (省略時は全ウォッチ)' },
470
+ page: { type: 'integer', description: 'ページ番号' },
471
+ },
472
+ },
473
+ },
474
+
475
+ // ------------------------------------------
476
+ // 検品 (2)
477
+ // ------------------------------------------
478
+ {
479
+ name: 'inspect_submit',
480
+ description: '商品のリモート検品データを送信。ハードウェア情報・Geekbenchスコア・ディスク健康状態を記録する',
481
+ inputSchema: {
482
+ type: 'object',
483
+ properties: {
484
+ item_id: { type: 'integer', description: '検品対象の商品ID' },
485
+ hardware_data: { type: 'object', description: 'ハードウェア情報 (system_profilerの出力等をJSONで)' },
486
+ geekbench_single: { type: 'integer', description: 'Geekbenchシングルコアスコア' },
487
+ geekbench_multi: { type: 'integer', description: 'Geekbenchマルチコアスコア' },
488
+ disk_health_percent: { type: 'integer', description: 'ディスク健康度 (0-100%)' },
489
+ disk_total_bytes: { type: 'integer', description: 'ディスク総容量 (bytes)' },
490
+ disk_used_bytes: { type: 'integer', description: 'ディスク使用量 (bytes)' },
491
+ },
492
+ required: ['item_id', 'hardware_data'],
493
+ },
494
+ },
495
+ {
496
+ name: 'inspect_get',
497
+ description: '商品の検品データを取得。ハードウェアスペック・ベンチマーク・ディスク状態を確認できる',
498
+ inputSchema: {
499
+ type: 'object',
500
+ properties: {
501
+ item_id: { type: 'integer', description: '商品ID' },
502
+ },
503
+ required: ['item_id'],
504
+ },
505
+ },
506
+
507
+ // ------------------------------------------
508
+ // 通知 (3)
509
+ // ------------------------------------------
510
+ {
511
+ name: 'notifications_broadcast',
512
+ description: 'ウォッチ登録ユーザーに新着商品の通知を一括送信する。出品者が自分の商品を宣伝する際に使用',
513
+ inputSchema: {
514
+ type: 'object',
515
+ properties: {
516
+ item_id: { type: 'integer', description: '通知対象の商品ID (自分の出品のみ)' },
517
+ },
518
+ required: ['item_id'],
519
+ },
520
+ },
521
+ {
522
+ name: 'notifications_list',
523
+ description: '自分の通知一覧を取得。ウォッチマッチ・新着出品・値下げ・システム通知を含む',
524
+ inputSchema: {
525
+ type: 'object',
526
+ properties: {
527
+ unread_only: { type: 'boolean', description: '未読のみ取得する場合 true' },
528
+ page: { type: 'integer', description: 'ページ番号' },
529
+ },
530
+ },
531
+ },
532
+ {
533
+ name: 'notifications_read',
534
+ description: '通知を既読にする。IDを指定すると単一既読、省略すると全既読になる',
535
+ inputSchema: {
536
+ type: 'object',
537
+ properties: {
538
+ id: { type: 'integer', description: '既読にする通知ID (省略で全既読)' },
539
+ },
540
+ },
541
+ },
542
+
543
+ // ------------------------------------------
544
+ // エージェント (1)
545
+ // ------------------------------------------
546
+ {
547
+ name: 'agent_upgrade_plan',
548
+ description: '買い替えシミュレーション。現在のスペックを入力すると、売却予想額・ターゲット機の相場・差額を計算する',
549
+ inputSchema: {
550
+ type: 'object',
551
+ properties: {
552
+ current_device_type: { type: 'string', description: '現在のデバイス種別 (mac_mini / mac_studio)' },
553
+ current_chip: { type: 'string', description: '現在のチップ名 (例: M2 Pro)' },
554
+ current_memory_gb: { type: 'integer', description: '現在のメモリ容量 (GB)' },
555
+ current_storage_gb: { type: 'integer', description: '現在のストレージ容量 (GB)' },
556
+ target_chip: { type: 'string', description: 'アップグレード先のチップ名 (例: M4 Pro)' },
557
+ target_memory_gb: { type: 'integer', description: 'アップグレード先のメモリ容量 (GB)' },
558
+ },
559
+ required: ['current_chip', 'current_memory_gb'],
560
+ },
561
+ },
562
+
563
+ // ------------------------------------------
564
+ // 決済追加 (2)
565
+ // ------------------------------------------
566
+ {
567
+ name: 'payment_status',
568
+ description: '取引の決済状態を確認する。Stripe決済ID・金額・手数料・ステータスを取得',
569
+ inputSchema: {
570
+ type: 'object',
571
+ properties: {
572
+ transaction_id: { type: 'integer', description: '取引ID' },
573
+ },
574
+ required: ['transaction_id'],
575
+ },
576
+ },
577
+ {
578
+ name: 'payment_refund',
579
+ description: '返金リクエストを実行する。pending_shipまたはshippedステータスの取引のみ可能',
580
+ inputSchema: {
581
+ type: 'object',
582
+ properties: {
583
+ transaction_id: { type: 'integer', description: '返金する取引ID' },
584
+ reason: { type: 'string', description: '返金理由' },
585
+ },
586
+ required: ['transaction_id'],
587
+ },
588
+ },
589
+
590
+ // ------------------------------------------
591
+ // Connect (2)
592
+ // ------------------------------------------
593
+ {
594
+ name: 'connect_status',
595
+ description: 'Stripe Connect(売上受取口座)の設定状態を確認する',
596
+ inputSchema: {
597
+ type: 'object',
598
+ properties: {},
599
+ },
600
+ },
601
+ {
602
+ name: 'connect_balance',
603
+ description: 'Stripe Connect口座の残高(利用可能額・保留額)を取得する',
604
+ inputSchema: {
605
+ type: 'object',
606
+ properties: {},
607
+ },
608
+ },
609
+
610
+ // ------------------------------------------
611
+ // スペック (3)
612
+ // ------------------------------------------
613
+ {
614
+ name: 'specs_chip_list',
615
+ description: 'Mac Clawが対応するAppleシリコンチップ一覧を取得。M1〜M4系列・Ultra/Pro/Maxを含む',
616
+ inputSchema: {
617
+ type: 'object',
618
+ properties: {},
619
+ },
620
+ },
621
+ {
622
+ name: 'specs_chip_detail',
623
+ description: '指定チップの詳細スペック(CPU/GPU/NPUコア数・対応メモリ上限・対応デバイス)を取得',
624
+ inputSchema: {
625
+ type: 'object',
626
+ properties: {
627
+ chip: { type: 'string', description: 'チップ名 (例: M4 Pro, M2 Ultra)' },
628
+ },
629
+ required: ['chip'],
630
+ },
631
+ },
632
+ {
633
+ name: 'specs_device_models',
634
+ description: 'Mac Mini / Mac Studioのモデル年別・チップ別の構成オプション一覧を取得',
635
+ inputSchema: {
636
+ type: 'object',
637
+ properties: {},
638
+ },
639
+ },
640
+
641
+ // ------------------------------------------
642
+ // 統計 (1)
643
+ // ------------------------------------------
644
+ {
645
+ name: 'stats_dashboard',
646
+ description: 'マーケットプレイスの統計ダッシュボードを取得。総出品数・成約数・平均成約価格・人気チップランキングを含む',
647
+ inputSchema: {
648
+ type: 'object',
649
+ properties: {},
650
+ },
651
+ },
652
+
653
+ // ------------------------------------------
654
+ // ヘルス (1)
655
+ // ------------------------------------------
656
+ {
657
+ name: 'health',
658
+ description: 'Mac Claw APIのヘルスチェック。認証不要。サーバー稼働状況とバージョン情報を返す',
659
+ inputSchema: {
660
+ type: 'object',
661
+ properties: {},
662
+ },
663
+ },
664
+ ];
665
+
666
+ // List tools
667
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
668
+ tools: TOOLS,
669
+ }));
670
+
671
+ // Handle tool calls
672
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
673
+ const { name, arguments: args } = request.params;
674
+
675
+ try {
676
+ let result;
677
+ switch (name) {
678
+ // 商品
679
+ case 'items_list':
680
+ result = await client.itemsList(args || {});
681
+ break;
682
+ case 'items_get':
683
+ result = await client.itemsGet(args.id);
684
+ break;
685
+ case 'items_search':
686
+ result = await client.itemsSearch(args.query, {
687
+ ...(args.page !== undefined && { page: args.page }),
688
+ ...(args.per_page !== undefined && { per_page: args.per_page }),
689
+ });
690
+ break;
691
+ case 'items_create':
692
+ result = await client.itemsCreate(args);
693
+ break;
694
+ case 'items_update': {
695
+ const { id, ...data } = args;
696
+ result = await client.itemsUpdate(id, data);
697
+ break;
698
+ }
699
+ case 'items_publish':
700
+ result = await client.itemsPublish(args.id);
701
+ break;
702
+ case 'items_cancel':
703
+ result = await client.itemsCancel(args.id);
704
+ break;
705
+
706
+ // 相場
707
+ case 'market_price_range':
708
+ result = await client.marketPriceRange(args || {});
709
+ break;
710
+ case 'market_recent_sales':
711
+ result = await client.marketRecentSales(args || {});
712
+ break;
713
+ case 'market_price_suggest':
714
+ result = await client.marketPriceSuggest(args);
715
+ break;
716
+ case 'market_retail_compare':
717
+ result = await client.marketRetailCompare(args);
718
+ break;
719
+ case 'market_demand_score':
720
+ result = await client.marketDemandScore(args.item_id);
721
+ break;
722
+
723
+ // 決済
724
+ case 'checkout_create':
725
+ result = await client.checkoutCreate(args.item_id);
726
+ break;
727
+
728
+ // 取引
729
+ case 'transactions_list':
730
+ result = await client.transactionsList(args || {});
731
+ break;
732
+ case 'transactions_get':
733
+ result = await client.transactionsGet(args.id);
734
+ break;
735
+ case 'transactions_ship':
736
+ result = await client.transactionsShip(args.id, args.tracking_number);
737
+ break;
738
+
739
+ // メッセージ
740
+ case 'messages_list':
741
+ result = await client.messagesList(args.transaction_id);
742
+ break;
743
+ case 'messages_send':
744
+ result = await client.messagesSend(args.transaction_id, args.body);
745
+ break;
746
+
747
+ // ユーザー
748
+ case 'users_me':
749
+ result = await client.usersMe();
750
+ break;
751
+ case 'users_get':
752
+ result = await client.usersGet(args.id);
753
+ break;
754
+ case 'users_update_profile':
755
+ result = await client.usersUpdateProfile(args || {});
756
+ break;
757
+
758
+ // ソーシャル
759
+ case 'social_like':
760
+ result = await client.socialLike(args.item_id);
761
+ break;
762
+ case 'social_comment':
763
+ result = await client.socialComment(args.item_id, args.body);
764
+ break;
765
+ case 'social_follow':
766
+ result = await client.socialFollow(args.user_id);
767
+ break;
768
+
769
+ // レビュー
770
+ case 'reviews_list':
771
+ result = await client.reviewsList(args || {});
772
+ break;
773
+ case 'reviews_post':
774
+ result = await client.reviewsPost(args.transaction_id, args.rating, args.comment);
775
+ break;
776
+
777
+ // ウォッチ
778
+ case 'watch_create':
779
+ result = await client.watchCreate(args);
780
+ break;
781
+ case 'watch_list':
782
+ result = await client.watchList();
783
+ break;
784
+ case 'watch_delete':
785
+ result = await client.watchDelete(args.id);
786
+ break;
787
+ case 'watch_check':
788
+ result = await client.watchCheck();
789
+ break;
790
+ case 'watch_matches':
791
+ result = await client.watchMatches(args || {});
792
+ break;
793
+
794
+ // 検品
795
+ case 'inspect_submit':
796
+ result = await client.inspectSubmit(args);
797
+ break;
798
+ case 'inspect_get':
799
+ result = await client.inspectGet(args.item_id);
800
+ break;
801
+
802
+ // 通知
803
+ case 'notifications_broadcast':
804
+ result = await client.notificationsBroadcast(args.item_id);
805
+ break;
806
+ case 'notifications_list':
807
+ result = await client.notificationsList(args || {});
808
+ break;
809
+ case 'notifications_read':
810
+ result = await client.notificationsRead(args || {});
811
+ break;
812
+
813
+ // エージェント
814
+ case 'agent_upgrade_plan':
815
+ result = await client.agentUpgradePlan(args);
816
+ break;
817
+
818
+ // 決済追加
819
+ case 'payment_status':
820
+ result = await client.paymentStatus(args.transaction_id);
821
+ break;
822
+ case 'payment_refund':
823
+ result = await client.paymentRefund(args.transaction_id, args.reason);
824
+ break;
825
+
826
+ // Connect
827
+ case 'connect_status':
828
+ result = await client.connectStatus();
829
+ break;
830
+ case 'connect_balance':
831
+ result = await client.connectBalance();
832
+ break;
833
+
834
+ // スペック
835
+ case 'specs_chip_list':
836
+ result = await client.specsChipList();
837
+ break;
838
+ case 'specs_chip_detail':
839
+ result = await client.specsChipDetail(args.chip);
840
+ break;
841
+ case 'specs_device_models':
842
+ result = await client.specsDeviceModels();
843
+ break;
844
+
845
+ // 統計
846
+ case 'stats_dashboard':
847
+ result = await client.statsDashboard();
848
+ break;
849
+
850
+ // ヘルス
851
+ case 'health':
852
+ result = await client.health();
853
+ break;
854
+
855
+ default:
856
+ throw new Error(`Unknown tool: ${name}`);
857
+ }
858
+
859
+ return {
860
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
861
+ };
862
+ } catch (error) {
863
+ return {
864
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
865
+ isError: true,
866
+ };
867
+ }
868
+ });
869
+
870
+ // ==========================================
871
+ // Start server (stdio or HTTP)
872
+ // ==========================================
873
+
874
+ async function main() {
875
+ if (TRANSPORT === 'http') {
876
+ // HTTP transport for VPS deployment
877
+ const { createServer } = await import('http');
878
+ const httpServer = createServer(async (req, res) => {
879
+ // CORS headers
880
+ res.setHeader('Access-Control-Allow-Origin', '*');
881
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
882
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
883
+
884
+ if (req.method === 'OPTIONS') {
885
+ res.writeHead(200);
886
+ res.end();
887
+ return;
888
+ }
889
+
890
+ // Health check
891
+ if (req.url === '/health') {
892
+ res.writeHead(200, { 'Content-Type': 'application/json' });
893
+ res.end(JSON.stringify({ status: 'ok', tools: TOOLS.length, version: '1.1.0' }));
894
+ return;
895
+ }
896
+
897
+ // SSE endpoint for MCP
898
+ if (req.url === '/sse' || req.url === '/mcp') {
899
+ res.writeHead(200, {
900
+ 'Content-Type': 'text/event-stream',
901
+ 'Cache-Control': 'no-cache',
902
+ 'Connection': 'keep-alive',
903
+ });
904
+
905
+ const toolsList = TOOLS.map(t => ({ name: t.name, description: t.description }));
906
+ res.write(`data: ${JSON.stringify({ type: 'tools', tools: toolsList })}\n\n`);
907
+ return;
908
+ }
909
+
910
+ // JSON-RPC endpoint
911
+ if (req.method === 'POST' && (req.url === '/' || req.url === '/rpc')) {
912
+ let body = '';
913
+ req.on('data', chunk => { body += chunk; });
914
+ req.on('end', async () => {
915
+ try {
916
+ const rpcRequest = JSON.parse(body);
917
+ let rpcResult;
918
+
919
+ if (rpcRequest.method === 'tools/list') {
920
+ rpcResult = { tools: TOOLS };
921
+ } else if (rpcRequest.method === 'tools/call') {
922
+ const handler = server.requestHandlers?.get?.(CallToolRequestSchema);
923
+ if (handler) {
924
+ rpcResult = await handler({ params: rpcRequest.params });
925
+ } else {
926
+ rpcResult = { error: 'Handler not found' };
927
+ }
928
+ } else {
929
+ rpcResult = { error: `Unknown method: ${rpcRequest.method}` };
930
+ }
931
+
932
+ res.writeHead(200, { 'Content-Type': 'application/json' });
933
+ res.end(JSON.stringify({
934
+ jsonrpc: '2.0',
935
+ id: rpcRequest.id,
936
+ result: rpcResult,
937
+ }));
938
+ } catch (e) {
939
+ res.writeHead(400, { 'Content-Type': 'application/json' });
940
+ res.end(JSON.stringify({ error: e.message }));
941
+ }
942
+ });
943
+ return;
944
+ }
945
+
946
+ // 404
947
+ res.writeHead(404, { 'Content-Type': 'application/json' });
948
+ res.end(JSON.stringify({ error: 'Not found' }));
949
+ });
950
+
951
+ httpServer.listen(PORT, () => {
952
+ console.error(`[macclaw-mcp] HTTP server listening on port ${PORT}`);
953
+ console.error(`[macclaw-mcp] Health: http://localhost:${PORT}/health`);
954
+ console.error(`[macclaw-mcp] Tools: ${TOOLS.length}`);
955
+ });
956
+ } else {
957
+ // stdio transport for local use
958
+ const transport = new StdioServerTransport();
959
+ await server.connect(transport);
960
+ console.error(`[macclaw-mcp] Connected via stdio (${TOOLS.length} tools)`);
961
+ }
962
+ }
963
+
964
+ main().catch(console.error);