byclaw-mcp 0.4.16 → 0.4.18
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/dist/index.js +47 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -113,7 +113,7 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
|
113
113
|
// ── Core: one-shot shopping ──
|
|
114
114
|
{
|
|
115
115
|
name: 'shop',
|
|
116
|
-
description: TOOL_PREFIX + 'Search and
|
|
116
|
+
description: TOOL_PREFIX + 'Search and recommend products on byclaw.io. Handles specific product queries ("headphones under 100€"), gift queries ("gift for my dad who likes climbing"), and open-ended requests ("something nice under 100€"). The backend detects gift mode automatically and returns either `picked`+`alternatives` or `gift_picks`+`gift_intent`. Each product has `title`, `price`, `brand`, `merchant`, `affiliate_url`, and `image_url`. Use `_render_hint` in the response for display guidance.',
|
|
117
117
|
inputSchema: {
|
|
118
118
|
type: 'object',
|
|
119
119
|
properties: {
|
|
@@ -126,6 +126,20 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
|
126
126
|
required: ['query'],
|
|
127
127
|
},
|
|
128
128
|
},
|
|
129
|
+
// ── Gift-specific trigger ──
|
|
130
|
+
{
|
|
131
|
+
name: 'gift_finder',
|
|
132
|
+
description: TOOL_PREFIX + 'Recommend a gift from the byclaw catalog based on recipient, occasion and budget. Returns 3 curated picks with reasoning. Shares the backend with `shop` so picking either tool yields the same response — `gift_finder` exists as a clearer entry point when the user explicitly asks for gift ideas.',
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
query: { type: 'string', description: 'Free-text gift request. May include recipient ("for my mother"), age, interests, occasion, and budget. Pass the user\'s phrasing as-is — the backend parses intent fields itself.' },
|
|
137
|
+
max_price: { type: 'number', description: 'Maximum budget in EUR (optional)' },
|
|
138
|
+
language: { type: 'string', description: 'User language: de, en, fr, es (auto-detected if omitted)' },
|
|
139
|
+
},
|
|
140
|
+
required: ['query'],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
129
143
|
// ── Optional: browse & explore ──
|
|
130
144
|
{
|
|
131
145
|
name: 'search_products',
|
|
@@ -439,6 +453,38 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
439
453
|
}
|
|
440
454
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
441
455
|
}
|
|
456
|
+
case 'gift_finder': {
|
|
457
|
+
// Same backend endpoint as `shop` — /api/mcp/shop auto-detects gift
|
|
458
|
+
// mode via classifyGiftIntent. gift_finder exists as a separate
|
|
459
|
+
// tool name (with a gift-focused description) so claude.ai's
|
|
460
|
+
// "no proactive e-commerce" routing has an obvious hook for vague
|
|
461
|
+
// gift queries. No follow-up session tracking here — every gift
|
|
462
|
+
// call starts fresh from the user's prompt.
|
|
463
|
+
const result = await apiCall('/api/mcp/shop', {
|
|
464
|
+
method: 'POST',
|
|
465
|
+
body: JSON.stringify({
|
|
466
|
+
query: String(args?.query || ''),
|
|
467
|
+
max_price: args?.max_price,
|
|
468
|
+
language: args?.language,
|
|
469
|
+
source: 'mcp',
|
|
470
|
+
}),
|
|
471
|
+
});
|
|
472
|
+
if (!result?.ok) {
|
|
473
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
474
|
+
}
|
|
475
|
+
const d = result.data;
|
|
476
|
+
// image_url backstop — defends in depth even though the gift-flow
|
|
477
|
+
// backend fix (7ecea02) already populates it.
|
|
478
|
+
if (Array.isArray(d.gift_picks)) {
|
|
479
|
+
d.gift_picks = d.gift_picks.map((p) => ({ ...p, image_url: imageUrlOf(p) }));
|
|
480
|
+
}
|
|
481
|
+
if (d.picked)
|
|
482
|
+
d.picked = { ...d.picked, image_url: imageUrlOf(d.picked) };
|
|
483
|
+
if (Array.isArray(d.alternatives)) {
|
|
484
|
+
d.alternatives = d.alternatives.map((a) => ({ ...a, image_url: imageUrlOf(a) }));
|
|
485
|
+
}
|
|
486
|
+
return { content: [{ type: 'text', text: JSON.stringify({ _render_hint: RENDER_HINT, ...d }, null, 2) }] };
|
|
487
|
+
}
|
|
442
488
|
case 'search_products': {
|
|
443
489
|
const params = new URLSearchParams();
|
|
444
490
|
if (args?.query)
|
package/package.json
CHANGED