@sellhapi/mcp 0.1.3 → 0.1.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/dist/server.js CHANGED
@@ -12,6 +12,7 @@ const studio_js_1 = require("./tools/studio.js");
12
12
  const commerce_js_1 = require("./tools/commerce.js");
13
13
  const content_js_1 = require("./tools/content.js");
14
14
  const intelligence_js_1 = require("./tools/intelligence.js");
15
+ const analytics_js_1 = require("./tools/analytics.js");
15
16
  const index_js_1 = require("./resources/index.js");
16
17
  function createMcpServer() {
17
18
  const server = new mcp_js_1.McpServer({
@@ -22,6 +23,7 @@ function createMcpServer() {
22
23
  (0, commerce_js_1.registerCommerceTools)(server);
23
24
  (0, content_js_1.registerContentTools)(server);
24
25
  (0, intelligence_js_1.registerIntelligenceTools)(server);
26
+ (0, analytics_js_1.registerAnalyticsTools)(server);
25
27
  (0, index_js_1.registerResources)(server);
26
28
  return server;
27
29
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Analytics tools — read-only access to pre-computed capability metrics.
3
+ */
4
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ export declare function registerAnalyticsTools(server: McpServer): void;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * Analytics tools — read-only access to pre-computed capability metrics.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerAnalyticsTools = registerAnalyticsTools;
7
+ const v4_1 = require("zod/v4");
8
+ const client_js_1 = require("../client.js");
9
+ const util_js_1 = require("../util.js");
10
+ function registerAnalyticsTools(server) {
11
+ server.registerTool("get_analytics", {
12
+ title: "Get Analytics",
13
+ description: "Get pre-computed analytics for this vendor. Returns revenue, transaction counts, and other metrics per capability for a given period. Use this to answer questions like 'what's my revenue this month', 'which capability earns the most', or 'how many orders have I had'. Metrics are computed on a schedule — call this before making business recommendations.",
14
+ annotations: { readOnlyHint: true },
15
+ inputSchema: {
16
+ period: v4_1.z.enum(["7d", "30d", "90d"]).optional().default("30d").describe("Time period for metrics (default: 30d)"),
17
+ },
18
+ }, async ({ period }) => (0, util_js_1.safeCall)(async () => {
19
+ const data = await (0, client_js_1.get)(`/api/mcp/analytics?period=${period}`);
20
+ return JSON.stringify(data, null, 2);
21
+ }));
22
+ }
@@ -72,10 +72,30 @@ function registerCommerceTools(server) {
72
72
  const data = await (0, client_js_1.get)("/api/mcp/appointments/services");
73
73
  return JSON.stringify(data.services, null, 2);
74
74
  }));
75
+ // ── Categories ────────────────────────────────────────────────────────────
76
+ server.registerTool("list_categories", {
77
+ title: "List Categories",
78
+ description: "List all product categories for this vendor. Call this before create_product or update_product to get valid categoryId values. Returns id, name, description for each category.",
79
+ annotations: { readOnlyHint: true },
80
+ }, async () => (0, util_js_1.safeCall)(async () => {
81
+ const data = await (0, client_js_1.get)("/api/mcp/categories");
82
+ return JSON.stringify(data.categories, null, 2);
83
+ }));
84
+ server.registerTool("create_category", {
85
+ title: "Create Category",
86
+ description: "Create a product category. Returns the new category id — use it immediately in create_product or update_product. Returns a 409 conflict (with the existing id) if a category with the same name already exists — use that id instead of creating a duplicate.",
87
+ inputSchema: {
88
+ name: v4_1.z.string().describe("Category name e.g. 'Clothing', 'Baked Goods'"),
89
+ description: v4_1.z.string().optional().describe("Optional description"),
90
+ },
91
+ }, async (input) => (0, util_js_1.safeCall)(async () => {
92
+ const data = await (0, client_js_1.post)("/api/mcp/categories", input);
93
+ return JSON.stringify(data, null, 2);
94
+ }));
75
95
  // ── Write: Create product ─────────────────────────────────────────────────
76
96
  server.registerTool("create_product", {
77
97
  title: "Create Product",
78
- description: "Create a physical or digital product. Call list_capabilities first to confirm the relevant capability (physical_products or digital_products) is active. Prices must be in kobo (₦1 = 100 kobo). imageUrl is optional — use an external URL if the vendor has one, or omit it.",
98
+ description: "Create a physical or digital product. Call list_capabilities first to confirm the relevant capability (physical_products or digital_products) is active. Call list_categories first to get a valid categoryId — supplying an invalid categoryId returns a 400 error. Prices must be in kobo (₦1 = 100 kobo). imageUrl is optional.",
79
99
  inputSchema: {
80
100
  name: v4_1.z.string().describe("Product name"),
81
101
  description: v4_1.z.string().optional().describe("Product description"),
@@ -83,11 +103,27 @@ function registerCommerceTools(server) {
83
103
  type: v4_1.z.enum(["physical", "digital"]).optional().default("physical").describe("Product type — physical (default) or digital"),
84
104
  stock: v4_1.z.number().int().min(0).optional().describe("Initial stock quantity (0 = out of stock). Omit for untracked stock."),
85
105
  imageUrl: v4_1.z.string().optional().describe("External image URL for the product"),
106
+ categoryId: v4_1.z.string().uuid().optional().describe("Category ID from list_categories. Omit if no category applies."),
86
107
  },
87
108
  }, async (input) => (0, util_js_1.safeCall)(async () => {
88
109
  const data = await (0, client_js_1.post)("/api/mcp/products", input);
89
110
  return JSON.stringify(data, null, 2);
90
111
  }));
112
+ // ── Write: Update product ─────────────────────────────────────────────────
113
+ server.registerTool("update_product", {
114
+ title: "Update Product",
115
+ description: "Update an existing product's name, description, category, or image. Call list_products to get the productId, list_categories to get a valid categoryId. Only fields provided are updated — omitted fields stay unchanged. Pass categoryId: null to remove the category.",
116
+ inputSchema: {
117
+ productId: v4_1.z.string().uuid().describe("Product ID from list_products"),
118
+ name: v4_1.z.string().optional().describe("New product name"),
119
+ description: v4_1.z.string().optional().describe("New product description"),
120
+ categoryId: v4_1.z.string().uuid().nullable().optional().describe("Category ID from list_categories, or null to remove category"),
121
+ imageUrl: v4_1.z.string().nullable().optional().describe("New image URL, or null to clear"),
122
+ },
123
+ }, async ({ productId, ...rest }) => (0, util_js_1.safeCall)(async () => {
124
+ const data = await (0, client_js_1.patch)(`/api/mcp/products/${productId}`, rest);
125
+ return JSON.stringify(data, null, 2);
126
+ }));
91
127
  // ── Write: Create event ───────────────────────────────────────────────────
92
128
  server.registerTool("create_event", {
93
129
  title: "Create Event",
@@ -181,13 +217,13 @@ function registerCommerceTools(server) {
181
217
  // ── Write: Create form ───────────────────────────────────────────────────
182
218
  server.registerTool("create_form", {
183
219
  title: "Create Form",
184
- description: "Create a published storefront form. Returns the form ID — use it in useContentForm(formId) and useSubmitForm(formId) in storefront code. Call list_capabilities first to confirm content capability is active. Field types: text, email, phone, textarea, select, checkbox, date, number.",
220
+ description: "Create a published storefront form. Returns the form ID — use it in useContentForm(formId) and useSubmitForm(formId) in storefront code. Call list_capabilities first to confirm content capability is active. Field types: text, email, phone, textarea, select, radio, checkbox, date, number, file.",
185
221
  inputSchema: {
186
222
  title: v4_1.z.string().describe("Form name e.g. 'Contact Us'"),
187
223
  description: v4_1.z.string().optional(),
188
224
  fields: v4_1.z.array(v4_1.z.object({
189
225
  label: v4_1.z.string().describe("Field label shown to the user"),
190
- type: v4_1.z.enum(["text", "email", "phone", "textarea", "select", "checkbox", "date", "number"]).describe("Field input type"),
226
+ type: v4_1.z.enum(["text", "email", "phone", "textarea", "select", "radio", "checkbox", "date", "number", "file"]).describe("Field input type"),
191
227
  required: v4_1.z.boolean().optional().default(false),
192
228
  placeholder: v4_1.z.string().optional(),
193
229
  options: v4_1.z.array(v4_1.z.string()).optional().describe("Options for select fields"),
@@ -124,7 +124,7 @@ function registerStudioTools(server) {
124
124
  // ── Push code (zero credits) ───────────────────────────────────────────────
125
125
  server.registerTool("push_code", {
126
126
  title: "Push Code",
127
- description: "Push client-generated React code to a project. Runs BuildGuard validation + esbuild bundling and returns a preview URL. ZERO AI credit cost — you generate the code, SellHapi only builds it. CRITICAL RULES: (1) import ONLY from @sellhapi/studio-runtime and react — NO external packages; (2) every top-level JSX section must have data-region, data-label, data-scope attributes; (3) NO inline style={{}} — use Tailwind only; (4) NO localStorage/sessionStorage for state management — EXCEPTION: window.location.href IS required and allowed for payment redirects (all commerce hooks return { paymentLink } — redirect with window.location.href = result.paymentLink); (5) prices via priceKobo / 100 — never hardcode currency. Call get_build_rules or get_runtime_hooks for full reference.",
127
+ description: "Push client-generated React code to a project. Runs BuildGuard validation + esbuild bundling and returns a preview URL. ZERO AI credit cost — you generate the code, SellHapi only builds it. CRITICAL RULES: (1) import ONLY from @sellhapi/studio-runtime and react — NO external packages; (2) every top-level JSX section must have data-region, data-label, data-scope attributes; (3) use inline React style objects for ALL styling — Tailwind is NOT available; (4) NO localStorage/sessionStorage for state management — EXCEPTION: window.location.href IS required and allowed for payment redirects (all commerce hooks return { paymentLink } — redirect with window.location.href = result.paymentLink); (5) prices via priceKobo / 100 — never hardcode currency. Call get_build_rules or get_runtime_hooks for full reference.",
128
128
  inputSchema: {
129
129
  projectId: v4_1.z.string().describe("Studio project ID"),
130
130
  files: v4_1.z.record(v4_1.z.string(), v4_1.z.string()).describe("Map of filename → code content. Must include App.tsx as entry point."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellhapi/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "SellHapi MCP server — build and manage SellHapi storefronts from AI tools",
5
5
  "bin": {
6
6
  "sellhapi-mcp": "./dist/index.js"