byclaw-mcp 0.4.14 → 0.4.16

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.
Files changed (2) hide show
  1. package/dist/index.js +52 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -53,16 +53,43 @@ function imageUrlOf(p) {
53
53
  const url = typeof p?.image_url === 'string' ? p.image_url.trim() : '';
54
54
  return url.startsWith('https://') || url.startsWith('http://') ? url : IMAGE_FALLBACK;
55
55
  }
56
+ // Hard cap on every backend request. Without it, if the backend is wedged
57
+ // (Anthropic 503 + SDK retry storm, ES lock, …) the MCP process would hang
58
+ // forever and the client (Claude Desktop) would only break out at its own
59
+ // 4 min cap with a "Server disconnected" disconnect — the very symptom
60
+ // Finding 094 described. 60 s is comfortably above normal backend latency
61
+ // (10-15 s for /api/mcp/shop) but below the client timeout, so the user
62
+ // gets a real error message instead of a stall.
63
+ const BACKEND_TIMEOUT_MS = 60000;
56
64
  async function apiCall(path, options = {}) {
57
65
  const headers = {
58
66
  'Authorization': `Bearer ${API_KEY}`,
59
67
  'Content-Type': 'application/json',
60
68
  };
61
- const res = await fetch(`${API_BASE}${path}`, {
62
- ...options,
63
- headers: { ...headers, ...options.headers },
64
- });
65
- return res.json();
69
+ try {
70
+ const res = await fetch(`${API_BASE}${path}`, {
71
+ ...options,
72
+ signal: AbortSignal.timeout(BACKEND_TIMEOUT_MS),
73
+ headers: { ...headers, ...options.headers },
74
+ });
75
+ return res.json();
76
+ }
77
+ catch (err) {
78
+ // AbortError fires when the timeout above kicks in. Translate to an
79
+ // ok=false body the existing call sites already know how to print.
80
+ if (err?.name === 'AbortError' || err?.name === 'TimeoutError') {
81
+ return {
82
+ ok: false,
83
+ error: `Backend timeout after ${BACKEND_TIMEOUT_MS / 1000}s (${path})`,
84
+ retryable: true,
85
+ };
86
+ }
87
+ return {
88
+ ok: false,
89
+ error: `Backend request failed: ${err?.message || 'unknown error'} (${path})`,
90
+ retryable: false,
91
+ };
92
+ }
66
93
  }
67
94
  const server = new index_js_1.Server({ name: 'byclaw', version: SERVER_VERSION }, { capabilities: { tools: {}, prompts: {} } });
68
95
  // Prompts
@@ -115,6 +142,11 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
115
142
  required: ['query'],
116
143
  },
117
144
  },
145
+ {
146
+ name: 'whoami',
147
+ description: TOOL_PREFIX + 'Return the active byclaw agent: its codename (e.g. #1949-H), personality (smartpicker / dealmaker / luxpicker / vibechecker / quickshooter), tagline, tone, and review style. Use when the user asks "which agent am I using", "what persona is searching for me", or "wer entscheidet hier". Cheap, no Anthropic call. Every shop / search_products / test_web_search response also surfaces this in the `agent` and `advisor_profile` fields.',
148
+ inputSchema: { type: 'object', properties: {} },
149
+ },
118
150
  {
119
151
  name: 'leave_review',
120
152
  description: TOOL_PREFIX + 'Write a product review. Only call if the user explicitly asks to leave a review. Only de and en allowed.',
@@ -295,6 +327,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
295
327
  return { content: [{ type: 'text', text: JSON.stringify({
296
328
  _render_hint: RENDER_HINT,
297
329
  type: 'gift_found',
330
+ agent: d.agent || null,
298
331
  advisor: advisorLabel,
299
332
  advisor_profile: d.advisor_profile || null,
300
333
  message: d.message || null,
@@ -319,6 +352,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
319
352
  if (d.status === 'clarification_needed') {
320
353
  return { content: [{ type: 'text', text: JSON.stringify({
321
354
  type: 'clarification_needed',
355
+ agent: d.agent || null,
322
356
  advisor: advisorLabel,
323
357
  advisor_profile: d.advisor_profile || null,
324
358
  message: d.message || '',
@@ -332,6 +366,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
332
366
  if (d.status === 'acknowledgement_legacy' || d.status === 'acknowledgement') {
333
367
  return { content: [{ type: 'text', text: JSON.stringify({
334
368
  type: d.status,
369
+ agent: d.agent || null,
335
370
  advisor: advisorLabel,
336
371
  advisor_profile: d.advisor_profile || null,
337
372
  message: d.message || '',
@@ -349,6 +384,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
349
384
  if (d.status === 'no_match') {
350
385
  content.push({ type: 'text', text: JSON.stringify({
351
386
  type: 'no_match',
387
+ agent: d.agent || null,
352
388
  advisor: advisorLabel,
353
389
  advisor_profile: d.advisor_profile || null,
354
390
  message: d.message || '',
@@ -362,6 +398,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
362
398
  content.push({ type: 'text', text: JSON.stringify({
363
399
  _render_hint: RENDER_HINT,
364
400
  type: d.status || 'product_found',
401
+ agent: d.agent || null,
365
402
  advisor: advisorLabel,
366
403
  advisor_profile: d.advisor_profile || null,
367
404
  picked: hasPick ? {
@@ -431,6 +468,13 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
431
468
  }
432
469
  return { content: [{ type: 'text', text: JSON.stringify({ _render_hint: RENDER_HINT, ...result }, null, 2) }] };
433
470
  }
471
+ case 'whoami': {
472
+ // Cheap lookup — backend `/api/agents/profile` resolves the agent
473
+ // from the API key and returns name + persona + style fields.
474
+ // No Anthropic call, no DB writes; ~50 ms typical.
475
+ const result = await apiCall('/api/agents/profile');
476
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
477
+ }
434
478
  case 'leave_review': {
435
479
  const lang = args?.language;
436
480
  if (lang !== 'de' && lang !== 'en') {
@@ -490,6 +534,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
490
534
  if (d.status === 'clarification_needed') {
491
535
  return { content: [{ type: 'text', text: JSON.stringify({
492
536
  type: 'clarification_needed',
537
+ agent: d.agent || null,
493
538
  advisor: d.advisor_profile?.name || d.agent?.personality || null,
494
539
  advisor_profile: d.advisor_profile || null,
495
540
  message: d.message || null,
@@ -504,6 +549,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
504
549
  return { content: [{ type: 'text', text: JSON.stringify({
505
550
  _render_hint: RENDER_HINT,
506
551
  type: 'gift_found',
552
+ agent: d.agent || null,
507
553
  advisor: d.advisor_profile?.name || d.agent?.personality || null,
508
554
  advisor_profile: d.advisor_profile || null,
509
555
  message: d.message || null,
@@ -520,6 +566,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
520
566
  return { content: [{ type: 'text', text: JSON.stringify({
521
567
  _render_hint: RENDER_HINT,
522
568
  type: d.status || 'no_match',
569
+ agent: d.agent || null,
523
570
  advisor: d.advisor_profile?.name || d.agent?.personality || null,
524
571
  advisor_profile: d.advisor_profile || null,
525
572
  message: d.message || null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byclaw-mcp",
3
- "version": "0.4.14",
3
+ "version": "0.4.16",
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": {