karrito-mcp 2.0.2 → 3.0.0

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
@@ -54,7 +54,7 @@ Add to your MCP configuration:
54
54
 
55
55
  | Variable | Required | Description |
56
56
  |----------|----------|-------------|
57
- | `KARRITO_API_KEY` | For authenticated tools | Your Karrito API key from [Settings > API](https://karrito.shop/settings/api) |
57
+ | `KARRITO_API_KEY` | For authenticated tools | Your Karrito API key from [Settings > Integrations](https://karrito.shop/admin/settings) |
58
58
  | `KARRITO_API_URL` | No | Custom API URL (default: `https://karrito.shop`) |
59
59
 
60
60
  ## Resources (5)
@@ -64,10 +64,10 @@ Static data about Karrito — no authentication required.
64
64
  | Resource | URI | Description |
65
65
  |----------|-----|-------------|
66
66
  | Pricing | `karrito://pricing` | Plans, prices, and features comparison |
67
- | Features | `karrito://features` | Complete feature list (core, pro, upcoming) |
67
+ | Features | `karrito://features` | Complete feature list (core, pro, lifetime, upcoming) |
68
68
  | Niches | `karrito://niches` | All 50 available store niches |
69
69
  | Competitors | `karrito://competitors` | 19 competitors compared with Karrito advantages |
70
- | Currencies | `karrito://currencies` | 6 supported LATAM currencies |
70
+ | Currencies | `karrito://currencies` | 6 supported currencies (DOP, USD, MXN, COP, ARS, BRL) |
71
71
 
72
72
  ## Tools (30)
73
73
 
@@ -75,7 +75,7 @@ Static data about Karrito — no authentication required.
75
75
 
76
76
  | Tool | Description |
77
77
  |------|-------------|
78
- | `search_catalogs` | Search public catalogs by keyword |
78
+ | `search_catalogs` | Search published catalogs by keyword |
79
79
  | `get_niche_info` | Get info about a specific niche |
80
80
 
81
81
  ### Products (auth required)
@@ -102,7 +102,7 @@ Static data about Karrito — no authentication required.
102
102
  |------|-------------|
103
103
  | `list_my_orders` | List orders with optional status filter |
104
104
  | `get_order` | Get detailed order info |
105
- | `update_order_status` | Change order status (confirm, ship, deliver, cancel) |
105
+ | `update_order_status` | Change order status (PENDING, CONFIRMED, COMPLETED, CANCELLED) |
106
106
 
107
107
  ### Store (auth required)
108
108
 
@@ -151,55 +151,73 @@ Static data about Karrito — no authentication required.
151
151
  | `update_shipping_option` | Update a shipping option |
152
152
  | `delete_shipping_option` | Delete a shipping option |
153
153
 
154
- ## Usage examples
155
-
156
- ### Browse public catalogs
154
+ ## Order status flow
157
155
 
158
- > "Search for bakery catalogs on Karrito"
156
+ ```
157
+ PENDING → CONFIRMED → COMPLETED
158
+ ↓ ↓
159
+ CANCELLED CANCELLED
160
+ ```
159
161
 
160
- The assistant will use `search_catalogs` with query "bakery" to find matching stores.
162
+ - `PENDING`: New order, not yet confirmed
163
+ - `CONFIRMED`: Order accepted by seller
164
+ - `COMPLETED`: Order delivered/fulfilled (terminal)
165
+ - `CANCELLED`: Order cancelled (terminal)
161
166
 
162
- ### Explore niches
167
+ ## Usage examples
163
168
 
164
- > "What niches does Karrito support for food businesses?"
169
+ ### Browse public catalogs
165
170
 
166
- The assistant will use `get_niche_info` to search food-related niches.
171
+ > "Search for bakery catalogs on Karrito"
167
172
 
168
173
  ### Manage your catalog
169
174
 
170
175
  > "List all my products and add a new one called 'Chocolate Cake' at $15"
171
176
 
172
- The assistant will use `list_my_products` and `create_product` (requires `KARRITO_API_KEY`).
173
-
174
177
  ### Manage orders
175
178
 
176
179
  > "Show me all pending orders and confirm the first one"
177
180
 
178
- The assistant will use `list_my_orders` with status filter and `update_order_status`.
179
-
180
181
  ### Create discounts
181
182
 
182
183
  > "Create a 20% discount code VERANO20 that expires on December 31"
183
184
 
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
185
  ### Get analytics
193
186
 
194
187
  > "How is my store performing? Show me the stats"
195
188
 
196
- The assistant will use `get_analytics` to retrieve store metrics.
197
-
198
189
  ### Compare platforms
199
190
 
200
191
  > "How does Karrito compare to Shopify and TiendaNube?"
201
192
 
202
- The assistant will read the `karrito://competitors` resource.
193
+ ## Changelog
194
+
195
+ ### v3.0.0 (2026-03-23)
196
+
197
+ **Breaking changes:**
198
+ - Order status values are now UPPERCASE: `PENDING`, `CONFIRMED`, `COMPLETED`, `CANCELLED`
199
+ - Removed `shipped` and `delivered` statuses (use `COMPLETED` instead)
200
+ - `create_shipping_option` no longer accepts `description` or `estimatedDays` (not supported by API)
201
+ - `update_shipping_option` no longer accepts `description` or `estimatedDays`
202
+
203
+ **Bug fixes:**
204
+ - Fixed `search_catalogs` — was calling non-existent endpoint, now works correctly
205
+ - Fixed `update_order_status` — was sending wrong status enum values
206
+ - Fixed `list_my_orders` status filter — was offering invalid status values
207
+ - Fixed `get_customer` — was returning list instead of individual customer
208
+ - Fixed pricing resource — `removeBranding` was incorrectly marked as Pro (it's Lifetime only)
209
+ - Fixed features resource — "Remove branding" moved from Pro to Lifetime section
210
+ - Fixed currencies resource — added BRL (Brazilian Real), removed CLP (not supported)
211
+ - Fixed auth error message URL — now points to correct page (`/admin/settings`)
212
+ - Fixed order status update URL path (`/orders/:id` instead of `/orders/:id/status`)
213
+
214
+ **Improvements:**
215
+ - Unified version across all files (was 4 different versions)
216
+ - Centralized auth error handling via shared `auth-guard.ts`
217
+ - Added `lifetime` section to features resource
218
+ - Added `position` field to `update_shipping_option`
219
+ - Added `comparePrice` and `isActive` to `create_product`
220
+ - Better tool descriptions with valid status values documented
203
221
 
204
222
  ## Development
205
223
 
package/dist/index.js CHANGED
@@ -19,9 +19,12 @@ import { register as registerReviewTools } from './tools/reviews.js';
19
19
  import { register as registerCustomerTools } from './tools/customers.js';
20
20
  import { register as registerStatsTools } from './tools/stats.js';
21
21
  import { register as registerShippingTools } from './tools/shipping.js';
22
+ const VERSION = '3.0.0';
23
+ const TOOLS = 30;
24
+ const RESOURCES = 5;
22
25
  const server = new McpServer({
23
26
  name: 'karrito',
24
- version: '2.0.0',
27
+ version: VERSION,
25
28
  });
26
29
  const client = new KarritoApiClient();
27
30
  // Register static resources (public, no auth needed)
@@ -77,13 +80,10 @@ async function main() {
77
80
  : 'not configured';
78
81
  // Verify connection if API key is present
79
82
  const { store, latency, error } = await verifyConnection();
80
- const VERSION = '2.0.1';
81
- const TOOLS = 30;
82
- const RESOURCES = 5;
83
83
  line('');
84
84
  line(` ${c.green}╔${'═'.repeat(51)}╗${c.reset}`);
85
85
  line(` ${c.green}║${c.reset} ${c.green}║${c.reset}`);
86
- line(` ${c.green}║${c.reset} ${c.bold}${c.green}🛒 Karrito MCP Server${c.reset} ${c.dim}v${VERSION}${c.reset} ${c.green}║${c.reset}`);
86
+ line(` ${c.green}║${c.reset} ${c.bold}${c.green}Karrito MCP Server${c.reset} ${c.dim}v${VERSION}${c.reset} ${c.green}║${c.reset}`);
87
87
  line(` ${c.green}║${c.reset} ${c.green}║${c.reset}`);
88
88
  line(` ${c.green}║${c.reset} ${c.white}${TOOLS} tools${c.reset} ${c.dim}•${c.reset} ${c.white}${RESOURCES} resources${c.reset} ${c.green}║${c.reset}`);
89
89
  line(` ${c.green}║${c.reset} ${c.dim}Manage your entire store from AI${c.reset} ${c.green}║${c.reset}`);
@@ -93,7 +93,7 @@ async function main() {
93
93
  line(` ${c.green}╠${'═'.repeat(51)}╣${c.reset}`);
94
94
  line(` ${c.green}║${c.reset} ${c.green}║${c.reset}`);
95
95
  line(` ${c.green}║${c.reset} ${c.dim}Store${c.reset} ${c.bold}${c.white}${pad(store.name, 36)}${c.reset} ${c.green}║${c.reset}`);
96
- line(` ${c.green}║${c.reset} ${c.dim}Plan${c.reset} ${pad(store.currency, 37)} ${c.green}║${c.reset}`);
96
+ line(` ${c.green}║${c.reset} ${c.dim}Currency${c.reset} ${pad(store.currency, 37)} ${c.green}║${c.reset}`);
97
97
  line(` ${c.green}║${c.reset} ${c.dim}Products${c.reset} ${pad(`${store.productCount} active`, 37)} ${c.green}║${c.reset}`);
98
98
  line(` ${c.green}║${c.reset} ${c.dim}Orders${c.reset} ${pad(`${store.orderCount} total`, 37)} ${c.green}║${c.reset}`);
99
99
  line(` ${c.green}║${c.reset} ${c.dim}Status${c.reset} ${store.isPublished ? `${c.green}● Published${c.reset}` : `${c.yellow}○ Unpublished${c.reset}`}${' '.repeat(store.isPublished ? 26 : 24)} ${c.green}║${c.reset}`);
@@ -9,7 +9,7 @@ export declare class KarritoApiClient {
9
9
  constructor(options?: ApiOptions);
10
10
  get hasApiKey(): boolean;
11
11
  private request;
12
- searchCatalogs(query?: string): Promise<unknown>;
12
+ searchCatalogs(query: string): Promise<unknown>;
13
13
  checkSlug(slug: string): Promise<{
14
14
  available: boolean;
15
15
  }>;
@@ -1,4 +1,5 @@
1
1
  const BASE_URL = process.env.KARRITO_API_URL ?? 'https://karrito.shop';
2
+ const VERSION = '3.0.0';
2
3
  export class KarritoApiClient {
3
4
  baseUrl;
4
5
  apiKey;
@@ -14,7 +15,7 @@ export class KarritoApiClient {
14
15
  async request(path, options = {}) {
15
16
  const headers = {
16
17
  'Content-Type': 'application/json',
17
- 'User-Agent': 'karrito-mcp/2.0.0',
18
+ 'User-Agent': `karrito-mcp/${VERSION}`,
18
19
  };
19
20
  if (this.apiKey) {
20
21
  headers['Authorization'] = `Bearer ${this.apiKey}`;
@@ -41,8 +42,7 @@ export class KarritoApiClient {
41
42
  }
42
43
  // --- Public endpoints (no auth) ---
43
44
  async searchCatalogs(query) {
44
- const params = query ? `?q=${encodeURIComponent(query)}` : '';
45
- return this.request(`/api/v1/stores${params}`);
45
+ return this.request(`/api/v1/catalogs?q=${encodeURIComponent(query)}`);
46
46
  }
47
47
  async checkSlug(slug) {
48
48
  return this.request(`/api/check-slug?slug=${encodeURIComponent(slug)}`);
@@ -101,7 +101,7 @@ export class KarritoApiClient {
101
101
  return this.request(`/api/v1/orders/${id}`);
102
102
  }
103
103
  async updateOrderStatus(id, status) {
104
- return this.request(`/api/v1/orders/${id}/status`, {
104
+ return this.request(`/api/v1/orders/${id}`, {
105
105
  method: 'PUT',
106
106
  body: JSON.stringify({ status }),
107
107
  });
@@ -143,7 +143,7 @@ export class KarritoApiClient {
143
143
  return this.request(`/api/v1/reviews?limit=${limit}&offset=${offset}${statusParam}`);
144
144
  }
145
145
  async moderateReview(id, status) {
146
- return this.request(`/api/v1/reviews/${id}/moderate`, {
146
+ return this.request(`/api/v1/reviews/${id}`, {
147
147
  method: 'PUT',
148
148
  body: JSON.stringify({ status }),
149
149
  });
@@ -0,0 +1,12 @@
1
+ import type { KarritoApiClient } from './api-client.js';
2
+ export declare function authError(): {
3
+ content: {
4
+ type: "text";
5
+ text: string;
6
+ }[];
7
+ isError: boolean;
8
+ };
9
+ /**
10
+ * Returns an auth error response if the client has no API key, or null if authenticated.
11
+ */
12
+ export declare function requireAuth(client: KarritoApiClient): ReturnType<typeof authError> | null;
@@ -0,0 +1,20 @@
1
+ const AUTH_ERROR_MESSAGE = 'Authentication required. Set the KARRITO_API_KEY environment variable. Generate one at https://karrito.shop/admin/settings (Integrations tab).';
2
+ export function authError() {
3
+ return {
4
+ content: [
5
+ {
6
+ type: 'text',
7
+ text: AUTH_ERROR_MESSAGE,
8
+ },
9
+ ],
10
+ isError: true,
11
+ };
12
+ }
13
+ /**
14
+ * Returns an auth error response if the client has no API key, or null if authenticated.
15
+ */
16
+ export function requireAuth(client) {
17
+ if (!client.hasApiKey)
18
+ return authError();
19
+ return null;
20
+ }
@@ -1,18 +1,18 @@
1
1
  const CURRENCIES_DATA = {
2
2
  supported: [
3
3
  { code: 'DOP', name: 'Peso dominicano', symbol: 'RD$', country: 'Republica Dominicana', decimalPlaces: 2 },
4
+ { code: 'USD', name: 'US Dollar', symbol: '$', country: 'United States / International', decimalPlaces: 2 },
4
5
  { code: 'MXN', name: 'Peso mexicano', symbol: '$', country: 'Mexico', decimalPlaces: 2 },
5
6
  { code: 'COP', name: 'Peso colombiano', symbol: '$', country: 'Colombia', decimalPlaces: 0 },
6
7
  { code: 'ARS', name: 'Peso argentino', symbol: '$', country: 'Argentina', decimalPlaces: 2 },
7
- { code: 'CLP', name: 'Peso chileno', symbol: '$', country: 'Chile', decimalPlaces: 0 },
8
- { code: 'USD', name: 'US Dollar', symbol: '$', country: 'United States / International', decimalPlaces: 2 },
8
+ { code: 'BRL', name: 'Real brasileno', symbol: 'R$', country: 'Brasil', decimalPlaces: 2 },
9
9
  ],
10
10
  default: 'DOP',
11
11
  notes: 'Currency is set per store. Prices display with the local symbol and formatting. No currency conversion is performed — sellers set prices in their local currency.',
12
12
  };
13
13
  export function register(server) {
14
14
  server.resource('currencies', 'karrito://currencies', {
15
- description: 'Supported currencies in Karrito — 6 LATAM currencies with formatting details',
15
+ description: 'Supported currencies in Karrito — 6 currencies with formatting details',
16
16
  mimeType: 'application/json',
17
17
  }, async (uri) => ({
18
18
  contents: [
@@ -4,7 +4,7 @@ const FEATURES_DATA = {
4
4
  { name: 'WhatsApp checkout', description: 'Customers order via WhatsApp message — no payment gateway needed' },
5
5
  { name: 'Custom slug', description: 'Your store at karrito.shop/your-name or custom domain' },
6
6
  { name: 'Mobile-first', description: 'Optimized for mobile browsers where 90%+ LATAM traffic comes from' },
7
- { name: 'Multi-currency', description: 'DOP, MXN, COP, ARS, CLP, USD supported natively' },
7
+ { name: 'Multi-currency', description: 'DOP, MXN, COP, ARS, BRL, USD supported natively' },
8
8
  { name: 'Product variants', description: 'Size, color, flavor — any variant type (Pro+)' },
9
9
  { name: 'Categories', description: 'Organize products with drag-and-drop categories' },
10
10
  { name: 'Product images', description: 'Up to 5 images per product with automatic optimization' },
@@ -14,10 +14,16 @@ const FEATURES_DATA = {
14
14
  pro: [
15
15
  { name: 'Analytics dashboard', description: 'Views, clicks, orders, top products, conversion funnel' },
16
16
  { name: 'Custom domain', description: 'Use your own domain (e.g., tienda.tudominio.com)' },
17
- { name: 'Remove branding', description: 'No "Powered by Karrito" badge' },
18
17
  { name: 'Priority support', description: 'WhatsApp support with <4h response time' },
19
18
  { name: 'Bulk import', description: 'Import products via CSV' },
20
19
  { name: 'Order history', description: 'Track and manage customer orders' },
20
+ { name: 'Discount codes', description: 'Percentage or fixed amount discount codes' },
21
+ { name: 'API access', description: 'REST API + MCP server for AI-powered store management' },
22
+ ],
23
+ lifetime: [
24
+ { name: 'Remove branding', description: 'No "Powered by Karrito" badge on your catalog' },
25
+ { name: 'Dedicated setup', description: 'We help you set up your store and import products' },
26
+ { name: 'Unlimited everything', description: 'No limits on products, categories, or staff' },
21
27
  ],
22
28
  upcoming: [
23
29
  { name: 'AI product descriptions', description: 'Generate compelling product descriptions with AI' },
@@ -30,7 +36,7 @@ const FEATURES_DATA = {
30
36
  };
31
37
  export function register(server) {
32
38
  server.resource('features', 'karrito://features', {
33
- description: 'Complete list of Karrito features — core, pro, and upcoming',
39
+ description: 'Complete list of Karrito features — core, pro, lifetime, and upcoming',
34
40
  mimeType: 'application/json',
35
41
  }, async (uri) => ({
36
42
  contents: [
@@ -21,7 +21,7 @@ const PRICING_DATA = {
21
21
  variants: true,
22
22
  analytics: true,
23
23
  customDomain: true,
24
- removeBranding: true,
24
+ removeBranding: false,
25
25
  },
26
26
  {
27
27
  name: 'Lifetime',
@@ -33,6 +33,7 @@ const PRICING_DATA = {
33
33
  analytics: true,
34
34
  customDomain: true,
35
35
  removeBranding: true,
36
+ dedicatedSetup: true,
36
37
  },
37
38
  ],
38
39
  commissions: 'Never. 0% on all plans.',
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  import { NICHES_DATA } from '../resources/niches.js';
3
3
  export function register(server, client) {
4
4
  // --- search_catalogs (public, no auth) ---
5
- server.tool('search_catalogs', 'Search public Karrito catalogs by keyword. No authentication required.', {
5
+ server.tool('search_catalogs', 'Search public Karrito catalogs by keyword (store name, product, or niche). No authentication required. Returns published stores matching the query.', {
6
6
  query: z.string().min(1).max(100).describe('Search query — store name, product, or niche'),
7
7
  }, async ({ query }) => {
8
8
  try {
@@ -1,96 +1,53 @@
1
1
  import { z } from 'zod';
2
+ import { requireAuth } from '../lib/auth-guard.js';
2
3
  export function register(server, client) {
3
- // --- list_categories (auth required) ---
4
- server.tool('list_categories', 'List categories in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
4
+ // --- list_categories ---
5
+ server.tool('list_categories', 'List categories in your Karrito store. Requires KARRITO_API_KEY.', {
5
6
  limit: z.number().min(1).max(100).default(50).describe('Max categories to return (1-100)'),
6
7
  offset: z.number().min(0).default(0).describe('Pagination offset'),
7
8
  }, async ({ limit, offset }) => {
8
- if (!client.hasApiKey) {
9
- return {
10
- content: [
11
- {
12
- type: 'text',
13
- 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',
14
- },
15
- ],
16
- isError: true,
17
- };
18
- }
9
+ const guard = requireAuth(client);
10
+ if (guard)
11
+ return guard;
19
12
  try {
20
13
  const data = await client.listCategories(limit, offset);
21
- return {
22
- content: [
23
- {
24
- type: 'text',
25
- text: JSON.stringify(data, null, 2),
26
- },
27
- ],
28
- };
14
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
29
15
  }
30
16
  catch (error) {
31
17
  const message = error instanceof Error ? error.message : 'Unknown error';
32
- return {
33
- content: [{ type: 'text', text: `Error listing categories: ${message}` }],
34
- isError: true,
35
- };
18
+ return { content: [{ type: 'text', text: `Error listing categories: ${message}` }], isError: true };
36
19
  }
37
20
  });
38
- // --- create_category (auth required) ---
39
- server.tool('create_category', 'Create a new category in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
21
+ // --- create_category ---
22
+ server.tool('create_category', 'Create a new category in your Karrito store. Requires KARRITO_API_KEY.', {
40
23
  name: z.string().min(1).max(100).describe('Category name'),
41
24
  position: z.number().int().min(0).optional().describe('Sort order (lower = first)'),
42
25
  }, async ({ name, position }) => {
43
- if (!client.hasApiKey) {
44
- return {
45
- content: [
46
- {
47
- type: 'text',
48
- 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',
49
- },
50
- ],
51
- isError: true,
52
- };
53
- }
26
+ const guard = requireAuth(client);
27
+ if (guard)
28
+ return guard;
54
29
  try {
55
30
  const categoryData = { name };
56
31
  if (position !== undefined)
57
32
  categoryData.position = position;
58
33
  const data = await client.createCategory(categoryData);
59
- return {
60
- content: [
61
- {
62
- type: 'text',
63
- text: JSON.stringify(data, null, 2),
64
- },
65
- ],
66
- };
34
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
67
35
  }
68
36
  catch (error) {
69
37
  const message = error instanceof Error ? error.message : 'Unknown error';
70
- return {
71
- content: [{ type: 'text', text: `Error creating category: ${message}` }],
72
- isError: true,
73
- };
38
+ return { content: [{ type: 'text', text: `Error creating category: ${message}` }], isError: true };
74
39
  }
75
40
  });
76
- // --- update_category (auth required) ---
77
- server.tool('update_category', 'Update an existing category in your Karrito store. Requires KARRITO_API_KEY environment variable.', {
41
+ // --- update_category ---
42
+ server.tool('update_category', 'Update an existing category in your Karrito store. Requires KARRITO_API_KEY.', {
78
43
  id: z.string().describe('Category ID to update'),
79
44
  name: z.string().min(1).max(100).optional().describe('New category name'),
80
45
  position: z.number().int().min(0).optional().describe('New sort order (lower = first)'),
81
46
  isActive: z.boolean().optional().describe('Whether the category is active'),
82
47
  }, 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
- }
48
+ const guard = requireAuth(client);
49
+ if (guard)
50
+ return guard;
94
51
  try {
95
52
  const updateData = {};
96
53
  for (const [key, value] of Object.entries(updates)) {
@@ -104,55 +61,27 @@ export function register(server, client) {
104
61
  };
105
62
  }
106
63
  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
- };
64
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
115
65
  }
116
66
  catch (error) {
117
67
  const message = error instanceof Error ? error.message : 'Unknown error';
118
- return {
119
- content: [{ type: 'text', text: `Error updating category: ${message}` }],
120
- isError: true,
121
- };
68
+ return { content: [{ type: 'text', text: `Error updating category: ${message}` }], isError: true };
122
69
  }
123
70
  });
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.', {
71
+ // --- delete_category ---
72
+ server.tool('delete_category', 'Delete a category from your Karrito store. Products in this category will become uncategorized. Requires KARRITO_API_KEY.', {
126
73
  id: z.string().describe('Category ID to delete'),
127
74
  }, 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
- }
75
+ const guard = requireAuth(client);
76
+ if (guard)
77
+ return guard;
139
78
  try {
140
79
  const data = await client.deleteCategory(id);
141
- return {
142
- content: [
143
- {
144
- type: 'text',
145
- text: JSON.stringify(data, null, 2),
146
- },
147
- ],
148
- };
80
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
149
81
  }
150
82
  catch (error) {
151
83
  const message = error instanceof Error ? error.message : 'Unknown error';
152
- return {
153
- content: [{ type: 'text', text: `Error deleting category: ${message}` }],
154
- isError: true,
155
- };
84
+ return { content: [{ type: 'text', text: `Error deleting category: ${message}` }], isError: true };
156
85
  }
157
86
  });
158
87
  }
@@ -1,65 +1,37 @@
1
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
- }
2
+ import { requireAuth } from '../lib/auth-guard.js';
13
3
  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.', {
4
+ // --- list_customers ---
5
+ server.tool('list_customers', 'List customers who have placed orders in your Karrito store. Requires KARRITO_API_KEY.', {
16
6
  limit: z.number().min(1).max(100).default(50).describe('Max customers to return (1-100)'),
17
7
  offset: z.number().min(0).default(0).describe('Pagination offset'),
18
8
  }, async ({ limit, offset }) => {
19
- if (!client.hasApiKey)
20
- return authError();
9
+ const guard = requireAuth(client);
10
+ if (guard)
11
+ return guard;
21
12
  try {
22
13
  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
- };
14
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
31
15
  }
32
16
  catch (error) {
33
17
  const message = error instanceof Error ? error.message : 'Unknown error';
34
- return {
35
- content: [{ type: 'text', text: `Error listing customers: ${message}` }],
36
- isError: true,
37
- };
18
+ return { content: [{ type: 'text', text: `Error listing customers: ${message}` }], isError: true };
38
19
  }
39
20
  });
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.', {
21
+ // --- get_customer ---
22
+ server.tool('get_customer', 'Get detailed information about a specific customer including their order history. Requires KARRITO_API_KEY.', {
42
23
  id: z.string().describe('Customer ID to retrieve'),
43
24
  }, async ({ id }) => {
44
- if (!client.hasApiKey)
45
- return authError();
25
+ const guard = requireAuth(client);
26
+ if (guard)
27
+ return guard;
46
28
  try {
47
29
  const data = await client.getCustomer(id);
48
- return {
49
- content: [
50
- {
51
- type: 'text',
52
- text: JSON.stringify(data, null, 2),
53
- },
54
- ],
55
- };
30
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
56
31
  }
57
32
  catch (error) {
58
33
  const message = error instanceof Error ? error.message : 'Unknown error';
59
- return {
60
- content: [{ type: 'text', text: `Error getting customer: ${message}` }],
61
- isError: true,
62
- };
34
+ return { content: [{ type: 'text', text: `Error getting customer: ${message}` }], isError: true };
63
35
  }
64
36
  });
65
37
  }