@tenpo/mcp 0.2.2 → 0.2.4

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,48 @@ 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
+ const r = result;
386
+ // Error path
387
+ if (r && r.ok === false) {
388
+ return {
389
+ content: [
390
+ { type: "text", text: `Error: ${r.error ?? "Tool execution failed"}` },
391
+ { type: "text", text: JSON.stringify(result, null, 2) },
392
+ ],
393
+ isError: true,
394
+ };
395
+ }
396
+ // chat_message can live at:
397
+ // data.chat_message — MCP-direct tools (onboarding, can_help_with, etc.)
398
+ // data.details.chat_message — gateway tools wrapped via jsonResult()
399
+ const chatMessage = r?.data?.details?.chat_message ??
400
+ r?.data?.chat_message;
401
+ if (typeof chatMessage === "string" && chatMessage.length > 0) {
402
+ // Primary: the chat-ready message. Secondary: structured JSON for introspection.
403
+ return {
404
+ content: [
405
+ { type: "text", text: chatMessage },
406
+ { type: "text", text: JSON.stringify(result, null, 2) },
407
+ ],
408
+ };
409
+ }
410
+ // No chat_message — return JSON directly (no recursion!)
411
+ return {
412
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
413
+ };
414
+ }
373
415
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
374
416
  await ensureApiKey();
375
417
  const { name, arguments: args = {} } = request.params;
@@ -380,7 +422,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
380
422
  method: "POST",
381
423
  body: args,
382
424
  });
383
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
425
+ return formatToolResult(result);
384
426
  }
385
427
  case "tenpo_run_tool": {
386
428
  const a = args;
@@ -390,7 +432,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
390
432
  method: "POST",
391
433
  body: a.args ?? {},
392
434
  });
393
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
435
+ return formatToolResult(result);
394
436
  }
395
437
  case "tenpo_classify_intent": {
396
438
  const a = args;
@@ -449,7 +491,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
449
491
  if (!a.resource)
450
492
  throw new Error("resource name required");
451
493
  const result = await tenpoFetch(`/api/v1/resources/${encodeURIComponent(a.resource)}`);
452
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
494
+ return formatToolResult(result);
453
495
  }
454
496
  case "tenpo_get_prompt": {
455
497
  const a = args;
@@ -464,7 +506,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
464
506
  path += `?${params.toString()}`;
465
507
  }
466
508
  const result = await tenpoFetch(path);
467
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
509
+ return formatToolResult(result);
468
510
  }
469
511
  case "tenpo_list_prompts": {
470
512
  const a = args;
@@ -472,7 +514,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
472
514
  ? `/api/v1/prompts?q=${encodeURIComponent(a.q)}`
473
515
  : `/api/v1/prompts`;
474
516
  const result = await tenpoFetch(path);
475
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
517
+ return formatToolResult(result);
476
518
  }
477
519
  case "tenpo_connect_integration": {
478
520
  const a = args;
@@ -483,13 +525,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
483
525
  method: "POST",
484
526
  body: { integration_id: a.integration_id, credentials: a.credentials },
485
527
  });
486
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
528
+ return formatToolResult(result);
487
529
  }
488
530
  const result = await tenpoFetch("/api/v1/connect/start", {
489
531
  method: "POST",
490
532
  body: { integration_id: a.integration_id },
491
533
  });
492
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
534
+ return formatToolResult(result);
493
535
  }
494
536
  case "tenpo_remember": {
495
537
  const a = args;
@@ -500,7 +542,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
500
542
  method: "POST",
501
543
  body: { content, scope: a.scope ?? a.memory_type ?? "fact", mode: a.mode ?? "append" },
502
544
  });
503
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
545
+ return formatToolResult(result);
504
546
  }
505
547
  // tenpo_pattern_detect, tenpo_fetch_url, tenpo_write_md, tenpo_route_skills,
506
548
  // tenpo_overview cases DROPPED — host AI's native capabilities cover them.
@@ -515,7 +557,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
515
557
  throw new Error(`Unknown tool: ${name}. Use tenpo_route to discover available tools.`);
516
558
  }
517
559
  const result = await tenpoFetch(`/api/v1/tools/${encodeURIComponent(name)}`, { method: "POST", body: args });
518
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
560
+ return formatToolResult(result);
519
561
  }
520
562
  }
521
563
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenpo/mcp",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",