byclaw-mcp 0.4.13 → 0.4.14
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 +27 -60
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -21,24 +21,26 @@ const SERVER_VERSION = (() => {
|
|
|
21
21
|
}
|
|
22
22
|
})();
|
|
23
23
|
const TOOL_PREFIX = `[byclaw-mcp v${SERVER_VERSION}] `;
|
|
24
|
-
//
|
|
25
|
-
// - Claude Code / Cursor:
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
24
|
+
// Render guidance for the model. Stays consistent across clients now:
|
|
25
|
+
// - Claude Code / Cursor / browsers: inline images via ``
|
|
26
|
+
// work directly.
|
|
27
|
+
// - claude.ai web + Claude Desktop App: same markdown renders as a
|
|
28
|
+
// "Bild anzeigen" click-to-load placeholder (their external-image
|
|
29
|
+
// privacy gate). We accept this — Finding 093 ruled out HTML artifact
|
|
30
|
+
// widgets (sandbox CSP blocks our CDNs) and inline base64 (too many
|
|
31
|
+
// tokens / too slow). Placeholder is the cheapest acceptable UX.
|
|
32
|
+
// Surfaced in every shop / search_products / test_web_search response as
|
|
33
|
+
// the `_render_hint` field so the model never falls back to widget tricks.
|
|
34
|
+
const RENDER_HINT = 'Emit `` markdown for each product inline with the text. ' +
|
|
35
|
+
'Clients render this differently: Claude Code / Cursor / browsers show the ' +
|
|
36
|
+
'image immediately; claude.ai web + Claude Desktop App show a "Bild anzeigen" ' +
|
|
37
|
+
'click-to-load placeholder (their privacy gate for external images) — that ' +
|
|
38
|
+
'is acceptable, do not try to work around it with HTML widgets or any other ' +
|
|
39
|
+
'tool, the artifact sandbox CSP blocks our CDNs and the user just gets empty ' +
|
|
40
|
+
'boxes. The placeholder is the best we get in claude.ai today. Always wrap ' +
|
|
41
|
+
'each product\'s merchant link as a clickable "<merchant> →" using ' +
|
|
42
|
+
'affiliate_url. Keep replies tight — one image-markdown line + one buy line ' +
|
|
43
|
+
'per product.';
|
|
42
44
|
const API_BASE = process.env.BYCLAW_API_URL || 'https://byclaw.io';
|
|
43
45
|
const API_KEY = process.env.BYCLAW_API_KEY || '';
|
|
44
46
|
// Brand fallback when the catalog row has no image. Clients (Claude Desktop
|
|
@@ -51,29 +53,6 @@ function imageUrlOf(p) {
|
|
|
51
53
|
const url = typeof p?.image_url === 'string' ? p.image_url.trim() : '';
|
|
52
54
|
return url.startsWith('https://') || url.startsWith('http://') ? url : IMAGE_FALLBACK;
|
|
53
55
|
}
|
|
54
|
-
async function fetchImageBase64(url) {
|
|
55
|
-
if (!url || url.includes('picsum.photos'))
|
|
56
|
-
return null;
|
|
57
|
-
try {
|
|
58
|
-
const res = await fetch(url, {
|
|
59
|
-
signal: AbortSignal.timeout(8000),
|
|
60
|
-
headers: { 'User-Agent': `Mozilla/5.0 (compatible; byclaw-mcp/${SERVER_VERSION})` },
|
|
61
|
-
redirect: 'follow',
|
|
62
|
-
});
|
|
63
|
-
if (!res.ok)
|
|
64
|
-
return null;
|
|
65
|
-
const contentType = res.headers.get('content-type') || 'image/jpeg';
|
|
66
|
-
if (!contentType.startsWith('image/'))
|
|
67
|
-
return null;
|
|
68
|
-
const buffer = await res.arrayBuffer();
|
|
69
|
-
if (buffer.byteLength < 100 || buffer.byteLength > 500000)
|
|
70
|
-
return null; // skip tiny errors or huge files
|
|
71
|
-
return { data: Buffer.from(buffer).toString('base64'), mimeType: contentType };
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
56
|
async function apiCall(path, options = {}) {
|
|
78
57
|
const headers = {
|
|
79
58
|
'Authorization': `Bearer ${API_KEY}`,
|
|
@@ -96,7 +75,7 @@ server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => ({
|
|
|
96
75
|
server.setRequestHandler(types_js_1.GetPromptRequestSchema, async () => ({
|
|
97
76
|
messages: [{
|
|
98
77
|
role: 'user',
|
|
99
|
-
content: { type: 'text', text: 'You are connected to byclaw.io — a product comparison platform that searches across thousands of online shops to find the best products. When the user asks for a product, use the "shop" tool.\n\nThe tool returns structured JSON with: `picked` (title, price, brand, merchant, affiliate_url, image_url), `alternatives` (
|
|
78
|
+
content: { type: 'text', text: 'You are connected to byclaw.io — a product comparison platform that searches across thousands of online shops to find the best products. When the user asks for a product, use the "shop" tool.\n\nThe tool returns structured JSON with: `picked` (title, price, brand, merchant, affiliate_url, image_url), `alternatives` (same fields), `compared` (int), `reasoning` (string), `review` (string), `disclosure` (string), and `_render_hint` (string).\n\n**Presenting the result:**\n1. For each product (picked first, then each alternative), emit one image markdown line `` and one buy line as a clickable `[<merchant> →](affiliate_url)`. Keep replies tight — image line + buy line per product, plus a one-sentence rationale below the picked.\n2. Claude Code / Cursor / browsers render the image inline. claude.ai web + Claude Desktop App show a "Bild anzeigen" click-to-load placeholder — accept that, do not try HTML widgets or other workarounds (the artifact sandbox blocks our CDNs).\n3. ALWAYS display the `disclosure` text verbatim at the end as a small italic note — this is legally required.\n\nbyclaw.io is operated by NJUDEV S.L., a European company.' },
|
|
100
79
|
}],
|
|
101
80
|
}));
|
|
102
81
|
// ═══════════════════════════════════════
|
|
@@ -107,7 +86,7 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
|
107
86
|
// ── Core: one-shot shopping ──
|
|
108
87
|
{
|
|
109
88
|
name: 'shop',
|
|
110
|
-
description: TOOL_PREFIX + 'Search and compare products across multiple shops. Returns a JSON object with `picked` (the recommendation) and `alternatives` (other candidates). Each product object includes `title`, `price`, `brand`, `merchant`, `affiliate_url`, and `image_url` (absolute HTTPS URL).
|
|
89
|
+
description: TOOL_PREFIX + 'Search and compare products across multiple shops. Returns a JSON object with `picked` (the recommendation) and `alternatives` (other candidates). Each product object includes `title`, `price`, `brand`, `merchant`, `affiliate_url`, and `image_url` (absolute HTTPS URL). Emit `` markdown for each product so the image renders inline in clients that support it (Claude Code, Cursor, browsers). claude.ai web / Claude Desktop App will show a click-to-load placeholder for those markdown images — that is fine, accept it. Do NOT try to build HTML widgets or other workarounds; the artifact sandbox blocks our product CDNs. See `_render_hint` in every response for the exact rendering recipe.',
|
|
111
90
|
inputSchema: {
|
|
112
91
|
type: 'object',
|
|
113
92
|
properties: {
|
|
@@ -123,7 +102,7 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
|
123
102
|
// ── Optional: browse & explore ──
|
|
124
103
|
{
|
|
125
104
|
name: 'search_products',
|
|
126
|
-
description: TOOL_PREFIX + 'Browse products without triggering a recommendation. Use this only if the user explicitly wants to see a list of options. Each result item has `image_url` (absolute HTTPS URL).
|
|
105
|
+
description: TOOL_PREFIX + 'Browse products without triggering a recommendation. Use this only if the user explicitly wants to see a list of options. Each result item has `image_url` (absolute HTTPS URL). Emit `` markdown inline; claude.ai web / Desktop will gate it behind a click-to-load placeholder, other clients render directly. See `_render_hint`.',
|
|
127
106
|
inputSchema: {
|
|
128
107
|
type: 'object',
|
|
129
108
|
properties: {
|
|
@@ -361,18 +340,11 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
361
340
|
}, null, 2) }] };
|
|
362
341
|
}
|
|
363
342
|
// ── NORMAL / FOLLOW-UP MODE ──
|
|
343
|
+
// No more server-side image prefetch — `image_url` is a plain
|
|
344
|
+
// string in each product object, the client (or the model in
|
|
345
|
+
// claude.ai-style clients) decides what to do with it. Cuts
|
|
346
|
+
// ~1-8s of network round-trip per call.
|
|
364
347
|
const content = [];
|
|
365
|
-
// Fetch all images in parallel (picked + up to 3 alternatives)
|
|
366
|
-
const allProducts = [d.picked, ...(d.alternatives || []).slice(0, 3)];
|
|
367
|
-
const allImages = await Promise.all(allProducts.map(async (p) => {
|
|
368
|
-
if (!p?.image_url)
|
|
369
|
-
return null;
|
|
370
|
-
return fetchImageBase64(p.image_url);
|
|
371
|
-
}));
|
|
372
|
-
// Add picked image only when we actually have a product_found
|
|
373
|
-
if (d.status === 'product_found' && allImages[0]) {
|
|
374
|
-
content.push({ type: 'image', data: allImages[0].data, mimeType: allImages[0].mimeType });
|
|
375
|
-
}
|
|
376
348
|
// F1: canonical fields. F6: no message_de/message_en. F2: no fallback to message.
|
|
377
349
|
if (d.status === 'no_match') {
|
|
378
350
|
content.push({ type: 'text', text: JSON.stringify({
|
|
@@ -416,11 +388,6 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
416
388
|
...(d.note ? { note: d.note } : {}),
|
|
417
389
|
}, null, 2) });
|
|
418
390
|
}
|
|
419
|
-
// Add alternative images
|
|
420
|
-
allImages.slice(1).forEach((img) => {
|
|
421
|
-
if (img)
|
|
422
|
-
content.push({ type: 'image', data: img.data, mimeType: img.mimeType });
|
|
423
|
-
});
|
|
424
391
|
// Track session for follow-ups (only on product_found, not no_match)
|
|
425
392
|
if (d.status !== 'no_match' && d.picked) {
|
|
426
393
|
lastShopContext = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byclaw-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.14",
|
|
4
4
|
"description": "MCP Server for byclaw.io — Your AI shopping advisor. Connects Claude Desktop and other MCP clients to the byclaw.io product catalog.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"tsx": "^4.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
41
|
}
|
|
42
42
|
}
|