@tenpo/mcp 0.2.2 → 0.2.3

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/README.md CHANGED
@@ -237,6 +237,9 @@ That's the first line. **First run auto-issues a free API key** (500 calls/month
237
237
 
238
238
  The second line goes into your AI host's config:
239
239
 
240
+ > ⚠️ **After editing the config, fully restart your AI host.**
241
+ > MCP servers are loaded at host startup — `claude mcp add`, saving Claude Desktop's config, or editing Cursor's MCP settings will register Tenpo, but the new tools won't appear in your running session until you **quit and relaunch the app** (Cmd+Q on Mac, fully close on Windows). This is a host-side limitation, not a Tenpo one.
242
+
240
243
  <details>
241
244
  <summary><b>Claude Desktop</b> · <code>claude_desktop_config.json</code></summary>
242
245
 
package/dist/index.js CHANGED
@@ -370,6 +370,43 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
370
370
  }));
371
371
  return { tools: [...TOOLS, ...dynamicEntries] };
372
372
  });
373
+ /**
374
+ * Format a backend tool result as MCP CallTool content.
375
+ *
376
+ * Many Tenpo tools return `data.chat_message` — a pre-formatted, chat-ready
377
+ * string the host AI should display verbatim. Surface that as the PRIMARY
378
+ * text block; append the structured JSON afterward so the host AI can still
379
+ * introspect (e.g. for follow-up tool calls or to read `instruction_for_host_ai`).
380
+ *
381
+ * This stops the old "double-encoded JSON inside JSON" UX where the host AI
382
+ * saw nothing but a stringified blob.
383
+ */
384
+ function formatToolResult(result) {
385
+ // Type guard: try to pull data.chat_message safely
386
+ const r = result;
387
+ // Error path: backend returned ok:false. Show it cleanly.
388
+ if (r && r.ok === false) {
389
+ return {
390
+ content: [
391
+ { type: "text", text: `Error: ${r.error ?? "Tool execution failed"}` },
392
+ { type: "text", text: JSON.stringify(result, null, 2) },
393
+ ],
394
+ isError: true,
395
+ };
396
+ }
397
+ const chatMessage = r?.data?.chat_message;
398
+ if (typeof chatMessage === "string" && chatMessage.length > 0) {
399
+ // Primary: the chat-ready message. Secondary: structured JSON for introspection.
400
+ return {
401
+ content: [
402
+ { type: "text", text: chatMessage },
403
+ { type: "text", text: JSON.stringify(result, null, 2) },
404
+ ],
405
+ };
406
+ }
407
+ // No chat_message — fall back to pretty-printed JSON.
408
+ return formatToolResult(result);
409
+ }
373
410
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
374
411
  await ensureApiKey();
375
412
  const { name, arguments: args = {} } = request.params;
@@ -380,7 +417,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
380
417
  method: "POST",
381
418
  body: args,
382
419
  });
383
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
420
+ return formatToolResult(result);
384
421
  }
385
422
  case "tenpo_run_tool": {
386
423
  const a = args;
@@ -390,7 +427,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
390
427
  method: "POST",
391
428
  body: a.args ?? {},
392
429
  });
393
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
430
+ return formatToolResult(result);
394
431
  }
395
432
  case "tenpo_classify_intent": {
396
433
  const a = args;
@@ -449,7 +486,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
449
486
  if (!a.resource)
450
487
  throw new Error("resource name required");
451
488
  const result = await tenpoFetch(`/api/v1/resources/${encodeURIComponent(a.resource)}`);
452
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
489
+ return formatToolResult(result);
453
490
  }
454
491
  case "tenpo_get_prompt": {
455
492
  const a = args;
@@ -464,7 +501,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
464
501
  path += `?${params.toString()}`;
465
502
  }
466
503
  const result = await tenpoFetch(path);
467
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
504
+ return formatToolResult(result);
468
505
  }
469
506
  case "tenpo_list_prompts": {
470
507
  const a = args;
@@ -472,7 +509,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
472
509
  ? `/api/v1/prompts?q=${encodeURIComponent(a.q)}`
473
510
  : `/api/v1/prompts`;
474
511
  const result = await tenpoFetch(path);
475
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
512
+ return formatToolResult(result);
476
513
  }
477
514
  case "tenpo_connect_integration": {
478
515
  const a = args;
@@ -483,13 +520,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
483
520
  method: "POST",
484
521
  body: { integration_id: a.integration_id, credentials: a.credentials },
485
522
  });
486
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
523
+ return formatToolResult(result);
487
524
  }
488
525
  const result = await tenpoFetch("/api/v1/connect/start", {
489
526
  method: "POST",
490
527
  body: { integration_id: a.integration_id },
491
528
  });
492
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
529
+ return formatToolResult(result);
493
530
  }
494
531
  case "tenpo_remember": {
495
532
  const a = args;
@@ -500,7 +537,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
500
537
  method: "POST",
501
538
  body: { content, scope: a.scope ?? a.memory_type ?? "fact", mode: a.mode ?? "append" },
502
539
  });
503
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
540
+ return formatToolResult(result);
504
541
  }
505
542
  // tenpo_pattern_detect, tenpo_fetch_url, tenpo_write_md, tenpo_route_skills,
506
543
  // tenpo_overview cases DROPPED — host AI's native capabilities cover them.
@@ -515,7 +552,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
515
552
  throw new Error(`Unknown tool: ${name}. Use tenpo_route to discover available tools.`);
516
553
  }
517
554
  const result = await tenpoFetch(`/api/v1/tools/${encodeURIComponent(name)}`, { method: "POST", body: args });
518
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
555
+ return formatToolResult(result);
519
556
  }
520
557
  }
521
558
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenpo/mcp",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Tenpo — the operator that runs alongside your store. Connects to 45+ commerce platforms (Shopify, Klaviyo, Meta Ads, GA4, Stripe…) and gives any AI host (Claude Desktop, Cursor, Claude Code, ChatGPT) deterministic answers about your sales, ads, email, inventory, suppliers, customers, finance, and competitors. Free tier, no card required.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tenpo.ai",