karrito-mcp 1.1.0 → 2.0.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP server for [Karrito](https://karrito.shop) — the digital catalog builder for WhatsApp sellers in LATAM.
4
4
 
5
- Connect your AI assistant (Claude, Cursor, Windsurf) to Karrito and manage your catalog, search public stores, and get platform information without leaving your editor.
5
+ Connect your AI assistant (Claude, Cursor, Windsurf) to Karrito and manage your entire store — products, orders, discounts, reviews, shipping, analytics without leaving your editor.
6
6
 
7
7
  ## Installation
8
8
 
@@ -69,15 +69,87 @@ Static data about Karrito — no authentication required.
69
69
  | Competitors | `karrito://competitors` | 19 competitors compared with Karrito advantages |
70
70
  | Currencies | `karrito://currencies` | 6 supported LATAM currencies |
71
71
 
72
- ## Tools (5)
72
+ ## Tools (30)
73
73
 
74
- | Tool | Auth | Description |
75
- |------|------|-------------|
76
- | `search_catalogs` | No | Search public catalogs by keyword |
77
- | `get_niche_info` | No | Get info about a specific niche |
78
- | `list_my_products` | Yes | List products in your catalog |
79
- | `create_product` | Yes | Create a new product |
80
- | `list_my_orders` | Yes | List orders from your store |
74
+ ### Public (no auth required)
75
+
76
+ | Tool | Description |
77
+ |------|-------------|
78
+ | `search_catalogs` | Search public catalogs by keyword |
79
+ | `get_niche_info` | Get info about a specific niche |
80
+
81
+ ### Products (auth required)
82
+
83
+ | Tool | Description |
84
+ |------|-------------|
85
+ | `list_my_products` | List products in your catalog |
86
+ | `create_product` | Create a new product |
87
+ | `update_product` | Update an existing product |
88
+ | `delete_product` | Delete a product (soft delete) |
89
+
90
+ ### Categories (auth required)
91
+
92
+ | Tool | Description |
93
+ |------|-------------|
94
+ | `list_categories` | List categories in your store |
95
+ | `create_category` | Create a new category |
96
+ | `update_category` | Update an existing category |
97
+ | `delete_category` | Delete a category |
98
+
99
+ ### Orders (auth required)
100
+
101
+ | Tool | Description |
102
+ |------|-------------|
103
+ | `list_my_orders` | List orders with optional status filter |
104
+ | `get_order` | Get detailed order info |
105
+ | `update_order_status` | Change order status (confirm, ship, deliver, cancel) |
106
+
107
+ ### Store (auth required)
108
+
109
+ | Tool | Description |
110
+ |------|-------------|
111
+ | `get_my_store` | Get store info (name, slug, currency, stats) |
112
+ | `update_store` | Update store settings (name, WhatsApp, currency, etc.) |
113
+ | `toggle_publish` | Quickly publish or unpublish your store |
114
+
115
+ ### Discounts (auth required)
116
+
117
+ | Tool | Description |
118
+ |------|-------------|
119
+ | `list_discounts` | List discount codes |
120
+ | `create_discount` | Create a discount code (percentage or fixed) |
121
+ | `update_discount` | Update an existing discount |
122
+ | `delete_discount` | Delete a discount code |
123
+
124
+ ### Reviews (auth required)
125
+
126
+ | Tool | Description |
127
+ |------|-------------|
128
+ | `list_reviews` | List reviews with optional status filter |
129
+ | `moderate_review` | Approve or reject a review |
130
+ | `delete_review` | Permanently delete a review |
131
+
132
+ ### Customers (auth required)
133
+
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `list_customers` | List customers who have ordered |
137
+ | `get_customer` | Get customer detail with order history |
138
+
139
+ ### Analytics (auth required)
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
143
+ | `get_analytics` | Store stats: orders, revenue, products, customers, AOV, popular products |
144
+
145
+ ### Shipping (auth required)
146
+
147
+ | Tool | Description |
148
+ |------|-------------|
149
+ | `list_shipping_options` | List shipping options |
150
+ | `create_shipping_option` | Create a shipping option (delivery, pickup, express) |
151
+ | `update_shipping_option` | Update a shipping option |
152
+ | `delete_shipping_option` | Delete a shipping option |
81
153
 
82
154
  ## Usage examples
83
155
 
@@ -99,6 +171,30 @@ The assistant will use `get_niche_info` to search food-related niches.
99
171
 
100
172
  The assistant will use `list_my_products` and `create_product` (requires `KARRITO_API_KEY`).
101
173
 
174
+ ### Manage orders
175
+
176
+ > "Show me all pending orders and confirm the first one"
177
+
178
+ The assistant will use `list_my_orders` with status filter and `update_order_status`.
179
+
180
+ ### Create discounts
181
+
182
+ > "Create a 20% discount code VERANO20 that expires on December 31"
183
+
184
+ The assistant will use `create_discount` with the provided parameters.
185
+
186
+ ### Moderate reviews
187
+
188
+ > "Show me pending reviews and approve the ones with 4+ stars"
189
+
190
+ The assistant will use `list_reviews` with status "pending" and `moderate_review`.
191
+
192
+ ### Get analytics
193
+
194
+ > "How is my store performing? Show me the stats"
195
+
196
+ The assistant will use `get_analytics` to retrieve store metrics.
197
+
102
198
  ### Compare platforms
103
199
 
104
200
  > "How does Karrito compare to Shopify and TiendaNube?"
package/dist/index.js CHANGED
@@ -14,9 +14,14 @@ import { register as registerProductTools } from './tools/products.js';
14
14
  import { register as registerOrderTools } from './tools/orders.js';
15
15
  import { register as registerCategoryTools } from './tools/categories.js';
16
16
  import { register as registerStoreTools } from './tools/store.js';
17
+ import { register as registerDiscountTools } from './tools/discounts.js';
18
+ import { register as registerReviewTools } from './tools/reviews.js';
19
+ import { register as registerCustomerTools } from './tools/customers.js';
20
+ import { register as registerStatsTools } from './tools/stats.js';
21
+ import { register as registerShippingTools } from './tools/shipping.js';
17
22
  const server = new McpServer({
18
23
  name: 'karrito',
19
- version: '1.1.0',
24
+ version: '2.0.0',
20
25
  });
21
26
  const client = new KarritoApiClient();
22
27
  // Register static resources (public, no auth needed)
@@ -31,13 +36,38 @@ registerProductTools(server, client);
31
36
  registerOrderTools(server, client);
32
37
  registerCategoryTools(server, client);
33
38
  registerStoreTools(server, client);
39
+ registerDiscountTools(server, client);
40
+ registerReviewTools(server, client);
41
+ registerCustomerTools(server, client);
42
+ registerStatsTools(server, client);
43
+ registerShippingTools(server, client);
34
44
  // Connect via stdio
35
45
  async function main() {
46
+ const hasKey = !!process.env.KARRITO_API_KEY;
47
+ const maskedKey = hasKey
48
+ ? `${process.env.KARRITO_API_KEY.slice(0, 8)}${'*'.repeat(12)}${process.env.KARRITO_API_KEY.slice(-4)}`
49
+ : 'not set';
50
+ console.error('');
51
+ console.error(' ╔═══════════════════════════════════════════════╗');
52
+ console.error(' ║ ║');
53
+ console.error(' ║ 🛒 Karrito MCP Server v2.0.0 ║');
54
+ console.error(' ║ ║');
55
+ console.error(' ║ 30 tools • 5 resources ║');
56
+ console.error(' ║ Manage your entire store from AI ║');
57
+ console.error(' ║ ║');
58
+ console.error(' ╠═══════════════════════════════════════════════╣');
59
+ console.error(` ║ API Key: ${maskedKey.padEnd(34)}║`);
60
+ console.error(` ║ Status: ${hasKey ? '● Connected' : '○ Public only (set KARRITO_API_KEY)'}${hasKey ? ' ' : ''}║`);
61
+ console.error(' ║ ║');
62
+ console.error(' ║ Docs: docs.karrito.shop ║');
63
+ console.error(' ║ npm: npmjs.com/package/karrito-mcp ║');
64
+ console.error(' ║ ║');
65
+ console.error(' ╚═══════════════════════════════════════════════╝');
66
+ console.error('');
36
67
  const transport = new StdioServerTransport();
37
68
  await server.connect(transport);
38
- console.error('Karrito MCP Server running on stdio');
39
69
  }
40
70
  main().catch((error) => {
41
- console.error('Fatal error:', error);
71
+ console.error('Fatal error:', error);
42
72
  process.exit(1);
43
73
  });
@@ -16,12 +16,30 @@ export declare class KarritoApiClient {
16
16
  listProducts(limit?: number, offset?: number): Promise<unknown>;
17
17
  getProduct(id: string): Promise<unknown>;
18
18
  createProduct(data: Record<string, unknown>): Promise<unknown>;
19
- listCategories(limit?: number, offset?: number): Promise<unknown>;
20
- listOrders(limit?: number, offset?: number, status?: string): Promise<unknown>;
21
- getOrder(id: string): Promise<unknown>;
22
19
  updateProduct(id: string, data: Record<string, unknown>): Promise<unknown>;
23
20
  deleteProduct(id: string): Promise<unknown>;
21
+ listCategories(limit?: number, offset?: number): Promise<unknown>;
24
22
  createCategory(data: Record<string, unknown>): Promise<unknown>;
23
+ updateCategory(id: string, data: Record<string, unknown>): Promise<unknown>;
24
+ deleteCategory(id: string): Promise<unknown>;
25
+ listOrders(limit?: number, offset?: number, status?: string): Promise<unknown>;
26
+ getOrder(id: string): Promise<unknown>;
27
+ updateOrderStatus(id: string, status: string): Promise<unknown>;
25
28
  getStore(): Promise<unknown>;
29
+ updateStore(data: Record<string, unknown>): Promise<unknown>;
30
+ listDiscounts(limit?: number, offset?: number): Promise<unknown>;
31
+ createDiscount(data: Record<string, unknown>): Promise<unknown>;
32
+ updateDiscount(id: string, data: Record<string, unknown>): Promise<unknown>;
33
+ deleteDiscount(id: string): Promise<unknown>;
34
+ listReviews(limit?: number, offset?: number, status?: string): Promise<unknown>;
35
+ moderateReview(id: string, status: string): Promise<unknown>;
36
+ deleteReview(id: string): Promise<unknown>;
37
+ listCustomers(limit?: number, offset?: number): Promise<unknown>;
38
+ getCustomer(id: string): Promise<unknown>;
39
+ getStats(): Promise<unknown>;
40
+ listShipping(): Promise<unknown>;
41
+ createShipping(data: Record<string, unknown>): Promise<unknown>;
42
+ updateShipping(id: string, data: Record<string, unknown>): Promise<unknown>;
43
+ deleteShipping(id: string): Promise<unknown>;
26
44
  }
27
45
  export {};
@@ -14,7 +14,7 @@ export class KarritoApiClient {
14
14
  async request(path, options = {}) {
15
15
  const headers = {
16
16
  'Content-Type': 'application/json',
17
- 'User-Agent': 'karrito-mcp/1.1.0',
17
+ 'User-Agent': 'karrito-mcp/2.0.0',
18
18
  };
19
19
  if (this.apiKey) {
20
20
  headers['Authorization'] = `Bearer ${this.apiKey}`;
@@ -47,7 +47,7 @@ export class KarritoApiClient {
47
47
  async checkSlug(slug) {
48
48
  return this.request(`/api/check-slug?slug=${encodeURIComponent(slug)}`);
49
49
  }
50
- // --- Authenticated endpoints ---
50
+ // --- Products ---
51
51
  async listProducts(limit = 50, offset = 0) {
52
52
  return this.request(`/api/v1/products?limit=${limit}&offset=${offset}`);
53
53
  }
@@ -60,9 +60,39 @@ export class KarritoApiClient {
60
60
  body: JSON.stringify(data),
61
61
  });
62
62
  }
63
+ async updateProduct(id, data) {
64
+ return this.request(`/api/v1/products/${id}`, {
65
+ method: 'PUT',
66
+ body: JSON.stringify(data),
67
+ });
68
+ }
69
+ async deleteProduct(id) {
70
+ return this.request(`/api/v1/products/${id}`, {
71
+ method: 'DELETE',
72
+ });
73
+ }
74
+ // --- Categories ---
63
75
  async listCategories(limit = 50, offset = 0) {
64
76
  return this.request(`/api/v1/categories?limit=${limit}&offset=${offset}`);
65
77
  }
78
+ async createCategory(data) {
79
+ return this.request('/api/v1/categories', {
80
+ method: 'POST',
81
+ body: JSON.stringify(data),
82
+ });
83
+ }
84
+ async updateCategory(id, data) {
85
+ return this.request(`/api/v1/categories/${id}`, {
86
+ method: 'PUT',
87
+ body: JSON.stringify(data),
88
+ });
89
+ }
90
+ async deleteCategory(id) {
91
+ return this.request(`/api/v1/categories/${id}`, {
92
+ method: 'DELETE',
93
+ });
94
+ }
95
+ // --- Orders ---
66
96
  async listOrders(limit = 50, offset = 0, status) {
67
97
  const statusParam = status ? `&status=${status}` : '';
68
98
  return this.request(`/api/v1/orders?limit=${limit}&offset=${offset}${statusParam}`);
@@ -70,24 +100,89 @@ export class KarritoApiClient {
70
100
  async getOrder(id) {
71
101
  return this.request(`/api/v1/orders/${id}`);
72
102
  }
73
- async updateProduct(id, data) {
74
- return this.request(`/api/v1/products/${id}`, {
103
+ async updateOrderStatus(id, status) {
104
+ return this.request(`/api/v1/orders/${id}/status`, {
105
+ method: 'PUT',
106
+ body: JSON.stringify({ status }),
107
+ });
108
+ }
109
+ // --- Store ---
110
+ async getStore() {
111
+ return this.request('/api/v1/store');
112
+ }
113
+ async updateStore(data) {
114
+ return this.request('/api/v1/store', {
75
115
  method: 'PUT',
76
116
  body: JSON.stringify(data),
77
117
  });
78
118
  }
79
- async deleteProduct(id) {
80
- return this.request(`/api/v1/products/${id}`, {
119
+ // --- Discounts ---
120
+ async listDiscounts(limit = 50, offset = 0) {
121
+ return this.request(`/api/v1/discounts?limit=${limit}&offset=${offset}`);
122
+ }
123
+ async createDiscount(data) {
124
+ return this.request('/api/v1/discounts', {
125
+ method: 'POST',
126
+ body: JSON.stringify(data),
127
+ });
128
+ }
129
+ async updateDiscount(id, data) {
130
+ return this.request(`/api/v1/discounts/${id}`, {
131
+ method: 'PUT',
132
+ body: JSON.stringify(data),
133
+ });
134
+ }
135
+ async deleteDiscount(id) {
136
+ return this.request(`/api/v1/discounts/${id}`, {
81
137
  method: 'DELETE',
82
138
  });
83
139
  }
84
- async createCategory(data) {
85
- return this.request('/api/v1/categories', {
140
+ // --- Reviews ---
141
+ async listReviews(limit = 50, offset = 0, status) {
142
+ const statusParam = status ? `&status=${status}` : '';
143
+ return this.request(`/api/v1/reviews?limit=${limit}&offset=${offset}${statusParam}`);
144
+ }
145
+ async moderateReview(id, status) {
146
+ return this.request(`/api/v1/reviews/${id}/moderate`, {
147
+ method: 'PUT',
148
+ body: JSON.stringify({ status }),
149
+ });
150
+ }
151
+ async deleteReview(id) {
152
+ return this.request(`/api/v1/reviews/${id}`, {
153
+ method: 'DELETE',
154
+ });
155
+ }
156
+ // --- Customers ---
157
+ async listCustomers(limit = 50, offset = 0) {
158
+ return this.request(`/api/v1/customers?limit=${limit}&offset=${offset}`);
159
+ }
160
+ async getCustomer(id) {
161
+ return this.request(`/api/v1/customers/${id}`);
162
+ }
163
+ // --- Stats ---
164
+ async getStats() {
165
+ return this.request('/api/v1/stats');
166
+ }
167
+ // --- Shipping ---
168
+ async listShipping() {
169
+ return this.request('/api/v1/shipping');
170
+ }
171
+ async createShipping(data) {
172
+ return this.request('/api/v1/shipping', {
86
173
  method: 'POST',
87
174
  body: JSON.stringify(data),
88
175
  });
89
176
  }
90
- async getStore() {
91
- return this.request('/api/v1/store');
177
+ async updateShipping(id, data) {
178
+ return this.request(`/api/v1/shipping/${id}`, {
179
+ method: 'PUT',
180
+ body: JSON.stringify(data),
181
+ });
182
+ }
183
+ async deleteShipping(id) {
184
+ return this.request(`/api/v1/shipping/${id}`, {
185
+ method: 'DELETE',
186
+ });
92
187
  }
93
188
  }
@@ -73,4 +73,86 @@ export function register(server, client) {
73
73
  };
74
74
  }
75
75
  });
76
+ // --- update_category (auth required) ---
77
+ server.tool('update_category', 'Update an existing category in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
78
+ id: z.string().describe('Category ID to update'),
79
+ name: z.string().min(1).max(100).optional().describe('New category name'),
80
+ position: z.number().int().min(0).optional().describe('New sort order (lower = first)'),
81
+ isActive: z.boolean().optional().describe('Whether the category is active'),
82
+ }, async ({ id, ...updates }) => {
83
+ if (!client.hasApiKey) {
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
89
+ },
90
+ ],
91
+ isError: true,
92
+ };
93
+ }
94
+ try {
95
+ const updateData = {};
96
+ for (const [key, value] of Object.entries(updates)) {
97
+ if (value !== undefined)
98
+ updateData[key] = value;
99
+ }
100
+ if (Object.keys(updateData).length === 0) {
101
+ return {
102
+ content: [{ type: 'text', text: 'No fields to update. Provide at least one field (name, position, isActive).' }],
103
+ isError: true,
104
+ };
105
+ }
106
+ const data = await client.updateCategory(id, updateData);
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: JSON.stringify(data, null, 2),
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ catch (error) {
117
+ const message = error instanceof Error ? error.message : 'Unknown error';
118
+ return {
119
+ content: [{ type: 'text', text: `Error updating category: ${message}` }],
120
+ isError: true,
121
+ };
122
+ }
123
+ });
124
+ // --- delete_category (auth required) ---
125
+ server.tool('delete_category', 'Delete a category from your Karrito store. Products in this category will become uncategorized. Requires KARRITO_API_KEY environment variable.', {
126
+ id: z.string().describe('Category ID to delete'),
127
+ }, async ({ id }) => {
128
+ if (!client.hasApiKey) {
129
+ return {
130
+ content: [
131
+ {
132
+ type: 'text',
133
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
134
+ },
135
+ ],
136
+ isError: true,
137
+ };
138
+ }
139
+ try {
140
+ const data = await client.deleteCategory(id);
141
+ return {
142
+ content: [
143
+ {
144
+ type: 'text',
145
+ text: JSON.stringify(data, null, 2),
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ catch (error) {
151
+ const message = error instanceof Error ? error.message : 'Unknown error';
152
+ return {
153
+ content: [{ type: 'text', text: `Error deleting category: ${message}` }],
154
+ isError: true,
155
+ };
156
+ }
157
+ });
76
158
  }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { KarritoApiClient } from '../lib/api-client.js';
3
+ export declare function register(server: McpServer, client: KarritoApiClient): void;
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ function authError() {
3
+ return {
4
+ content: [
5
+ {
6
+ type: 'text',
7
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
8
+ },
9
+ ],
10
+ isError: true,
11
+ };
12
+ }
13
+ export function register(server, client) {
14
+ // --- list_customers (auth required) ---
15
+ server.tool('list_customers', 'List customers who have placed orders in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
16
+ limit: z.number().min(1).max(100).default(50).describe('Max customers to return (1-100)'),
17
+ offset: z.number().min(0).default(0).describe('Pagination offset'),
18
+ }, async ({ limit, offset }) => {
19
+ if (!client.hasApiKey)
20
+ return authError();
21
+ try {
22
+ const data = await client.listCustomers(limit, offset);
23
+ return {
24
+ content: [
25
+ {
26
+ type: 'text',
27
+ text: JSON.stringify(data, null, 2),
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ catch (error) {
33
+ const message = error instanceof Error ? error.message : 'Unknown error';
34
+ return {
35
+ content: [{ type: 'text', text: `Error listing customers: ${message}` }],
36
+ isError: true,
37
+ };
38
+ }
39
+ });
40
+ // --- get_customer (auth required) ---
41
+ server.tool('get_customer', 'Get detailed information about a specific customer including their order history. Requires KARRITO_API_KEY environment variable.', {
42
+ id: z.string().describe('Customer ID to retrieve'),
43
+ }, async ({ id }) => {
44
+ if (!client.hasApiKey)
45
+ return authError();
46
+ try {
47
+ const data = await client.getCustomer(id);
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text',
52
+ text: JSON.stringify(data, null, 2),
53
+ },
54
+ ],
55
+ };
56
+ }
57
+ catch (error) {
58
+ const message = error instanceof Error ? error.message : 'Unknown error';
59
+ return {
60
+ content: [{ type: 'text', text: `Error getting customer: ${message}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { KarritoApiClient } from '../lib/api-client.js';
3
+ export declare function register(server: McpServer, client: KarritoApiClient): void;
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+ function authError() {
3
+ return {
4
+ content: [
5
+ {
6
+ type: 'text',
7
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
8
+ },
9
+ ],
10
+ isError: true,
11
+ };
12
+ }
13
+ export function register(server, client) {
14
+ // --- list_discounts (auth required) ---
15
+ server.tool('list_discounts', 'List discount codes in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
16
+ limit: z.number().min(1).max(100).default(50).describe('Max discounts to return (1-100)'),
17
+ offset: z.number().min(0).default(0).describe('Pagination offset'),
18
+ }, async ({ limit, offset }) => {
19
+ if (!client.hasApiKey)
20
+ return authError();
21
+ try {
22
+ const data = await client.listDiscounts(limit, offset);
23
+ return {
24
+ content: [
25
+ {
26
+ type: 'text',
27
+ text: JSON.stringify(data, null, 2),
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ catch (error) {
33
+ const message = error instanceof Error ? error.message : 'Unknown error';
34
+ return {
35
+ content: [{ type: 'text', text: `Error listing discounts: ${message}` }],
36
+ isError: true,
37
+ };
38
+ }
39
+ });
40
+ // --- create_discount (auth required) ---
41
+ server.tool('create_discount', 'Create a new discount code for your Karrito store. Requires KARRITO_API_KEY environment variable.', {
42
+ code: z.string().min(1).max(50).describe('Discount code (e.g., VERANO20)'),
43
+ type: z.enum(['percentage', 'fixed']).describe('Discount type: percentage (e.g., 20%) or fixed amount (e.g., $5 off)'),
44
+ value: z.number().min(0).describe('Discount value (percentage 0-100 or fixed amount in store currency)'),
45
+ minOrderAmount: z.number().min(0).optional().describe('Minimum order amount required to use this discount'),
46
+ maxUses: z.number().int().min(1).optional().describe('Maximum number of times this discount can be used'),
47
+ expiresAt: z.string().optional().describe('Expiration date in ISO 8601 format (e.g., 2026-12-31T23:59:59Z)'),
48
+ }, async ({ code, type, value, minOrderAmount, maxUses, expiresAt }) => {
49
+ if (!client.hasApiKey)
50
+ return authError();
51
+ try {
52
+ const discountData = { code, type, value };
53
+ if (minOrderAmount !== undefined)
54
+ discountData.minOrderAmount = minOrderAmount;
55
+ if (maxUses !== undefined)
56
+ discountData.maxUses = maxUses;
57
+ if (expiresAt !== undefined)
58
+ discountData.expiresAt = expiresAt;
59
+ const data = await client.createDiscount(discountData);
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: JSON.stringify(data, null, 2),
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ catch (error) {
70
+ const message = error instanceof Error ? error.message : 'Unknown error';
71
+ return {
72
+ content: [{ type: 'text', text: `Error creating discount: ${message}` }],
73
+ isError: true,
74
+ };
75
+ }
76
+ });
77
+ // --- update_discount (auth required) ---
78
+ server.tool('update_discount', 'Update an existing discount code in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
79
+ id: z.string().describe('Discount ID to update'),
80
+ code: z.string().min(1).max(50).optional().describe('New discount code'),
81
+ type: z.enum(['percentage', 'fixed']).optional().describe('New discount type'),
82
+ value: z.number().min(0).optional().describe('New discount value'),
83
+ minOrderAmount: z.number().min(0).optional().describe('New minimum order amount'),
84
+ maxUses: z.number().int().min(1).optional().describe('New maximum uses'),
85
+ expiresAt: z.string().optional().describe('New expiration date in ISO 8601 format'),
86
+ }, async ({ id, ...updates }) => {
87
+ if (!client.hasApiKey)
88
+ return authError();
89
+ try {
90
+ const updateData = {};
91
+ for (const [key, value] of Object.entries(updates)) {
92
+ if (value !== undefined)
93
+ updateData[key] = value;
94
+ }
95
+ if (Object.keys(updateData).length === 0) {
96
+ return {
97
+ content: [{ type: 'text', text: 'No fields to update. Provide at least one field (code, type, value, minOrderAmount, maxUses, expiresAt).' }],
98
+ isError: true,
99
+ };
100
+ }
101
+ const data = await client.updateDiscount(id, updateData);
102
+ return {
103
+ content: [
104
+ {
105
+ type: 'text',
106
+ text: JSON.stringify(data, null, 2),
107
+ },
108
+ ],
109
+ };
110
+ }
111
+ catch (error) {
112
+ const message = error instanceof Error ? error.message : 'Unknown error';
113
+ return {
114
+ content: [{ type: 'text', text: `Error updating discount: ${message}` }],
115
+ isError: true,
116
+ };
117
+ }
118
+ });
119
+ // --- delete_discount (auth required) ---
120
+ server.tool('delete_discount', 'Delete a discount code from your Karrito store. Requires KARRITO_API_KEY environment variable.', {
121
+ id: z.string().describe('Discount ID to delete'),
122
+ }, async ({ id }) => {
123
+ if (!client.hasApiKey)
124
+ return authError();
125
+ try {
126
+ const data = await client.deleteDiscount(id);
127
+ return {
128
+ content: [
129
+ {
130
+ type: 'text',
131
+ text: JSON.stringify(data, null, 2),
132
+ },
133
+ ],
134
+ };
135
+ }
136
+ catch (error) {
137
+ const message = error instanceof Error ? error.message : 'Unknown error';
138
+ return {
139
+ content: [{ type: 'text', text: `Error deleting discount: ${message}` }],
140
+ isError: true,
141
+ };
142
+ }
143
+ });
144
+ }
@@ -5,7 +5,7 @@ export function register(server, client) {
5
5
  limit: z.number().min(1).max(100).default(50).describe('Max orders to return (1-100)'),
6
6
  offset: z.number().min(0).default(0).describe('Pagination offset'),
7
7
  status: z
8
- .enum(['pending', 'confirmed', 'delivered', 'cancelled'])
8
+ .enum(['pending', 'confirmed', 'shipped', 'delivered', 'cancelled'])
9
9
  .optional()
10
10
  .describe('Filter by order status'),
11
11
  }, async ({ limit, offset, status }) => {
@@ -39,4 +39,75 @@ export function register(server, client) {
39
39
  };
40
40
  }
41
41
  });
42
+ // --- get_order (auth required) ---
43
+ server.tool('get_order', 'Get detailed information about a specific order including items, customer info, and status. Requires KARRITO_API_KEY environment variable.', {
44
+ id: z.string().describe('Order ID to retrieve'),
45
+ }, async ({ id }) => {
46
+ if (!client.hasApiKey) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
52
+ },
53
+ ],
54
+ isError: true,
55
+ };
56
+ }
57
+ try {
58
+ const data = await client.getOrder(id);
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text',
63
+ text: JSON.stringify(data, null, 2),
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ catch (error) {
69
+ const message = error instanceof Error ? error.message : 'Unknown error';
70
+ return {
71
+ content: [{ type: 'text', text: `Error getting order: ${message}` }],
72
+ isError: true,
73
+ };
74
+ }
75
+ });
76
+ // --- update_order_status (auth required) ---
77
+ server.tool('update_order_status', 'Update the status of an order (e.g., confirm, ship, deliver, or cancel). Requires KARRITO_API_KEY environment variable.', {
78
+ id: z.string().describe('Order ID to update'),
79
+ status: z
80
+ .enum(['pending', 'confirmed', 'shipped', 'delivered', 'cancelled'])
81
+ .describe('New order status'),
82
+ }, async ({ id, status }) => {
83
+ if (!client.hasApiKey) {
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
89
+ },
90
+ ],
91
+ isError: true,
92
+ };
93
+ }
94
+ try {
95
+ const data = await client.updateOrderStatus(id, status);
96
+ return {
97
+ content: [
98
+ {
99
+ type: 'text',
100
+ text: JSON.stringify(data, null, 2),
101
+ },
102
+ ],
103
+ };
104
+ }
105
+ catch (error) {
106
+ const message = error instanceof Error ? error.message : 'Unknown error';
107
+ return {
108
+ content: [{ type: 'text', text: `Error updating order status: ${message}` }],
109
+ isError: true,
110
+ };
111
+ }
112
+ });
42
113
  }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { KarritoApiClient } from '../lib/api-client.js';
3
+ export declare function register(server: McpServer, client: KarritoApiClient): void;
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ function authError() {
3
+ return {
4
+ content: [
5
+ {
6
+ type: 'text',
7
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
8
+ },
9
+ ],
10
+ isError: true,
11
+ };
12
+ }
13
+ export function register(server, client) {
14
+ // --- list_reviews (auth required) ---
15
+ server.tool('list_reviews', 'List product reviews in your Karrito store. Can filter by moderation status. Requires KARRITO_API_KEY environment variable.', {
16
+ limit: z.number().min(1).max(100).default(20).describe('Max reviews to return (1-100)'),
17
+ offset: z.number().min(0).default(0).describe('Pagination offset'),
18
+ status: z
19
+ .enum(['pending', 'approved', 'rejected'])
20
+ .optional()
21
+ .describe('Filter by moderation status'),
22
+ }, async ({ limit, offset, status }) => {
23
+ if (!client.hasApiKey)
24
+ return authError();
25
+ try {
26
+ const data = await client.listReviews(limit, offset, status);
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: JSON.stringify(data, null, 2),
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ catch (error) {
37
+ const message = error instanceof Error ? error.message : 'Unknown error';
38
+ return {
39
+ content: [{ type: 'text', text: `Error listing reviews: ${message}` }],
40
+ isError: true,
41
+ };
42
+ }
43
+ });
44
+ // --- moderate_review (auth required) ---
45
+ server.tool('moderate_review', 'Approve or reject a product review. Approved reviews are visible on the public catalog. Requires KARRITO_API_KEY environment variable.', {
46
+ id: z.string().describe('Review ID to moderate'),
47
+ status: z.enum(['approved', 'rejected']).describe('Moderation decision: approved (visible) or rejected (hidden)'),
48
+ }, async ({ id, status }) => {
49
+ if (!client.hasApiKey)
50
+ return authError();
51
+ try {
52
+ const data = await client.moderateReview(id, status);
53
+ return {
54
+ content: [
55
+ {
56
+ type: 'text',
57
+ text: JSON.stringify(data, null, 2),
58
+ },
59
+ ],
60
+ };
61
+ }
62
+ catch (error) {
63
+ const message = error instanceof Error ? error.message : 'Unknown error';
64
+ return {
65
+ content: [{ type: 'text', text: `Error moderating review: ${message}` }],
66
+ isError: true,
67
+ };
68
+ }
69
+ });
70
+ // --- delete_review (auth required) ---
71
+ server.tool('delete_review', 'Permanently delete a product review. Requires KARRITO_API_KEY environment variable.', {
72
+ id: z.string().describe('Review ID to delete'),
73
+ }, async ({ id }) => {
74
+ if (!client.hasApiKey)
75
+ return authError();
76
+ try {
77
+ const data = await client.deleteReview(id);
78
+ return {
79
+ content: [
80
+ {
81
+ type: 'text',
82
+ text: JSON.stringify(data, null, 2),
83
+ },
84
+ ],
85
+ };
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : 'Unknown error';
89
+ return {
90
+ content: [{ type: 'text', text: `Error deleting review: ${message}` }],
91
+ isError: true,
92
+ };
93
+ }
94
+ });
95
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { KarritoApiClient } from '../lib/api-client.js';
3
+ export declare function register(server: McpServer, client: KarritoApiClient): void;
@@ -0,0 +1,139 @@
1
+ import { z } from 'zod';
2
+ function authError() {
3
+ return {
4
+ content: [
5
+ {
6
+ type: 'text',
7
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
8
+ },
9
+ ],
10
+ isError: true,
11
+ };
12
+ }
13
+ export function register(server, client) {
14
+ // --- list_shipping_options (auth required) ---
15
+ server.tool('list_shipping_options', 'List shipping options configured for your Karrito store. Requires KARRITO_API_KEY environment variable.', {}, async () => {
16
+ if (!client.hasApiKey)
17
+ return authError();
18
+ try {
19
+ const data = await client.listShipping();
20
+ return {
21
+ content: [
22
+ {
23
+ type: 'text',
24
+ text: JSON.stringify(data, null, 2),
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof Error ? error.message : 'Unknown error';
31
+ return {
32
+ content: [{ type: 'text', text: `Error listing shipping options: ${message}` }],
33
+ isError: true,
34
+ };
35
+ }
36
+ });
37
+ // --- create_shipping_option (auth required) ---
38
+ server.tool('create_shipping_option', 'Create a new shipping option for your Karrito store (e.g., delivery, pickup, express). Requires KARRITO_API_KEY environment variable.', {
39
+ name: z.string().min(1).max(100).describe('Shipping option name (e.g., "Delivery", "Pickup in store", "Express")'),
40
+ price: z.number().min(0).describe('Shipping price in store currency (0 for free shipping)'),
41
+ description: z.string().max(500).optional().describe('Description of this shipping option'),
42
+ estimatedDays: z.number().int().min(0).optional().describe('Estimated delivery time in days'),
43
+ isActive: z.boolean().optional().describe('Whether this shipping option is active (default: true)'),
44
+ }, async ({ name, price, description, estimatedDays, isActive }) => {
45
+ if (!client.hasApiKey)
46
+ return authError();
47
+ try {
48
+ const shippingData = { name, price };
49
+ if (description !== undefined)
50
+ shippingData.description = description;
51
+ if (estimatedDays !== undefined)
52
+ shippingData.estimatedDays = estimatedDays;
53
+ if (isActive !== undefined)
54
+ shippingData.isActive = isActive;
55
+ const data = await client.createShipping(shippingData);
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: JSON.stringify(data, null, 2),
61
+ },
62
+ ],
63
+ };
64
+ }
65
+ catch (error) {
66
+ const message = error instanceof Error ? error.message : 'Unknown error';
67
+ return {
68
+ content: [{ type: 'text', text: `Error creating shipping option: ${message}` }],
69
+ isError: true,
70
+ };
71
+ }
72
+ });
73
+ // --- update_shipping_option (auth required) ---
74
+ server.tool('update_shipping_option', 'Update an existing shipping option in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
75
+ id: z.string().describe('Shipping option ID to update'),
76
+ name: z.string().min(1).max(100).optional().describe('New shipping option name'),
77
+ price: z.number().min(0).optional().describe('New shipping price'),
78
+ description: z.string().max(500).optional().describe('New description'),
79
+ estimatedDays: z.number().int().min(0).optional().describe('New estimated delivery time in days'),
80
+ isActive: z.boolean().optional().describe('Whether this shipping option is active'),
81
+ }, async ({ id, ...updates }) => {
82
+ if (!client.hasApiKey)
83
+ return authError();
84
+ try {
85
+ const updateData = {};
86
+ for (const [key, value] of Object.entries(updates)) {
87
+ if (value !== undefined)
88
+ updateData[key] = value;
89
+ }
90
+ if (Object.keys(updateData).length === 0) {
91
+ return {
92
+ content: [{ type: 'text', text: 'No fields to update. Provide at least one field (name, price, description, estimatedDays, isActive).' }],
93
+ isError: true,
94
+ };
95
+ }
96
+ const data = await client.updateShipping(id, updateData);
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: JSON.stringify(data, null, 2),
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ catch (error) {
107
+ const message = error instanceof Error ? error.message : 'Unknown error';
108
+ return {
109
+ content: [{ type: 'text', text: `Error updating shipping option: ${message}` }],
110
+ isError: true,
111
+ };
112
+ }
113
+ });
114
+ // --- delete_shipping_option (auth required) ---
115
+ server.tool('delete_shipping_option', 'Delete a shipping option from your Karrito store. Requires KARRITO_API_KEY environment variable.', {
116
+ id: z.string().describe('Shipping option ID to delete'),
117
+ }, async ({ id }) => {
118
+ if (!client.hasApiKey)
119
+ return authError();
120
+ try {
121
+ const data = await client.deleteShipping(id);
122
+ return {
123
+ content: [
124
+ {
125
+ type: 'text',
126
+ text: JSON.stringify(data, null, 2),
127
+ },
128
+ ],
129
+ };
130
+ }
131
+ catch (error) {
132
+ const message = error instanceof Error ? error.message : 'Unknown error';
133
+ return {
134
+ content: [{ type: 'text', text: `Error deleting shipping option: ${message}` }],
135
+ isError: true,
136
+ };
137
+ }
138
+ });
139
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { KarritoApiClient } from '../lib/api-client.js';
3
+ export declare function register(server: McpServer, client: KarritoApiClient): void;
@@ -0,0 +1,34 @@
1
+ export function register(server, client) {
2
+ // --- get_analytics (auth required) ---
3
+ server.tool('get_analytics', 'Get store analytics: total orders, revenue, products, customers, average order value, and popular products. Requires KARRITO_API_KEY environment variable.', {}, async () => {
4
+ if (!client.hasApiKey) {
5
+ return {
6
+ content: [
7
+ {
8
+ type: 'text',
9
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
10
+ },
11
+ ],
12
+ isError: true,
13
+ };
14
+ }
15
+ try {
16
+ const data = await client.getStats();
17
+ return {
18
+ content: [
19
+ {
20
+ type: 'text',
21
+ text: JSON.stringify(data, null, 2),
22
+ },
23
+ ],
24
+ };
25
+ }
26
+ catch (error) {
27
+ const message = error instanceof Error ? error.message : 'Unknown error';
28
+ return {
29
+ content: [{ type: 'text', text: `Error getting analytics: ${message}` }],
30
+ isError: true,
31
+ };
32
+ }
33
+ });
34
+ }
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod';
1
2
  export function register(server, client) {
2
3
  // --- get_my_store (auth required) ---
3
4
  server.tool('get_my_store', 'Get information about your Karrito store (name, slug, currency, stats). Requires KARRITO_API_KEY environment variable.', {}, async () => {
@@ -31,4 +32,93 @@ export function register(server, client) {
31
32
  };
32
33
  }
33
34
  });
35
+ // --- update_store (auth required) ---
36
+ server.tool('update_store', 'Update your Karrito store settings (name, WhatsApp number, currency, description, etc.). Requires KARRITO_API_KEY environment variable.', {
37
+ name: z.string().min(1).max(100).optional().describe('Store name'),
38
+ whatsappNumber: z.string().optional().describe('WhatsApp number in E.164 format (e.g., +18091234567)'),
39
+ currency: z
40
+ .enum(['DOP', 'USD', 'MXN', 'COP', 'ARS', 'BRL'])
41
+ .optional()
42
+ .describe('Store currency'),
43
+ whatsappTemplate: z.string().max(2000).optional().describe('WhatsApp message template with shortcodes: {tienda}, {productos}, {total}, {nombre}'),
44
+ isPublished: z.boolean().optional().describe('Whether the store is publicly visible'),
45
+ isMaintenance: z.boolean().optional().describe('Whether the store is in maintenance mode'),
46
+ description: z.string().max(500).optional().describe('Store description'),
47
+ }, async ({ ...updates }) => {
48
+ if (!client.hasApiKey) {
49
+ return {
50
+ content: [
51
+ {
52
+ type: 'text',
53
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
54
+ },
55
+ ],
56
+ isError: true,
57
+ };
58
+ }
59
+ try {
60
+ const updateData = {};
61
+ for (const [key, value] of Object.entries(updates)) {
62
+ if (value !== undefined)
63
+ updateData[key] = value;
64
+ }
65
+ if (Object.keys(updateData).length === 0) {
66
+ return {
67
+ content: [{ type: 'text', text: 'No fields to update. Provide at least one field (name, whatsappNumber, currency, whatsappTemplate, isPublished, isMaintenance, description).' }],
68
+ isError: true,
69
+ };
70
+ }
71
+ const data = await client.updateStore(updateData);
72
+ return {
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: JSON.stringify(data, null, 2),
77
+ },
78
+ ],
79
+ };
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : 'Unknown error';
83
+ return {
84
+ content: [{ type: 'text', text: `Error updating store: ${message}` }],
85
+ isError: true,
86
+ };
87
+ }
88
+ });
89
+ // --- toggle_publish (auth required) ---
90
+ server.tool('toggle_publish', 'Quickly publish or unpublish your Karrito store. Shortcut for update_store with isPublished. Requires KARRITO_API_KEY environment variable.', {
91
+ published: z.boolean().describe('true to publish the store, false to unpublish'),
92
+ }, async ({ published }) => {
93
+ if (!client.hasApiKey) {
94
+ return {
95
+ content: [
96
+ {
97
+ type: 'text',
98
+ text: 'Authentication required. Set the KARRITO_API_KEY environment variable with your Karrito API key. You can generate one at https://karrito.shop/settings/api',
99
+ },
100
+ ],
101
+ isError: true,
102
+ };
103
+ }
104
+ try {
105
+ const data = await client.updateStore({ isPublished: published });
106
+ const action = published ? 'published' : 'unpublished';
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `Store ${action} successfully.\n\n${JSON.stringify(data, null, 2)}`,
112
+ },
113
+ ],
114
+ };
115
+ }
116
+ catch (error) {
117
+ const message = error instanceof Error ? error.message : 'Unknown error';
118
+ return {
119
+ content: [{ type: 'text', text: `Error toggling publish: ${message}` }],
120
+ isError: true,
121
+ };
122
+ }
123
+ });
34
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karrito-mcp",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server for Karrito — digital catalog builder for WhatsApp sellers in LATAM",
5
5
  "bin": {
6
6
  "karrito-mcp": "./dist/index.js"