@iflow-mcp/dynamicendpoints-etsy-mcp 1.2.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/build/index.js ADDED
@@ -0,0 +1,2459 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import axios from 'axios';
6
+ import { z } from 'zod';
7
+ // Etsy API Configuration
8
+ const ETSY_API_BASE_URL = 'https://openapi.etsy.com/v3';
9
+ // Configuration schema for Smithery - All fields optional
10
+ export const configSchema = z.object({
11
+ apiKey: z.string().optional().describe('Your Etsy API key (Keystring) from the Etsy Developer Portal. Falls back to ETSY_API_KEY environment variable.'),
12
+ shopId: z.string().optional().describe('Your Etsy shop ID (optional, for faster shop operations). Falls back to ETSY_SHOP_ID environment variable.'),
13
+ accessToken: z.string().optional().describe('OAuth access token for shop management features (optional, required for write operations). Falls back to ETSY_ACCESS_TOKEN environment variable.'),
14
+ });
15
+ class EtsyMCPServer {
16
+ server;
17
+ axiosInstance;
18
+ config;
19
+ constructor(config) {
20
+ this.config = config;
21
+ const headers = {
22
+ 'x-api-key': this.config.apiKey,
23
+ };
24
+ // Add OAuth token if available for authenticated requests
25
+ if (this.config.accessToken) {
26
+ headers['Authorization'] = `Bearer ${this.config.accessToken}`;
27
+ }
28
+ this.axiosInstance = axios.create({
29
+ baseURL: ETSY_API_BASE_URL,
30
+ headers,
31
+ });
32
+ this.server = new Server({
33
+ name: 'etsy-mcp-server',
34
+ version: '1.0.0',
35
+ }, {
36
+ capabilities: {
37
+ tools: {},
38
+ prompts: {},
39
+ resources: {},
40
+ },
41
+ });
42
+ this.setupHandlers();
43
+ }
44
+ setupHandlers() {
45
+ // List available tools
46
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
47
+ tools: this.getTools(),
48
+ }));
49
+ // Handle tool calls
50
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
51
+ const { name, arguments: args } = request.params;
52
+ try {
53
+ switch (name) {
54
+ case 'search_listings':
55
+ return await this.searchListings(args);
56
+ case 'get_listing':
57
+ return await this.getListing(args);
58
+ case 'get_shop':
59
+ return await this.getShop(args);
60
+ case 'get_shop_listings':
61
+ return await this.getShopListings(args);
62
+ case 'search_shops':
63
+ return await this.searchShops(args);
64
+ case 'get_listing_inventory':
65
+ return await this.getListingInventory(args);
66
+ case 'get_listing_images':
67
+ return await this.getListingImages(args);
68
+ case 'get_shop_sections':
69
+ return await this.getShopSections(args);
70
+ case 'get_trending_listings':
71
+ return await this.getTrendingListings(args);
72
+ case 'find_shops':
73
+ return await this.findShops(args);
74
+ // Shop Management Tools
75
+ case 'create_listing':
76
+ return await this.createListing(args);
77
+ case 'update_listing':
78
+ return await this.updateListing(args);
79
+ case 'delete_listing':
80
+ return await this.deleteListing(args);
81
+ case 'update_listing_inventory':
82
+ return await this.updateListingInventory(args);
83
+ case 'upload_listing_image':
84
+ return await this.uploadListingImage(args);
85
+ case 'create_shop_section':
86
+ return await this.createShopSection(args);
87
+ case 'update_shop_section':
88
+ return await this.updateShopSection(args);
89
+ case 'delete_shop_section':
90
+ return await this.deleteShopSection(args);
91
+ case 'update_shop':
92
+ return await this.updateShop(args);
93
+ default:
94
+ throw new Error(`Unknown tool: ${name}`);
95
+ }
96
+ }
97
+ catch (error) {
98
+ if (axios.isAxiosError(error)) {
99
+ return {
100
+ content: [
101
+ {
102
+ type: 'text',
103
+ text: JSON.stringify({
104
+ error: error.response?.data || error.message,
105
+ status: error.response?.status,
106
+ }, null, 2),
107
+ },
108
+ ],
109
+ };
110
+ }
111
+ throw error;
112
+ }
113
+ });
114
+ // List available prompts
115
+ this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
116
+ prompts: this.getPrompts(),
117
+ }));
118
+ // Handle prompt requests
119
+ this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
120
+ const { name, arguments: args } = request.params;
121
+ return await this.getPrompt(name, args);
122
+ });
123
+ // List available resources
124
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
125
+ resources: this.getResources(),
126
+ }));
127
+ // Handle resource reads
128
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
129
+ const { uri } = request.params;
130
+ return await this.readResource(uri);
131
+ });
132
+ }
133
+ getTools() {
134
+ return [
135
+ {
136
+ name: 'search_listings',
137
+ description: 'Search for active Etsy listings. Supports keyword search with various filters.',
138
+ inputSchema: {
139
+ type: 'object',
140
+ properties: {
141
+ keywords: {
142
+ type: 'string',
143
+ description: 'Search keywords for finding listings',
144
+ },
145
+ limit: {
146
+ type: 'number',
147
+ description: 'Maximum number of results (default: 25, max: 100)',
148
+ default: 25,
149
+ },
150
+ offset: {
151
+ type: 'number',
152
+ description: 'Number of results to skip (for pagination)',
153
+ default: 0,
154
+ },
155
+ min_price: {
156
+ type: 'number',
157
+ description: 'Minimum price in the shop currency',
158
+ },
159
+ max_price: {
160
+ type: 'number',
161
+ description: 'Maximum price in the shop currency',
162
+ },
163
+ sort_on: {
164
+ type: 'string',
165
+ enum: ['created', 'price', 'updated', 'score'],
166
+ description: 'Field to sort results on',
167
+ },
168
+ sort_order: {
169
+ type: 'string',
170
+ enum: ['asc', 'desc', 'ascending', 'descending'],
171
+ description: 'Sort order',
172
+ },
173
+ },
174
+ required: ['keywords'],
175
+ },
176
+ annotations: {
177
+ readOnlyHint: true,
178
+ destructiveHint: false,
179
+ idempotentHint: true,
180
+ },
181
+ },
182
+ {
183
+ name: 'get_listing',
184
+ description: 'Get detailed information about a specific Etsy listing by its ID.',
185
+ inputSchema: {
186
+ type: 'object',
187
+ properties: {
188
+ listing_id: {
189
+ type: 'number',
190
+ description: 'The numeric ID of the listing',
191
+ },
192
+ includes: {
193
+ type: 'array',
194
+ items: {
195
+ type: 'string',
196
+ enum: ['Shop', 'Images', 'User', 'Translations', 'Inventory'],
197
+ },
198
+ description: 'Additional data to include in the response',
199
+ },
200
+ },
201
+ required: ['listing_id'],
202
+ },
203
+ annotations: {
204
+ readOnlyHint: true,
205
+ destructiveHint: false,
206
+ idempotentHint: true,
207
+ },
208
+ },
209
+ {
210
+ name: 'get_shop',
211
+ description: 'Get information about an Etsy shop by shop ID.',
212
+ inputSchema: {
213
+ type: 'object',
214
+ properties: {
215
+ shop_id: {
216
+ type: 'number',
217
+ description: 'The numeric ID of the shop',
218
+ },
219
+ },
220
+ required: ['shop_id'],
221
+ },
222
+ annotations: {
223
+ readOnlyHint: true,
224
+ destructiveHint: false,
225
+ idempotentHint: true,
226
+ },
227
+ },
228
+ {
229
+ name: 'get_shop_listings',
230
+ description: 'Get all active listings from a specific shop.',
231
+ inputSchema: {
232
+ type: 'object',
233
+ properties: {
234
+ shop_id: {
235
+ type: 'number',
236
+ description: 'The numeric ID of the shop',
237
+ },
238
+ limit: {
239
+ type: 'number',
240
+ description: 'Maximum number of results (default: 25, max: 100)',
241
+ default: 25,
242
+ },
243
+ offset: {
244
+ type: 'number',
245
+ description: 'Number of results to skip (for pagination)',
246
+ default: 0,
247
+ },
248
+ state: {
249
+ type: 'string',
250
+ enum: ['active', 'inactive', 'sold_out', 'draft', 'expired'],
251
+ description: 'Filter by listing state (default: active)',
252
+ default: 'active',
253
+ },
254
+ },
255
+ required: ['shop_id'],
256
+ },
257
+ annotations: {
258
+ readOnlyHint: true,
259
+ destructiveHint: false,
260
+ idempotentHint: true,
261
+ },
262
+ },
263
+ {
264
+ name: 'search_shops',
265
+ description: 'Search for Etsy shops by shop name.',
266
+ inputSchema: {
267
+ type: 'object',
268
+ properties: {
269
+ shop_name: {
270
+ type: 'string',
271
+ description: 'The shop name to search for',
272
+ },
273
+ limit: {
274
+ type: 'number',
275
+ description: 'Maximum number of results (default: 25, max: 100)',
276
+ default: 25,
277
+ },
278
+ offset: {
279
+ type: 'number',
280
+ description: 'Number of results to skip (for pagination)',
281
+ default: 0,
282
+ },
283
+ },
284
+ required: ['shop_name'],
285
+ },
286
+ annotations: {
287
+ readOnlyHint: true,
288
+ destructiveHint: false,
289
+ idempotentHint: true,
290
+ },
291
+ },
292
+ {
293
+ name: 'get_listing_inventory',
294
+ description: 'Get inventory information for a listing, including available quantities and variations.',
295
+ inputSchema: {
296
+ type: 'object',
297
+ properties: {
298
+ listing_id: {
299
+ type: 'number',
300
+ description: 'The numeric ID of the listing',
301
+ },
302
+ },
303
+ required: ['listing_id'],
304
+ },
305
+ annotations: {
306
+ readOnlyHint: true,
307
+ destructiveHint: false,
308
+ idempotentHint: true,
309
+ },
310
+ },
311
+ {
312
+ name: 'get_listing_images',
313
+ description: 'Get all images associated with a specific listing.',
314
+ inputSchema: {
315
+ type: 'object',
316
+ properties: {
317
+ listing_id: {
318
+ type: 'number',
319
+ description: 'The numeric ID of the listing',
320
+ },
321
+ },
322
+ required: ['listing_id'],
323
+ },
324
+ annotations: {
325
+ readOnlyHint: true,
326
+ destructiveHint: false,
327
+ idempotentHint: true,
328
+ },
329
+ },
330
+ {
331
+ name: 'get_shop_sections',
332
+ description: 'Get all sections/categories for a specific shop.',
333
+ inputSchema: {
334
+ type: 'object',
335
+ properties: {
336
+ shop_id: {
337
+ type: 'number',
338
+ description: 'The numeric ID of the shop',
339
+ },
340
+ },
341
+ required: ['shop_id'],
342
+ },
343
+ annotations: {
344
+ readOnlyHint: true,
345
+ destructiveHint: false,
346
+ idempotentHint: true,
347
+ },
348
+ },
349
+ {
350
+ name: 'get_trending_listings',
351
+ description: 'Get current trending listings on Etsy.',
352
+ inputSchema: {
353
+ type: 'object',
354
+ properties: {
355
+ limit: {
356
+ type: 'number',
357
+ description: 'Maximum number of results (default: 25, max: 100)',
358
+ default: 25,
359
+ },
360
+ offset: {
361
+ type: 'number',
362
+ description: 'Number of results to skip (for pagination)',
363
+ default: 0,
364
+ },
365
+ },
366
+ },
367
+ annotations: {
368
+ readOnlyHint: true,
369
+ destructiveHint: false,
370
+ idempotentHint: true,
371
+ },
372
+ },
373
+ {
374
+ name: 'find_shops',
375
+ description: 'Find shops by location or other criteria.',
376
+ inputSchema: {
377
+ type: 'object',
378
+ properties: {
379
+ location: {
380
+ type: 'string',
381
+ description: 'Location to search for shops',
382
+ },
383
+ limit: {
384
+ type: 'number',
385
+ description: 'Maximum number of results (default: 25, max: 100)',
386
+ default: 25,
387
+ },
388
+ offset: {
389
+ type: 'number',
390
+ description: 'Number of results to skip (for pagination)',
391
+ default: 0,
392
+ },
393
+ },
394
+ },
395
+ annotations: {
396
+ readOnlyHint: true,
397
+ destructiveHint: false,
398
+ idempotentHint: true,
399
+ },
400
+ },
401
+ // Shop Management Tools (require OAuth access token)
402
+ {
403
+ name: 'create_listing',
404
+ description: 'Create a new listing in your Etsy shop. Requires OAuth access token.',
405
+ inputSchema: {
406
+ type: 'object',
407
+ properties: {
408
+ shop_id: {
409
+ type: 'number',
410
+ description: 'Your shop ID',
411
+ },
412
+ quantity: {
413
+ type: 'number',
414
+ description: 'Available quantity',
415
+ },
416
+ title: {
417
+ type: 'string',
418
+ description: 'Listing title (max 140 characters)',
419
+ },
420
+ description: {
421
+ type: 'string',
422
+ description: 'Item description',
423
+ },
424
+ price: {
425
+ type: 'number',
426
+ description: 'Price in shop currency',
427
+ },
428
+ who_made: {
429
+ type: 'string',
430
+ enum: ['i_did', 'someone_else', 'collective'],
431
+ description: 'Who made this item',
432
+ },
433
+ when_made: {
434
+ type: 'string',
435
+ description: 'When was it made (e.g., made_to_order, 2020_2023, 2010_2019)',
436
+ },
437
+ taxonomy_id: {
438
+ type: 'number',
439
+ description: 'Category taxonomy ID',
440
+ },
441
+ shipping_profile_id: {
442
+ type: 'number',
443
+ description: 'Shipping profile ID (optional)',
444
+ },
445
+ shop_section_id: {
446
+ type: 'number',
447
+ description: 'Shop section ID (optional)',
448
+ },
449
+ tags: {
450
+ type: 'array',
451
+ items: { type: 'string' },
452
+ description: 'Array of tags (max 13)',
453
+ },
454
+ },
455
+ required: [
456
+ 'shop_id',
457
+ 'quantity',
458
+ 'title',
459
+ 'description',
460
+ 'price',
461
+ 'who_made',
462
+ 'when_made',
463
+ 'taxonomy_id',
464
+ ],
465
+ },
466
+ annotations: {
467
+ readOnlyHint: false,
468
+ destructiveHint: false,
469
+ idempotentHint: false,
470
+ },
471
+ },
472
+ {
473
+ name: 'update_listing',
474
+ description: 'Update an existing listing. Requires OAuth access token.',
475
+ inputSchema: {
476
+ type: 'object',
477
+ properties: {
478
+ shop_id: {
479
+ type: 'number',
480
+ description: 'Your shop ID',
481
+ },
482
+ listing_id: {
483
+ type: 'number',
484
+ description: 'Listing ID to update',
485
+ },
486
+ title: {
487
+ type: 'string',
488
+ description: 'New title',
489
+ },
490
+ description: {
491
+ type: 'string',
492
+ description: 'New description',
493
+ },
494
+ price: {
495
+ type: 'number',
496
+ description: 'New price',
497
+ },
498
+ quantity: {
499
+ type: 'number',
500
+ description: 'New quantity',
501
+ },
502
+ tags: {
503
+ type: 'array',
504
+ items: { type: 'string' },
505
+ description: 'New tags',
506
+ },
507
+ shop_section_id: {
508
+ type: 'number',
509
+ description: 'Shop section ID',
510
+ },
511
+ },
512
+ required: ['shop_id', 'listing_id'],
513
+ },
514
+ annotations: {
515
+ readOnlyHint: false,
516
+ destructiveHint: false,
517
+ idempotentHint: true,
518
+ },
519
+ },
520
+ {
521
+ name: 'delete_listing',
522
+ description: 'Delete a listing from your shop. Requires OAuth access token.',
523
+ inputSchema: {
524
+ type: 'object',
525
+ properties: {
526
+ listing_id: {
527
+ type: 'number',
528
+ description: 'Listing ID to delete',
529
+ },
530
+ },
531
+ required: ['listing_id'],
532
+ },
533
+ annotations: {
534
+ readOnlyHint: false,
535
+ destructiveHint: true,
536
+ idempotentHint: true,
537
+ },
538
+ },
539
+ {
540
+ name: 'update_listing_inventory',
541
+ description: 'Update inventory for a listing (quantities, prices, SKUs). Requires OAuth access token.',
542
+ inputSchema: {
543
+ type: 'object',
544
+ properties: {
545
+ listing_id: {
546
+ type: 'number',
547
+ description: 'Listing ID',
548
+ },
549
+ products: {
550
+ type: 'array',
551
+ description: 'Array of product variations with offerings',
552
+ items: {
553
+ type: 'object',
554
+ properties: {
555
+ sku: { type: 'string' },
556
+ property_values: {
557
+ type: 'array',
558
+ items: {
559
+ type: 'object',
560
+ properties: {
561
+ property_id: { type: 'number' },
562
+ property_name: { type: 'string' },
563
+ scale_id: { type: 'number' },
564
+ value_ids: {
565
+ type: 'array',
566
+ items: { type: 'number' },
567
+ },
568
+ values: {
569
+ type: 'array',
570
+ items: { type: 'string' },
571
+ },
572
+ },
573
+ },
574
+ },
575
+ offerings: {
576
+ type: 'array',
577
+ items: {
578
+ type: 'object',
579
+ properties: {
580
+ price: { type: 'number' },
581
+ quantity: { type: 'number' },
582
+ is_enabled: { type: 'boolean' },
583
+ },
584
+ },
585
+ },
586
+ },
587
+ },
588
+ },
589
+ price_on_property: {
590
+ type: 'array',
591
+ items: { type: 'number' },
592
+ description: 'Property IDs that affect price',
593
+ },
594
+ quantity_on_property: {
595
+ type: 'array',
596
+ items: { type: 'number' },
597
+ description: 'Property IDs that affect quantity',
598
+ },
599
+ sku_on_property: {
600
+ type: 'array',
601
+ items: { type: 'number' },
602
+ description: 'Property IDs that affect SKU',
603
+ },
604
+ },
605
+ required: ['listing_id', 'products'],
606
+ },
607
+ annotations: {
608
+ readOnlyHint: false,
609
+ destructiveHint: false,
610
+ idempotentHint: true,
611
+ },
612
+ },
613
+ {
614
+ name: 'upload_listing_image',
615
+ description: 'Upload an image to a listing. Requires OAuth access token and image file.',
616
+ inputSchema: {
617
+ type: 'object',
618
+ properties: {
619
+ shop_id: {
620
+ type: 'number',
621
+ description: 'Your shop ID',
622
+ },
623
+ listing_id: {
624
+ type: 'number',
625
+ description: 'Listing ID',
626
+ },
627
+ image_url: {
628
+ type: 'string',
629
+ description: 'URL of the image to upload',
630
+ },
631
+ rank: {
632
+ type: 'number',
633
+ description: 'Display order (1 = primary image)',
634
+ },
635
+ alt_text: {
636
+ type: 'string',
637
+ description: 'Alternative text for accessibility',
638
+ },
639
+ },
640
+ required: ['shop_id', 'listing_id', 'image_url'],
641
+ },
642
+ annotations: {
643
+ readOnlyHint: false,
644
+ destructiveHint: false,
645
+ idempotentHint: false,
646
+ },
647
+ },
648
+ {
649
+ name: 'create_shop_section',
650
+ description: 'Create a new shop section/category. Requires OAuth access token.',
651
+ inputSchema: {
652
+ type: 'object',
653
+ properties: {
654
+ shop_id: {
655
+ type: 'number',
656
+ description: 'Your shop ID',
657
+ },
658
+ title: {
659
+ type: 'string',
660
+ description: 'Section title',
661
+ },
662
+ },
663
+ required: ['shop_id', 'title'],
664
+ },
665
+ annotations: {
666
+ readOnlyHint: false,
667
+ destructiveHint: false,
668
+ idempotentHint: false,
669
+ },
670
+ },
671
+ {
672
+ name: 'update_shop_section',
673
+ description: 'Update a shop section. Requires OAuth access token.',
674
+ inputSchema: {
675
+ type: 'object',
676
+ properties: {
677
+ shop_id: {
678
+ type: 'number',
679
+ description: 'Your shop ID',
680
+ },
681
+ shop_section_id: {
682
+ type: 'number',
683
+ description: 'Section ID to update',
684
+ },
685
+ title: {
686
+ type: 'string',
687
+ description: 'New section title',
688
+ },
689
+ },
690
+ required: ['shop_id', 'shop_section_id', 'title'],
691
+ },
692
+ annotations: {
693
+ readOnlyHint: false,
694
+ destructiveHint: false,
695
+ idempotentHint: true,
696
+ },
697
+ },
698
+ {
699
+ name: 'delete_shop_section',
700
+ description: 'Delete a shop section. Requires OAuth access token.',
701
+ inputSchema: {
702
+ type: 'object',
703
+ properties: {
704
+ shop_id: {
705
+ type: 'number',
706
+ description: 'Your shop ID',
707
+ },
708
+ shop_section_id: {
709
+ type: 'number',
710
+ description: 'Section ID to delete',
711
+ },
712
+ },
713
+ required: ['shop_id', 'shop_section_id'],
714
+ },
715
+ annotations: {
716
+ readOnlyHint: false,
717
+ destructiveHint: true,
718
+ idempotentHint: true,
719
+ },
720
+ },
721
+ {
722
+ name: 'update_shop',
723
+ description: 'Update shop information (title, announcement, etc.). Requires OAuth access token.',
724
+ inputSchema: {
725
+ type: 'object',
726
+ properties: {
727
+ shop_id: {
728
+ type: 'number',
729
+ description: 'Your shop ID',
730
+ },
731
+ title: {
732
+ type: 'string',
733
+ description: 'Shop title',
734
+ },
735
+ announcement: {
736
+ type: 'string',
737
+ description: 'Shop announcement message',
738
+ },
739
+ sale_message: {
740
+ type: 'string',
741
+ description: 'Message to buyers at checkout',
742
+ },
743
+ policy_welcome: {
744
+ type: 'string',
745
+ description: 'Shop policies welcome message',
746
+ },
747
+ },
748
+ required: ['shop_id'],
749
+ },
750
+ annotations: {
751
+ readOnlyHint: false,
752
+ destructiveHint: false,
753
+ idempotentHint: true,
754
+ },
755
+ },
756
+ ];
757
+ }
758
+ async searchListings(args) {
759
+ const params = {
760
+ keywords: args.keywords,
761
+ limit: args.limit || 25,
762
+ offset: args.offset || 0,
763
+ };
764
+ if (args.min_price)
765
+ params.min_price = args.min_price;
766
+ if (args.max_price)
767
+ params.max_price = args.max_price;
768
+ if (args.sort_on)
769
+ params.sort_on = args.sort_on;
770
+ if (args.sort_order)
771
+ params.sort_order = args.sort_order;
772
+ const response = await this.axiosInstance.get('/application/listings/active', {
773
+ params,
774
+ });
775
+ return {
776
+ content: [
777
+ {
778
+ type: 'text',
779
+ text: JSON.stringify(response.data, null, 2),
780
+ },
781
+ ],
782
+ };
783
+ }
784
+ async getListing(args) {
785
+ const includes = args.includes?.join(',');
786
+ const params = includes ? { includes } : {};
787
+ const response = await this.axiosInstance.get(`/application/listings/${args.listing_id}`, { params });
788
+ return {
789
+ content: [
790
+ {
791
+ type: 'text',
792
+ text: JSON.stringify(response.data, null, 2),
793
+ },
794
+ ],
795
+ };
796
+ }
797
+ async getShop(args) {
798
+ const response = await this.axiosInstance.get(`/application/shops/${args.shop_id}`);
799
+ return {
800
+ content: [
801
+ {
802
+ type: 'text',
803
+ text: JSON.stringify(response.data, null, 2),
804
+ },
805
+ ],
806
+ };
807
+ }
808
+ async getShopListings(args) {
809
+ const params = {
810
+ limit: args.limit || 25,
811
+ offset: args.offset || 0,
812
+ state: args.state || 'active',
813
+ };
814
+ const response = await this.axiosInstance.get(`/application/shops/${args.shop_id}/listings`, { params });
815
+ return {
816
+ content: [
817
+ {
818
+ type: 'text',
819
+ text: JSON.stringify(response.data, null, 2),
820
+ },
821
+ ],
822
+ };
823
+ }
824
+ async searchShops(args) {
825
+ const params = {
826
+ shop_name: args.shop_name,
827
+ limit: args.limit || 25,
828
+ offset: args.offset || 0,
829
+ };
830
+ const response = await this.axiosInstance.get('/application/shops', { params });
831
+ return {
832
+ content: [
833
+ {
834
+ type: 'text',
835
+ text: JSON.stringify(response.data, null, 2),
836
+ },
837
+ ],
838
+ };
839
+ }
840
+ async getListingInventory(args) {
841
+ const response = await this.axiosInstance.get(`/application/listings/${args.listing_id}/inventory`);
842
+ return {
843
+ content: [
844
+ {
845
+ type: 'text',
846
+ text: JSON.stringify(response.data, null, 2),
847
+ },
848
+ ],
849
+ };
850
+ }
851
+ async getListingImages(args) {
852
+ const response = await this.axiosInstance.get(`/application/listings/${args.listing_id}/images`);
853
+ return {
854
+ content: [
855
+ {
856
+ type: 'text',
857
+ text: JSON.stringify(response.data, null, 2),
858
+ },
859
+ ],
860
+ };
861
+ }
862
+ async getShopSections(args) {
863
+ const response = await this.axiosInstance.get(`/application/shops/${args.shop_id}/sections`);
864
+ return {
865
+ content: [
866
+ {
867
+ type: 'text',
868
+ text: JSON.stringify(response.data, null, 2),
869
+ },
870
+ ],
871
+ };
872
+ }
873
+ async getTrendingListings(args) {
874
+ const params = {
875
+ limit: args.limit || 25,
876
+ offset: args.offset || 0,
877
+ };
878
+ const response = await this.axiosInstance.get('/application/listings/trending', {
879
+ params,
880
+ });
881
+ return {
882
+ content: [
883
+ {
884
+ type: 'text',
885
+ text: JSON.stringify(response.data, null, 2),
886
+ },
887
+ ],
888
+ };
889
+ }
890
+ async findShops(args) {
891
+ const params = {
892
+ limit: args.limit || 25,
893
+ offset: args.offset || 0,
894
+ };
895
+ if (args.location)
896
+ params.location = args.location;
897
+ const response = await this.axiosInstance.get('/application/shops', { params });
898
+ return {
899
+ content: [
900
+ {
901
+ type: 'text',
902
+ text: JSON.stringify(response.data, null, 2),
903
+ },
904
+ ],
905
+ };
906
+ }
907
+ // Shop Management Methods (require OAuth)
908
+ async createListing(args) {
909
+ if (!this.config.accessToken) {
910
+ return {
911
+ content: [
912
+ {
913
+ type: 'text',
914
+ text: JSON.stringify({
915
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
916
+ }, null, 2),
917
+ },
918
+ ],
919
+ };
920
+ }
921
+ const data = {
922
+ quantity: args.quantity,
923
+ title: args.title,
924
+ description: args.description,
925
+ price: args.price,
926
+ who_made: args.who_made,
927
+ when_made: args.when_made,
928
+ taxonomy_id: args.taxonomy_id,
929
+ };
930
+ if (args.shipping_profile_id)
931
+ data.shipping_profile_id = args.shipping_profile_id;
932
+ if (args.shop_section_id)
933
+ data.shop_section_id = args.shop_section_id;
934
+ if (args.tags)
935
+ data.tags = args.tags;
936
+ const response = await this.axiosInstance.post(`/application/shops/${args.shop_id}/listings`, data);
937
+ return {
938
+ content: [
939
+ {
940
+ type: 'text',
941
+ text: JSON.stringify(response.data, null, 2),
942
+ },
943
+ ],
944
+ };
945
+ }
946
+ async updateListing(args) {
947
+ if (!this.config.accessToken) {
948
+ return {
949
+ content: [
950
+ {
951
+ type: 'text',
952
+ text: JSON.stringify({
953
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
954
+ }, null, 2),
955
+ },
956
+ ],
957
+ };
958
+ }
959
+ const data = {};
960
+ if (args.title)
961
+ data.title = args.title;
962
+ if (args.description)
963
+ data.description = args.description;
964
+ if (args.price)
965
+ data.price = args.price;
966
+ if (args.quantity)
967
+ data.quantity = args.quantity;
968
+ if (args.tags)
969
+ data.tags = args.tags;
970
+ if (args.shop_section_id !== undefined)
971
+ data.shop_section_id = args.shop_section_id;
972
+ const response = await this.axiosInstance.patch(`/application/shops/${args.shop_id}/listings/${args.listing_id}`, data);
973
+ return {
974
+ content: [
975
+ {
976
+ type: 'text',
977
+ text: JSON.stringify(response.data, null, 2),
978
+ },
979
+ ],
980
+ };
981
+ }
982
+ async deleteListing(args) {
983
+ if (!this.config.accessToken) {
984
+ return {
985
+ content: [
986
+ {
987
+ type: 'text',
988
+ text: JSON.stringify({
989
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
990
+ }, null, 2),
991
+ },
992
+ ],
993
+ };
994
+ }
995
+ const response = await this.axiosInstance.delete(`/application/listings/${args.listing_id}`);
996
+ return {
997
+ content: [
998
+ {
999
+ type: 'text',
1000
+ text: JSON.stringify({ success: true, message: 'Listing deleted successfully' }, null, 2),
1001
+ },
1002
+ ],
1003
+ };
1004
+ }
1005
+ async updateListingInventory(args) {
1006
+ if (!this.config.accessToken) {
1007
+ return {
1008
+ content: [
1009
+ {
1010
+ type: 'text',
1011
+ text: JSON.stringify({
1012
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1013
+ }, null, 2),
1014
+ },
1015
+ ],
1016
+ };
1017
+ }
1018
+ const data = {
1019
+ products: args.products,
1020
+ };
1021
+ if (args.price_on_property)
1022
+ data.price_on_property = args.price_on_property;
1023
+ if (args.quantity_on_property)
1024
+ data.quantity_on_property = args.quantity_on_property;
1025
+ if (args.sku_on_property)
1026
+ data.sku_on_property = args.sku_on_property;
1027
+ const response = await this.axiosInstance.put(`/application/listings/${args.listing_id}/inventory`, data);
1028
+ return {
1029
+ content: [
1030
+ {
1031
+ type: 'text',
1032
+ text: JSON.stringify(response.data, null, 2),
1033
+ },
1034
+ ],
1035
+ };
1036
+ }
1037
+ async uploadListingImage(args) {
1038
+ if (!this.config.accessToken) {
1039
+ return {
1040
+ content: [
1041
+ {
1042
+ type: 'text',
1043
+ text: JSON.stringify({
1044
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1045
+ }, null, 2),
1046
+ },
1047
+ ],
1048
+ };
1049
+ }
1050
+ // Note: Etsy API requires multipart/form-data for image upload
1051
+ // This is a simplified version - actual implementation needs form-data handling
1052
+ const data = {
1053
+ image: args.image_url,
1054
+ };
1055
+ if (args.rank)
1056
+ data.rank = args.rank;
1057
+ if (args.alt_text)
1058
+ data.alt_text = args.alt_text;
1059
+ const response = await this.axiosInstance.post(`/application/shops/${args.shop_id}/listings/${args.listing_id}/images`, data);
1060
+ return {
1061
+ content: [
1062
+ {
1063
+ type: 'text',
1064
+ text: JSON.stringify(response.data, null, 2),
1065
+ },
1066
+ ],
1067
+ };
1068
+ }
1069
+ async createShopSection(args) {
1070
+ if (!this.config.accessToken) {
1071
+ return {
1072
+ content: [
1073
+ {
1074
+ type: 'text',
1075
+ text: JSON.stringify({
1076
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1077
+ }, null, 2),
1078
+ },
1079
+ ],
1080
+ };
1081
+ }
1082
+ const response = await this.axiosInstance.post(`/application/shops/${args.shop_id}/sections`, { title: args.title });
1083
+ return {
1084
+ content: [
1085
+ {
1086
+ type: 'text',
1087
+ text: JSON.stringify(response.data, null, 2),
1088
+ },
1089
+ ],
1090
+ };
1091
+ }
1092
+ async updateShopSection(args) {
1093
+ if (!this.config.accessToken) {
1094
+ return {
1095
+ content: [
1096
+ {
1097
+ type: 'text',
1098
+ text: JSON.stringify({
1099
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1100
+ }, null, 2),
1101
+ },
1102
+ ],
1103
+ };
1104
+ }
1105
+ const response = await this.axiosInstance.put(`/application/shops/${args.shop_id}/sections/${args.shop_section_id}`, { title: args.title });
1106
+ return {
1107
+ content: [
1108
+ {
1109
+ type: 'text',
1110
+ text: JSON.stringify(response.data, null, 2),
1111
+ },
1112
+ ],
1113
+ };
1114
+ }
1115
+ async deleteShopSection(args) {
1116
+ if (!this.config.accessToken) {
1117
+ return {
1118
+ content: [
1119
+ {
1120
+ type: 'text',
1121
+ text: JSON.stringify({
1122
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1123
+ }, null, 2),
1124
+ },
1125
+ ],
1126
+ };
1127
+ }
1128
+ const response = await this.axiosInstance.delete(`/application/shops/${args.shop_id}/sections/${args.shop_section_id}`);
1129
+ return {
1130
+ content: [
1131
+ {
1132
+ type: 'text',
1133
+ text: JSON.stringify({ success: true, message: 'Shop section deleted successfully' }, null, 2),
1134
+ },
1135
+ ],
1136
+ };
1137
+ }
1138
+ async updateShop(args) {
1139
+ if (!this.config.accessToken) {
1140
+ return {
1141
+ content: [
1142
+ {
1143
+ type: 'text',
1144
+ text: JSON.stringify({
1145
+ error: 'OAuth access token required. Set ETSY_ACCESS_TOKEN environment variable.',
1146
+ }, null, 2),
1147
+ },
1148
+ ],
1149
+ };
1150
+ }
1151
+ const data = {};
1152
+ if (args.title)
1153
+ data.title = args.title;
1154
+ if (args.announcement)
1155
+ data.announcement = args.announcement;
1156
+ if (args.sale_message)
1157
+ data.sale_message = args.sale_message;
1158
+ if (args.policy_welcome)
1159
+ data.policy_welcome = args.policy_welcome;
1160
+ const response = await this.axiosInstance.put(`/application/shops/${args.shop_id}`, data);
1161
+ return {
1162
+ content: [
1163
+ {
1164
+ type: 'text',
1165
+ text: JSON.stringify(response.data, null, 2),
1166
+ },
1167
+ ],
1168
+ };
1169
+ }
1170
+ // Prompts functionality
1171
+ getPrompts() {
1172
+ return [
1173
+ {
1174
+ name: 'create-listing-guide',
1175
+ title: 'Create Listing Guide',
1176
+ description: 'Guide for creating a complete Etsy listing with best practices',
1177
+ arguments: [
1178
+ {
1179
+ name: 'product_type',
1180
+ description: 'Type of product to list (e.g., handmade, vintage, craft supply)',
1181
+ required: true,
1182
+ },
1183
+ ],
1184
+ },
1185
+ {
1186
+ name: 'optimize-listing',
1187
+ title: 'Optimize Listing',
1188
+ description: 'Generate SEO-optimized title, tags, and description for a listing',
1189
+ arguments: [
1190
+ {
1191
+ name: 'listing_id',
1192
+ description: 'The ID of the listing to optimize',
1193
+ required: true,
1194
+ },
1195
+ {
1196
+ name: 'focus_keywords',
1197
+ description: 'Keywords to focus on for SEO',
1198
+ required: false,
1199
+ },
1200
+ ],
1201
+ },
1202
+ {
1203
+ name: 'shop-analytics-review',
1204
+ title: 'Shop Analytics Review',
1205
+ description: 'Create a comprehensive shop performance review template',
1206
+ arguments: [
1207
+ {
1208
+ name: 'shop_id',
1209
+ description: 'The shop ID to analyze',
1210
+ required: true,
1211
+ },
1212
+ {
1213
+ name: 'time_period',
1214
+ description: 'Time period for analysis (e.g., last_month, last_quarter)',
1215
+ required: false,
1216
+ },
1217
+ ],
1218
+ },
1219
+ {
1220
+ name: 'product-photography-tips',
1221
+ title: 'Product Photography Tips',
1222
+ description: 'Get tailored product photography guidance for Etsy',
1223
+ arguments: [
1224
+ {
1225
+ name: 'product_category',
1226
+ description: 'Category of product (e.g., jewelry, home decor, clothing)',
1227
+ required: true,
1228
+ },
1229
+ ],
1230
+ },
1231
+ {
1232
+ name: 'pricing-strategy',
1233
+ title: 'Pricing Strategy',
1234
+ description: 'Calculate competitive pricing for Etsy listings',
1235
+ arguments: [
1236
+ {
1237
+ name: 'material_cost',
1238
+ description: 'Total cost of materials',
1239
+ required: true,
1240
+ },
1241
+ {
1242
+ name: 'time_hours',
1243
+ description: 'Hours spent creating the product',
1244
+ required: true,
1245
+ },
1246
+ {
1247
+ name: 'desired_hourly_rate',
1248
+ description: 'Desired hourly rate for labor',
1249
+ required: false,
1250
+ },
1251
+ ],
1252
+ },
1253
+ ];
1254
+ }
1255
+ async getPrompt(name, args) {
1256
+ switch (name) {
1257
+ case 'create-listing-guide':
1258
+ return {
1259
+ messages: [
1260
+ {
1261
+ role: 'user',
1262
+ content: {
1263
+ type: 'text',
1264
+ text: `Help me create a compelling Etsy listing for a ${args?.product_type || 'handmade'} product. Please guide me through:
1265
+
1266
+ 1. **Title Creation** (max 140 characters):
1267
+ - Include key search terms
1268
+ - Be specific and descriptive
1269
+ - Follow format: [What it is] | [Key feature] | [Use case]
1270
+
1271
+ 2. **Description Structure**:
1272
+ - Opening hook (2-3 sentences)
1273
+ - Key features and benefits (bullet points)
1274
+ - Materials and dimensions
1275
+ - Care instructions
1276
+ - Shipping and policies
1277
+
1278
+ 3. **Tags Strategy** (13 tags):
1279
+ - Mix of broad and specific terms
1280
+ - Include long-tail keywords
1281
+ - Consider seasonal trends
1282
+
1283
+ 4. **Pricing Considerations**:
1284
+ - Material costs + labor + overhead
1285
+ - Competitive analysis
1286
+ - Etsy fees (6.5% transaction + 3% + $0.25 processing)
1287
+
1288
+ 5. **Photos Checklist**:
1289
+ - Main photo: white background, product centered
1290
+ - Lifestyle photos showing scale/use
1291
+ - Detail shots of craftsmanship
1292
+ - Size comparison images
1293
+
1294
+ Please help me optimize each section for maximum visibility and conversions.`,
1295
+ },
1296
+ },
1297
+ ],
1298
+ };
1299
+ case 'optimize-listing':
1300
+ const listingId = args?.listing_id;
1301
+ const keywords = args?.focus_keywords || 'handmade, unique, quality';
1302
+ return {
1303
+ messages: [
1304
+ {
1305
+ role: 'user',
1306
+ content: {
1307
+ type: 'text',
1308
+ text: `Analyze and optimize Etsy listing ${listingId} with focus on these keywords: ${keywords}
1309
+
1310
+ Please provide:
1311
+
1312
+ 1. **SEO-Optimized Title**:
1313
+ - Incorporate keywords: ${keywords}
1314
+ - Stay under 140 characters
1315
+ - Front-load important terms
1316
+
1317
+ 2. **Enhanced Description**:
1318
+ - Keyword-rich opening paragraph
1319
+ - Scannable bullet points
1320
+ - Answer common customer questions
1321
+ - Include size, material, and shipping info
1322
+
1323
+ 3. **Strategic Tags** (13 recommendations):
1324
+ - High-volume search terms
1325
+ - Niche-specific keywords
1326
+ - Long-tail variations
1327
+ - Seasonal opportunities
1328
+
1329
+ 4. **Competitive Analysis**:
1330
+ - Similar listings comparison
1331
+ - Price positioning
1332
+ - Unique selling points
1333
+
1334
+ 5. **Action Items**:
1335
+ - Quick wins for immediate improvement
1336
+ - Long-term optimization strategies
1337
+ - A/B testing suggestions`,
1338
+ },
1339
+ },
1340
+ ],
1341
+ };
1342
+ case 'shop-analytics-review':
1343
+ const shopId = args?.shop_id;
1344
+ const period = args?.time_period || 'last_month';
1345
+ return {
1346
+ messages: [
1347
+ {
1348
+ role: 'user',
1349
+ content: {
1350
+ type: 'text',
1351
+ text: `Create a comprehensive analytics review for Etsy shop ${shopId} covering ${period}.
1352
+
1353
+ Analysis Framework:
1354
+
1355
+ 1. **Traffic Metrics**:
1356
+ - Total visits and sources
1357
+ - Conversion rate trends
1358
+ - Bounce rate analysis
1359
+ - Top-performing listings
1360
+
1361
+ 2. **Sales Performance**:
1362
+ - Revenue and order volume
1363
+ - Average order value
1364
+ - Best-selling products
1365
+ - Seasonal patterns
1366
+
1367
+ 3. **SEO Performance**:
1368
+ - Top search terms driving traffic
1369
+ - Listing ranking improvements/declines
1370
+ - Click-through rates
1371
+ - Tag effectiveness
1372
+
1373
+ 4. **Customer Insights**:
1374
+ - Geographic distribution
1375
+ - Repeat customer rate
1376
+ - Review sentiment analysis
1377
+ - Cart abandonment rate
1378
+
1379
+ 5. **Recommendations**:
1380
+ - Underperforming listings to optimize
1381
+ - Inventory adjustments
1382
+ - Marketing opportunities
1383
+ - Pricing strategy review
1384
+
1385
+ Please use the available MCP tools to gather data and provide actionable insights.`,
1386
+ },
1387
+ },
1388
+ ],
1389
+ };
1390
+ case 'product-photography-tips':
1391
+ const category = args?.product_category || 'general products';
1392
+ return {
1393
+ messages: [
1394
+ {
1395
+ role: 'user',
1396
+ content: {
1397
+ type: 'text',
1398
+ text: `Provide comprehensive product photography guidance for ${category} on Etsy.
1399
+
1400
+ **Photography Essentials:**
1401
+
1402
+ 1. **Equipment Setup**:
1403
+ - Camera: DSLR, mirrorless, or modern smartphone
1404
+ - Lighting: Natural light or softbox setup
1405
+ - Background: White sweep or lifestyle setting
1406
+ - Tripod for consistency
1407
+
1408
+ 2. **Shot List for ${category}**:
1409
+ - Hero shot (main listing photo)
1410
+ - Scale/size reference shots
1411
+ - Detail and texture closeups
1412
+ - Lifestyle/in-use photos
1413
+ - Packaging presentation
1414
+ - Multiple angles (360° view)
1415
+
1416
+ 3. **Technical Requirements**:
1417
+ - Resolution: Minimum 2000px on longest side
1418
+ - Format: JPG for best compatibility
1419
+ - File size: Under 1MB for fast loading
1420
+ - Aspect ratio: Square (1:1) works best
1421
+
1422
+ 4. **Styling Tips for ${category}**:
1423
+ - Props that complement without distracting
1424
+ - Consistent brand aesthetic
1425
+ - Color harmony and contrast
1426
+ - Storytelling through composition
1427
+
1428
+ 5. **Post-Processing**:
1429
+ - Brightness and contrast adjustment
1430
+ - Color correction for accuracy
1431
+ - Background cleanup
1432
+ - Watermarking considerations
1433
+
1434
+ 6. **Etsy-Specific Best Practices**:
1435
+ - First photo determines thumbnail
1436
+ - Mobile optimization crucial (70% of traffic)
1437
+ - Use all 10 image slots
1438
+ - Video recommended for engagement`,
1439
+ },
1440
+ },
1441
+ ],
1442
+ };
1443
+ case 'pricing-strategy':
1444
+ const materialCost = parseFloat(args?.material_cost || '0');
1445
+ const timeHours = parseFloat(args?.time_hours || '0');
1446
+ const hourlyRate = parseFloat(args?.desired_hourly_rate || '25');
1447
+ const laborCost = timeHours * hourlyRate;
1448
+ const subtotal = materialCost + laborCost;
1449
+ const overhead = subtotal * 0.15; // 15% overhead
1450
+ const totalCost = subtotal + overhead;
1451
+ const etsyFees = totalCost * 0.095; // 6.5% transaction + ~3% processing
1452
+ const minimumPrice = totalCost + etsyFees;
1453
+ const suggestedRetail = minimumPrice * 1.5; // 50% profit margin
1454
+ return {
1455
+ messages: [
1456
+ {
1457
+ role: 'user',
1458
+ content: {
1459
+ type: 'text',
1460
+ text: `Pricing Analysis for Your Etsy Product:
1461
+
1462
+ **Cost Breakdown:**
1463
+ - Materials: $${materialCost.toFixed(2)}
1464
+ - Labor (${timeHours}h × $${hourlyRate}/h): $${laborCost.toFixed(2)}
1465
+ - Overhead (15%): $${overhead.toFixed(2)}
1466
+ - **Total Cost: $${totalCost.toFixed(2)}**
1467
+
1468
+ **Etsy Fees:**
1469
+ - Transaction fee (6.5%): $${(totalCost * 0.065).toFixed(2)}
1470
+ - Processing fee (~3%): $${(totalCost * 0.03).toFixed(2)}
1471
+ - Listing fee: $0.20
1472
+ - **Total Fees: $${(etsyFees + 0.20).toFixed(2)}**
1473
+
1474
+ **Pricing Recommendations:**
1475
+
1476
+ 1. **Break-Even Price:** $${minimumPrice.toFixed(2)}
1477
+ - Covers all costs and fees
1478
+ - No profit margin
1479
+
1480
+ 2. **Suggested Retail Price:** $${suggestedRetail.toFixed(2)}
1481
+ - Includes 50% profit margin
1482
+ - Competitive positioning
1483
+ - Room for sales/promotions
1484
+
1485
+ 3. **Premium Positioning:** $${(suggestedRetail * 1.3).toFixed(2)}
1486
+ - Higher perceived value
1487
+ - Artisan/luxury market
1488
+ - Custom/made-to-order
1489
+
1490
+ **Pricing Strategy Tips:**
1491
+
1492
+ • **Psychological Pricing:** Consider ending prices in .95 or .99
1493
+ • **Competitor Analysis:** Research 10-15 similar listings
1494
+ • **Volume Discounts:** Offer tiered pricing for multiple purchases
1495
+ • **Seasonal Adjustments:** Premium during peak seasons
1496
+ • **Bundle Opportunities:** Create product sets for higher AOV
1497
+
1498
+ **Profitability Check:**
1499
+ - At $${suggestedRetail.toFixed(2)}: ${((suggestedRetail - minimumPrice) / suggestedRetail * 100).toFixed(1)}% profit margin
1500
+ - Monthly goal: Sell 20 units = $${(suggestedRetail * 20).toFixed(2)} revenue
1501
+ - Profit: $${((suggestedRetail - minimumPrice) * 20).toFixed(2)}
1502
+
1503
+ Would you like me to analyze competitor pricing or explore different pricing strategies?`,
1504
+ },
1505
+ },
1506
+ ],
1507
+ };
1508
+ default:
1509
+ throw new Error(`Unknown prompt: ${name}`);
1510
+ }
1511
+ }
1512
+ // Resources functionality
1513
+ getResources() {
1514
+ return [
1515
+ {
1516
+ name: 'etsy-api-docs',
1517
+ uri: 'etsy://docs/api',
1518
+ title: 'Etsy API Documentation',
1519
+ description: 'Comprehensive Etsy Open API v3 documentation and reference',
1520
+ mimeType: 'text/plain',
1521
+ },
1522
+ {
1523
+ name: 'etsy-seller-handbook',
1524
+ uri: 'etsy://docs/seller-handbook',
1525
+ title: 'Etsy Seller Handbook',
1526
+ description: 'Best practices and guides for Etsy sellers',
1527
+ mimeType: 'text/plain',
1528
+ },
1529
+ {
1530
+ name: 'etsy-seo-guide',
1531
+ uri: 'etsy://docs/seo-guide',
1532
+ title: 'Etsy SEO Guide',
1533
+ description: 'Complete guide to Etsy search engine optimization',
1534
+ mimeType: 'text/plain',
1535
+ },
1536
+ {
1537
+ name: 'etsy-shipping-guide',
1538
+ uri: 'etsy://docs/shipping',
1539
+ title: 'Etsy Shipping Guide',
1540
+ description: 'Shipping policies, strategies, and best practices',
1541
+ mimeType: 'text/plain',
1542
+ },
1543
+ {
1544
+ name: 'etsy-photography-tips',
1545
+ uri: 'etsy://docs/photography',
1546
+ title: 'Product Photography Tips',
1547
+ description: 'Professional product photography guide for Etsy',
1548
+ mimeType: 'text/plain',
1549
+ },
1550
+ {
1551
+ name: 'etsy-fees-calculator',
1552
+ uri: 'etsy://tools/fees-calculator',
1553
+ title: 'Etsy Fees Calculator',
1554
+ description: 'Calculate all Etsy fees and pricing recommendations',
1555
+ mimeType: 'application/json',
1556
+ },
1557
+ ];
1558
+ }
1559
+ async readResource(uri) {
1560
+ switch (uri) {
1561
+ case 'etsy://docs/api':
1562
+ return {
1563
+ contents: [
1564
+ {
1565
+ uri,
1566
+ mimeType: 'text/plain',
1567
+ text: `# Etsy Open API v3 Documentation
1568
+
1569
+ **Base URL:** https://openapi.etsy.com/v3
1570
+
1571
+ ## Authentication
1572
+ - **API Key:** Required for all requests (x-api-key header)
1573
+ - **OAuth 2.0:** Required for write operations and private data
1574
+
1575
+ ## Rate Limits
1576
+ - 10,000 requests per day per API key
1577
+ - Burst limit: 10 requests per second
1578
+
1579
+ ## Key Endpoints
1580
+
1581
+ ### Listings
1582
+ - GET /v3/application/listings/active - Search active listings
1583
+ - GET /v3/application/listings/{listing_id} - Get listing details
1584
+ - POST /v3/application/shops/{shop_id}/listings - Create listing (OAuth)
1585
+ - PUT /v3/application/shops/{shop_id}/listings/{listing_id} - Update listing (OAuth)
1586
+
1587
+ ### Shops
1588
+ - GET /v3/application/shops/{shop_id} - Get shop details
1589
+ - GET /v3/application/shops/{shop_id}/listings/active - Get shop listings
1590
+ - PATCH /v3/application/shops/{shop_id} - Update shop (OAuth)
1591
+
1592
+ ### Search
1593
+ - GET /v3/application/listings/active - Search with filters
1594
+ - GET /v3/application/shops - Search shops
1595
+
1596
+ ## Common Parameters
1597
+ - **limit:** Number of results (max 100)
1598
+ - **offset:** Pagination offset
1599
+ - **keywords:** Search terms
1600
+ - **sort_on:** created, price, score
1601
+ - **sort_order:** asc, desc
1602
+
1603
+ ## Response Format
1604
+ All responses return JSON with consistent structure:
1605
+ {
1606
+ "count": 10,
1607
+ "results": [...],
1608
+ "next_page": "..."
1609
+ }
1610
+
1611
+ ## Error Codes
1612
+ - 400: Bad Request
1613
+ - 401: Unauthorized
1614
+ - 403: Forbidden
1615
+ - 404: Not Found
1616
+ - 429: Rate Limit Exceeded
1617
+ - 500: Internal Server Error
1618
+
1619
+ For complete documentation, visit: https://developers.etsy.com/documentation`,
1620
+ },
1621
+ ],
1622
+ };
1623
+ case 'etsy://docs/seller-handbook':
1624
+ return {
1625
+ contents: [
1626
+ {
1627
+ uri,
1628
+ mimeType: 'text/plain',
1629
+ text: `# Etsy Seller Handbook
1630
+
1631
+ ## Getting Started
1632
+
1633
+ ### Shop Setup
1634
+ 1. Create your shop name (unique, memorable, brandable)
1635
+ 2. Set shop policies (returns, exchanges, privacy)
1636
+ 3. Configure payment methods
1637
+ 4. Set up shipping profiles
1638
+ 5. Add shop sections for organization
1639
+
1640
+ ### Listing Optimization
1641
+ - **Title:** 140 characters, front-load keywords
1642
+ - **Tags:** 13 tags, mix broad and specific terms
1643
+ - **Description:** Rich details, answer questions
1644
+ - **Photos:** 10 images, high quality, multiple angles
1645
+ - **Price:** Competitive but profitable
1646
+
1647
+ ## Best Practices
1648
+
1649
+ ### Product Photography
1650
+ - Natural lighting or softbox setup
1651
+ - White/neutral background for main image
1652
+ - Lifestyle photos showing scale and use
1653
+ - Detail shots of craftsmanship
1654
+ - Consistent style across listings
1655
+
1656
+ ### SEO Strategy
1657
+ - Research keywords with Etsy search
1658
+ - Use all 13 tag slots
1659
+ - Update titles seasonally
1660
+ - Monitor search analytics
1661
+ - Optimize based on performance
1662
+
1663
+ ### Customer Service
1664
+ - Respond within 24 hours
1665
+ - Be professional and friendly
1666
+ - Address issues promptly
1667
+ - Request reviews politely
1668
+ - Build relationships
1669
+
1670
+ ### Marketing
1671
+ - Share listings on social media
1672
+ - Use Etsy Ads strategically
1673
+ - Offer promotions and sales
1674
+ - Build email list
1675
+ - Collaborate with other sellers
1676
+
1677
+ ### Shop Management
1678
+ - Update inventory regularly
1679
+ - Process orders promptly
1680
+ - Track metrics and analytics
1681
+ - Adjust strategy based on data
1682
+ - Stay current with Etsy policies
1683
+
1684
+ ## Growth Strategies
1685
+
1686
+ ### Scaling Your Business
1687
+ 1. Identify best-sellers
1688
+ 2. Create complementary products
1689
+ 3. Optimize production workflow
1690
+ 4. Consider help or automation
1691
+ 5. Expand product line strategically
1692
+
1693
+ ### Seasonal Planning
1694
+ - Plan inventory 3 months ahead
1695
+ - Create seasonal listings early
1696
+ - Adjust keywords for holidays
1697
+ - Run strategic promotions
1698
+ - Prepare for peak shipping times
1699
+
1700
+ ## Resources
1701
+ - Etsy Seller Handbook: https://www.etsy.com/seller-handbook
1702
+ - Etsy Forums: Community support
1703
+ - Etsy Teams: Local seller groups
1704
+ - Etsy Success Newsletter: Tips and updates`,
1705
+ },
1706
+ ],
1707
+ };
1708
+ case 'etsy://docs/seo-guide':
1709
+ return {
1710
+ contents: [
1711
+ {
1712
+ uri,
1713
+ mimeType: 'text/plain',
1714
+ text: `# Complete Etsy SEO Guide
1715
+
1716
+ ## Understanding Etsy Search
1717
+
1718
+ Etsy search algorithm considers:
1719
+ 1. **Query Matching:** Titles, tags, categories, attributes
1720
+ 2. **Listing Quality Score:** Photos, description, shop policies
1721
+ 3. **Customer Experience:** Reviews, shipping, messages
1722
+ 4. **Shop History:** Sales, favorites, age
1723
+
1724
+ ## Keyword Research
1725
+
1726
+ ### Tools and Methods
1727
+ - Etsy search bar autocomplete
1728
+ - Competitor listing analysis
1729
+ - Google Trends for seasonal terms
1730
+ - Customer language from reviews
1731
+ - Long-tail keyword variations
1732
+
1733
+ ### Keyword Types
1734
+ - **Broad terms:** "handmade jewelry"
1735
+ - **Specific terms:** "sterling silver moon necklace"
1736
+ - **Long-tail:** "personalized graduation gift for daughter"
1737
+ - **Seasonal:** "Christmas stocking stuffer"
1738
+
1739
+ ## Title Optimization
1740
+
1741
+ ### Best Practices
1742
+ - **Front-load keywords:** Most important words first
1743
+ - **Be specific:** "Vintage 1960s Leather Crossbody Bag"
1744
+ - **Include key attributes:** Size, color, material, use
1745
+ - **Use natural language:** Readable, not keyword stuffed
1746
+ - **Stay under 140 characters**
1747
+
1748
+ ### Title Formula
1749
+ [What It Is] | [Key Features] | [Use Case/Benefit]
1750
+
1751
+ Example: "Handmade Ceramic Mug | 16oz Large Coffee Cup | Perfect Gift for Coffee Lovers"
1752
+
1753
+ ## Tag Strategy
1754
+
1755
+ ### Maximizing 13 Tags
1756
+ - 3-4 broad terms (handmade jewelry)
1757
+ - 4-5 specific terms (moonstone pendant necklace)
1758
+ - 3-4 long-tail keywords (boho wedding jewelry gift)
1759
+ - 1-2 seasonal/trending terms
1760
+
1761
+ ### Tag Tips
1762
+ - Use all 13 tags
1763
+ - Multi-word phrases (etsy treats as one tag)
1764
+ - Consider misspellings for common terms
1765
+ - Update seasonally
1766
+ - Test and adjust based on analytics
1767
+
1768
+ ## Description SEO
1769
+
1770
+ ### Structure
1771
+ 1. **Opening paragraph:** Keyword-rich summary (first 160 characters show in search)
1772
+ 2. **Key features:** Bullet points with natural keywords
1773
+ 3. **Details:** Size, materials, care instructions
1774
+ 4. **Story/Use cases:** Emotional connection
1775
+ 5. **Policies:** Shipping, returns (reassurance)
1776
+
1777
+ ### SEO Writing Tips
1778
+ - Natural keyword integration
1779
+ - Answer common questions
1780
+ - Use headers/formatting
1781
+ - Include relevant attributes
1782
+ - Cross-link related listings
1783
+
1784
+ ## Categories and Attributes
1785
+
1786
+ ### Importance
1787
+ - Required for category-specific searches
1788
+ - Helps Etsy understand your product
1789
+ - Affects search placement
1790
+
1791
+ ### Best Practices
1792
+ - Choose most specific category
1793
+ - Fill all relevant attributes
1794
+ - Be accurate (affects trust signals)
1795
+ - Update when Etsy adds new options
1796
+
1797
+ ## Shop-Wide SEO
1798
+
1799
+ ### Shop Title
1800
+ - Appears in external search (Google)
1801
+ - Include main product category
1802
+ - Keep under 55 characters
1803
+
1804
+ ### Shop Sections
1805
+ - Organize products logically
1806
+ - Use keyword-rich section names
1807
+ - Helps customers browse
1808
+ - Improves internal linking
1809
+
1810
+ ### About Section
1811
+ - Tell your brand story
1812
+ - Include relevant keywords naturally
1813
+ - Build trust and connection
1814
+
1815
+ ## Performance Tracking
1816
+
1817
+ ### Key Metrics
1818
+ - **Search views:** How often listings appear
1819
+ - **Click-through rate:** Views vs. visits
1820
+ - **Conversion rate:** Visits to sales
1821
+ - **Search terms:** What brings traffic
1822
+
1823
+ ### Tools
1824
+ - Etsy Stats (Shop Manager)
1825
+ - Google Analytics (external traffic)
1826
+ - Search Analytics (keyword performance)
1827
+
1828
+ ## Advanced Strategies
1829
+
1830
+ ### A/B Testing
1831
+ - Test title variations
1832
+ - Try different primary photos
1833
+ - Adjust pricing strategies
1834
+ - Monitor conversion impact
1835
+
1836
+ ### Seasonal Optimization
1837
+ - Update keywords 6-8 weeks before holidays
1838
+ - Create seasonal landing collections
1839
+ - Adjust inventory for demand
1840
+ - Plan content calendar
1841
+
1842
+ ### External SEO
1843
+ - Social media sharing
1844
+ - Blog content
1845
+ - Pinterest optimization
1846
+ - Backlink building
1847
+
1848
+ ## Common SEO Mistakes
1849
+
1850
+ ### Avoid These
1851
+ - ❌ Keyword stuffing (unreadable titles)
1852
+ - ❌ Irrelevant tags (misleading)
1853
+ - ❌ All caps titles (looks spammy)
1854
+ - ❌ Trademark violations
1855
+ - ❌ Duplicate listings (compete with yourself)
1856
+ - ❌ Ignoring analytics (data-driven decisions)
1857
+
1858
+ ## Quick Wins Checklist
1859
+
1860
+ 1. ✅ Use all 13 tags on every listing
1861
+ 2. ✅ Front-load titles with keywords
1862
+ 3. ✅ Fill all category attributes
1863
+ 4. ✅ Add 10 high-quality photos
1864
+ 5. ✅ Write detailed descriptions
1865
+ 6. ✅ Update seasonal listings early
1866
+ 7. ✅ Monitor and adjust based on stats
1867
+ 8. ✅ Respond to messages quickly
1868
+ 9. ✅ Encourage reviews
1869
+ 10. ✅ Keep inventory active
1870
+
1871
+ Remember: SEO is ongoing. Review and optimize regularly based on performance data.`,
1872
+ },
1873
+ ],
1874
+ };
1875
+ case 'etsy://docs/shipping':
1876
+ return {
1877
+ contents: [
1878
+ {
1879
+ uri,
1880
+ mimeType: 'text/plain',
1881
+ text: `# Etsy Shipping Guide
1882
+
1883
+ ## Shipping Strategy
1884
+
1885
+ ### Setting Up Profiles
1886
+ - Create profiles for different product types
1887
+ - Include domestic and international options
1888
+ - Consider package dimensions and weight
1889
+ - Build in handling time realistically
1890
+
1891
+ ### Pricing Models
1892
+ 1. **Calculated shipping:** Real-time carrier rates
1893
+ 2. **Fixed shipping:** Set price per location
1894
+ 3. **Free shipping:** Built into product price
1895
+ 4. **Combined shipping:** Discounts for multiple items
1896
+
1897
+ ## Domestic Shipping (US)
1898
+
1899
+ ### Carrier Options
1900
+ - **USPS:** Most cost-effective for small items
1901
+ - First Class: Under 16oz, 2-5 days
1902
+ - Priority Mail: 1-3 days, includes tracking
1903
+ - Priority Express: Overnight
1904
+ - **UPS/FedEx:** Better for heavy items
1905
+ - **Regional carriers:** For local deliveries
1906
+
1907
+ ### Shipping Supplies
1908
+ - Proper boxes/mailers for protection
1909
+ - Bubble wrap or packing peanuts
1910
+ - Thank you cards/branding materials
1911
+ - Shipping labels (consider label printer)
1912
+ - Tape gun for efficiency
1913
+
1914
+ ## International Shipping
1915
+
1916
+ ### Considerations
1917
+ - Customs forms required
1918
+ - Longer delivery times (7-21+ days)
1919
+ - Higher costs
1920
+ - Customs fees (buyer responsibility)
1921
+ - Tracking may be limited
1922
+
1923
+ ### International Best Practices
1924
+ - Clearly state delivery times
1925
+ - Mention customs fees in listing
1926
+ - Use USPS International services
1927
+ - Consider restricted countries
1928
+ - Package securely for long transit
1929
+
1930
+ ## Free Shipping
1931
+
1932
+ ### Etsy Free Shipping Guarantee
1933
+ - US orders $35+
1934
+ - Boosts search placement
1935
+ - Competitive advantage
1936
+
1937
+ ### Implementation Strategies
1938
+ 1. **Built-in pricing:** Add shipping to product cost
1939
+ 2. **Threshold:** Free over $X
1940
+ 3. **Flat rate:** Same price all items
1941
+ 4. **Promotional:** Limited time offers
1942
+
1943
+ ### Making It Work
1944
+ - Calculate average shipping cost
1945
+ - Adjust product pricing accordingly
1946
+ - Monitor profit margins
1947
+ - Test different thresholds
1948
+
1949
+ ## Processing and Handling
1950
+
1951
+ ### Handling Time
1952
+ - Set realistic timeframes
1953
+ - Account for production time
1954
+ - Consider weekends/holidays
1955
+ - Update for busy seasons
1956
+ - Communicate delays promptly
1957
+
1958
+ ### Best Practices
1959
+ - Process orders within 24 hours
1960
+ - Print labels via Etsy (discounts)
1961
+ - Upload tracking immediately
1962
+ - Send shipped notification
1963
+ - Include tracking in message
1964
+
1965
+ ## Packaging Tips
1966
+
1967
+ ### Protection
1968
+ - Double box fragile items
1969
+ - Use adequate cushioning
1970
+ - Seal securely with quality tape
1971
+ - Waterproof if necessary
1972
+ - Test drop to ensure safety
1973
+
1974
+ ### Branding
1975
+ - Thank you card with logo
1976
+ - Branded tissue paper/tape
1977
+ - Business cards for reorders
1978
+ - Care instruction cards
1979
+ - Surprise bonus (sticker, sample)
1980
+
1981
+ ### Sustainability
1982
+ - Eco-friendly materials
1983
+ - Minimal packaging (reduce waste)
1984
+ - Recyclable/biodegradable options
1985
+ - Reuse shipping materials
1986
+ - Communicate green practices
1987
+
1988
+ ## Tracking and Insurance
1989
+
1990
+ ### Tracking
1991
+ - Always include for $20+ orders
1992
+ - Protects buyer and seller
1993
+ - Required for claims
1994
+ - Builds customer confidence
1995
+ - Enables delivery confirmation
1996
+
1997
+ ### Insurance
1998
+ - Recommend for $100+ items
1999
+ - Protects against loss/damage
2000
+ - Small additional cost
2001
+ - Peace of mind for both parties
2002
+ - Required for high-value claims
2003
+
2004
+ ## Handling Shipping Issues
2005
+
2006
+ ### Lost Packages
2007
+ 1. Check tracking for updates
2008
+ 2. Contact carrier after expected delivery
2009
+ 3. File claim if insured
2010
+ 4. Reship or refund customer
2011
+ 5. Document everything
2012
+
2013
+ ### Damaged Items
2014
+ 1. Request photos from customer
2015
+ 2. File carrier claim if insured
2016
+ 3. Offer replacement or refund
2017
+ 4. Improve packaging for future
2018
+ 5. Learn from issues
2019
+
2020
+ ### Delays
2021
+ - Communicate proactively
2022
+ - Provide tracking updates
2023
+ - Apologize sincerely
2024
+ - Offer solutions (expedited reship)
2025
+ - Learn from experience
2026
+
2027
+ ## International Customs
2028
+
2029
+ ### Required Information
2030
+ - Accurate item description
2031
+ - Value for customs
2032
+ - Country of origin
2033
+ - HS tariff code (when applicable)
2034
+ - Proper forms (CN22, CN23)
2035
+
2036
+ ### Common Issues
2037
+ - Held in customs (varies by country)
2038
+ - Duties/taxes (buyer pays)
2039
+ - Restricted items (varies by country)
2040
+ - Lost customs forms
2041
+
2042
+ ## Shipping Metrics to Track
2043
+
2044
+ ### Key Performance Indicators
2045
+ - Average shipping cost per order
2046
+ - Delivery time vs. promised time
2047
+ - Damage/loss rate
2048
+ - Customer satisfaction with shipping
2049
+ - Carrier performance
2050
+
2051
+ ### Optimization
2052
+ - Compare carrier rates regularly
2053
+ - Negotiate volume discounts
2054
+ - Optimize package sizes
2055
+ - Reduce handling time
2056
+ - Improve packaging efficiency
2057
+
2058
+ ## Seasonal Shipping
2059
+
2060
+ ### Holiday Preparation
2061
+ - Post cutoff dates clearly
2062
+ - Allow extra processing time
2063
+ - Recommend shipping upgrades
2064
+ - Communicate delays promptly
2065
+ - Set shop vacation if overwhelmed
2066
+
2067
+ ### Peak Season Tips
2068
+ - Order supplies early
2069
+ - Batch process orders
2070
+ - Use shipping software
2071
+ - Consider hired help
2072
+ - Maintain quality standards
2073
+
2074
+ ## Shipping Policies
2075
+
2076
+ ### What to Include
2077
+ - Processing/handling time
2078
+ - Shipping methods available
2079
+ - International shipping details
2080
+ - Customs/duties information
2081
+ - Lost package procedures
2082
+ - Upgrade options
2083
+
2084
+ ### Communication
2085
+ - Clear in listing descriptions
2086
+ - Shop policies page
2087
+ - Order messages
2088
+ - FAQ section
2089
+ - Proactive updates
2090
+
2091
+ Remember: Excellent shipping experience leads to 5-star reviews and repeat customers!`,
2092
+ },
2093
+ ],
2094
+ };
2095
+ case 'etsy://docs/photography':
2096
+ return {
2097
+ contents: [
2098
+ {
2099
+ uri,
2100
+ mimeType: 'text/plain',
2101
+ text: `# Professional Product Photography for Etsy
2102
+
2103
+ ## Equipment Essentials
2104
+
2105
+ ### Camera Options
2106
+ - **DSLR/Mirrorless:** Best quality and control
2107
+ - **High-end smartphone:** Adequate for many products
2108
+ - **Point-and-shoot:** Budget-friendly option
2109
+
2110
+ ### Lighting
2111
+ - **Natural light:** Free, beautiful (near window)
2112
+ - **Softbox kit:** $50-200, consistent results
2113
+ - **Ring light:** Great for small items
2114
+ - **Reflectors:** Bounce and fill light ($20-40)
2115
+
2116
+ ### Support Equipment
2117
+ - **Tripod:** Essential for consistency ($30-100)
2118
+ - **Backdrop:** White seamless paper or sweep
2119
+ - **Light tent:** For small items ($20-50)
2120
+ - **Props:** Lifestyle and scale reference
2121
+
2122
+ ## Photo Requirements
2123
+
2124
+ ### Technical Specifications
2125
+ - **Resolution:** Minimum 2000px longest side
2126
+ - **Format:** JPG preferred
2127
+ - **File size:** Under 1MB for fast loading
2128
+ - **Aspect ratio:** Square (1:1) ideal
2129
+ - **Number:** Use all 10 slots
2130
+
2131
+ ### Etsy-Specific Considerations
2132
+ - First photo is thumbnail (most important)
2133
+ - Mobile optimization critical (70% traffic)
2134
+ - Photos appear in search results
2135
+ - Zoom feature requires high resolution
2136
+
2137
+ ## The Essential Shot List
2138
+
2139
+ ### 1. Hero Shot (Main Photo)
2140
+ - Clean white/neutral background
2141
+ - Product centered and well-lit
2142
+ - Shows full item clearly
2143
+ - No distractions
2144
+ - **This determines clicks!**
2145
+
2146
+ ### 2. Detail Shots (2-3 photos)
2147
+ - Close-ups of craftsmanship
2148
+ - Texture and materials
2149
+ - Quality indicators
2150
+ - Unique features
2151
+ - Proof of handmade quality
2152
+
2153
+ ### 3. Scale Reference (1-2 photos)
2154
+ - Product in hand
2155
+ - Next to common object (coin, ruler)
2156
+ - Lifestyle shot showing size
2157
+ - Multiple items together
2158
+
2159
+ ### 4. Lifestyle Photos (2-3 photos)
2160
+ - Product in use
2161
+ - Styled in environment
2162
+ - Emotional connection
2163
+ - Shows practical application
2164
+ - Aspirational yet relatable
2165
+
2166
+ ### 5. Variations (if applicable)
2167
+ - Color options
2168
+ - Size options
2169
+ - Customization examples
2170
+ - Before/after (if relevant)
2171
+
2172
+ ### 6. Packaging Shot (1 photo)
2173
+ - How it arrives
2174
+ - Gift-ready presentation
2175
+ - Professional impression
2176
+ - Sets expectations
2177
+
2178
+ ## Lighting Techniques
2179
+
2180
+ ### Natural Light Setup
2181
+ 1. **Location:** Near large window
2182
+ 2. **Time:** Overcast day or indirect sunlight
2183
+ 3. **Positioning:** Product 3-5 feet from window
2184
+ 4. **Fill light:** White reflector opposite window
2185
+ 5. **Background:** Window to side, not behind
2186
+
2187
+ ### Artificial Light Setup
2188
+ 1. **Main light:** 45° angle, above product
2189
+ 2. **Fill light:** Opposite side, lower intensity
2190
+ 3. **Background light:** Optional, separates product
2191
+ 4. **Diffusion:** Soften with umbrella or softbox
2192
+
2193
+ ### Common Lighting Mistakes
2194
+ - ❌ Direct harsh sunlight (hard shadows)
2195
+ - ❌ Yellow indoor bulbs (color cast)
2196
+ - ❌ Mixed light sources (inconsistent color)
2197
+ - ❌ Underexposure (dark, details lost)
2198
+ - ❌ Overexposure (blown highlights)
2199
+
2200
+ ## Styling and Composition
2201
+
2202
+ ### Rule of Thirds
2203
+ - Divide frame into 3x3 grid
2204
+ - Place subject at intersections
2205
+ - Creates visual interest
2206
+ - More dynamic than centered
2207
+
2208
+ ### Background Choices
2209
+ - **White:** Clean, professional, versatile
2210
+ - **Neutral (gray, beige):** Sophisticated
2211
+ - **Wood/texture:** Warm, handmade feel
2212
+ - **Lifestyle setting:** Context and story
2213
+ - **Avoid:** Busy patterns, competing colors
2214
+
2215
+ ### Props and Styling
2216
+ - **Purpose:** Complement, not compete
2217
+ - **Scale:** Show size and context
2218
+ - **Color:** Harmonize with product
2219
+ - **Relevance:** Support product story
2220
+ - **Minimalism:** Less is often more
2221
+
2222
+ ## Camera Settings
2223
+
2224
+ ### Smartphone Photography
2225
+ - **HDR Mode:** ON for balanced exposure
2226
+ - **Grid:** Enable for composition
2227
+ - **Focus:** Tap to focus on product
2228
+ - **Exposure:** Adjust with slider
2229
+ - **Portrait Mode:** Use for depth (carefully)
2230
+
2231
+ ### DSLR/Mirrorless Settings
2232
+ - **Aperture:** f/8-f/16 for sharpness
2233
+ - **ISO:** 100-400 (minimize noise)
2234
+ - **Shutter speed:** 1/125 or faster
2235
+ - **White balance:** Match light source
2236
+ - **Shooting mode:** Aperture priority or manual
2237
+
2238
+ ## Post-Processing
2239
+
2240
+ ### Essential Edits
2241
+ 1. **Crop:** Straighten and compose
2242
+ 2. **Exposure:** Brighten if needed
2243
+ 3. **White balance:** Correct color cast
2244
+ 4. **Contrast:** Add depth
2245
+ 5. **Sharpen:** Enhance details (subtle)
2246
+
2247
+ ### Editing Software
2248
+ - **Mobile:** Snapseed, VSCO, Lightroom Mobile
2249
+ - **Desktop:** Adobe Lightroom, Photoshop
2250
+ - **Free:** GIMP, Photopea
2251
+ - **Etsy App:** Basic editing built-in
2252
+
2253
+ ### Consistency is Key
2254
+ - Create presets for brand look
2255
+ - Edit entire shoot together
2256
+ - Match lighting across images
2257
+ - Maintain color accuracy
2258
+ - Same background style
2259
+
2260
+ ## Category-Specific Tips
2261
+
2262
+ ### Jewelry
2263
+ - Macro lens or smartphone macro mode
2264
+ - Neutral background or lifestyle
2265
+ - Show scale (worn on model)
2266
+ - Capture sparkle and detail
2267
+ - Multiple angles of stones
2268
+
2269
+ ### Clothing
2270
+ - On model or dress form
2271
+ - Show fit and drape
2272
+ - Detail shots of fabric/stitching
2273
+ - Size chart reference
2274
+ - Flat lay for patterns
2275
+
2276
+ ### Home Decor
2277
+ - Staged in room setting
2278
+ - Show scale with furniture
2279
+ - Multiple room applications
2280
+ - Detail shots of materials
2281
+ - Lighting effects if applicable
2282
+
2283
+ ### Art/Prints
2284
+ - Straight-on, no distortion
2285
+ - Frame it to show presentation
2286
+ - Include size reference
2287
+ - Detail shot if textured
2288
+ - Lifestyle shot on wall
2289
+
2290
+ ## Mobile Photography Tips
2291
+
2292
+ ### Smartphone Best Practices
2293
+ - Clean lens before shooting
2294
+ - Use built-in grid lines
2295
+ - Tap to focus on product
2296
+ - Shoot in natural light
2297
+ - Use HDR mode
2298
+ - Take multiple shots
2299
+ - Edit consistently
2300
+
2301
+ ### Mobile Limitations
2302
+ - Lower resolution than DSLR
2303
+ - Less control over depth of field
2304
+ - Challenging in low light
2305
+ - Limited zoom (use digital carefully)
2306
+
2307
+ ## Video Content
2308
+
2309
+ ### Why Add Video
2310
+ - 360° product view
2311
+ - Show size and scale
2312
+ - Demonstrate use
2313
+ - Increase engagement
2314
+ - Boost conversions
2315
+
2316
+ ### Video Tips
2317
+ - 5-15 seconds ideal
2318
+ - Stable (use tripod)
2319
+ - Good lighting
2320
+ - Slow smooth movements
2321
+ - No sound needed
2322
+ - Show key features
2323
+
2324
+ ## Photo Mistakes to Avoid
2325
+
2326
+ ### Common Errors
2327
+ - ❌ Cluttered background
2328
+ - ❌ Poor lighting (dark/yellow)
2329
+ - ❌ Blurry images
2330
+ - ❌ Incorrect colors
2331
+ - ❌ Can't see details
2332
+ - ❌ Inconsistent style
2333
+ - ❌ Filter overuse
2334
+ - ❌ Watermarks (reduces clicks)
2335
+
2336
+ ## Workflow Efficiency
2337
+
2338
+ ### Batch Photography
2339
+ 1. Set up once for multiple products
2340
+ 2. Same lighting for consistency
2341
+ 3. Shoot all variations
2342
+ 4. Edit together with presets
2343
+ 5. Upload in batch
2344
+
2345
+ ### Time-Saving Tips
2346
+ - Prepare all products beforehand
2347
+ - Set up permanent photo station
2348
+ - Use presets for editing
2349
+ - Create shot list template
2350
+ - Schedule regular photo days
2351
+
2352
+ ## Testing and Optimization
2353
+
2354
+ ### A/B Testing
2355
+ - Try different main photos
2356
+ - Test lifestyle vs. white background
2357
+ - Monitor click-through rates
2358
+ - Adjust based on performance
2359
+
2360
+ ### Analytics
2361
+ - Track which photos get clicked
2362
+ - Monitor conversion rates
2363
+ - Note customer questions about photos
2364
+ - Update based on data
2365
+
2366
+ Remember: Photos are your most important listing element. Invest time in getting them right!`,
2367
+ },
2368
+ ],
2369
+ };
2370
+ case 'etsy://tools/fees-calculator':
2371
+ return {
2372
+ contents: [
2373
+ {
2374
+ uri,
2375
+ mimeType: 'application/json',
2376
+ text: JSON.stringify({
2377
+ title: 'Etsy Fees Calculator',
2378
+ description: 'Calculate all Etsy fees and determine optimal pricing',
2379
+ fees: {
2380
+ listing_fee: 0.20,
2381
+ transaction_fee_percent: 6.5,
2382
+ payment_processing_percent: 3.0,
2383
+ payment_processing_fixed: 0.25,
2384
+ offsite_ads_percent: 12.0, // for sales under $10k/year
2385
+ currency_conversion_percent: 2.5,
2386
+ },
2387
+ calculator: {
2388
+ example: {
2389
+ product_price: 50.00,
2390
+ calculation: {
2391
+ listing_fee: 0.20,
2392
+ transaction_fee: 3.25, // 6.5% of $50
2393
+ payment_processing: 1.75, // 3% of $50 + $0.25
2394
+ total_fees: 5.20,
2395
+ seller_receives: 44.80,
2396
+ fee_percentage: 10.4,
2397
+ },
2398
+ },
2399
+ },
2400
+ pricing_formula: {
2401
+ minimum_price: '(Material_Cost + Labor_Cost + Overhead) / (1 - Fee_Percentage)',
2402
+ suggested_retail: 'Minimum_Price * (1 + Desired_Profit_Margin)',
2403
+ profit_margin: 'Recommended 30-50% for sustainable business',
2404
+ },
2405
+ tips: [
2406
+ 'Build fees into your pricing, don\'t absorb them',
2407
+ 'Account for 10-15% in fees for domestic sales',
2408
+ 'Add 15-20% for international sales (currency conversion)',
2409
+ 'Remember offsite ads are 12% (15% if over $10k annual)',
2410
+ 'Free shipping? Add average shipping cost to price',
2411
+ 'Consider payment processor fees vary by country',
2412
+ ],
2413
+ }, null, 2),
2414
+ },
2415
+ ],
2416
+ };
2417
+ default:
2418
+ throw new Error(`Unknown resource URI: ${uri}`);
2419
+ }
2420
+ }
2421
+ getServer() {
2422
+ return this.server;
2423
+ }
2424
+ async run() {
2425
+ const transport = new StdioServerTransport();
2426
+ await this.server.connect(transport);
2427
+ console.error('Etsy MCP Server running on stdio');
2428
+ }
2429
+ }
2430
+ // Smithery createServer export (required for Smithery deployment)
2431
+ export default function createServer({ config }) {
2432
+ const etsyConfig = {
2433
+ apiKey: config?.apiKey || process.env.ETSY_API_KEY || 'demo-key-readonly',
2434
+ shopId: config?.shopId || process.env.ETSY_SHOP_ID,
2435
+ accessToken: config?.accessToken || process.env.ETSY_ACCESS_TOKEN,
2436
+ };
2437
+ // No validation - server can run without credentials for demo/documentation purposes
2438
+ // API calls will fail gracefully if credentials are missing
2439
+ const mcpServer = new EtsyMCPServer(etsyConfig);
2440
+ return mcpServer.getServer();
2441
+ }
2442
+ // CLI entry point (for direct execution with stdio)
2443
+ if (import.meta.url === `file://${process.argv[1]}`) {
2444
+ const config = {
2445
+ apiKey: process.env.ETSY_API_KEY || '',
2446
+ shopId: process.env.ETSY_SHOP_ID,
2447
+ accessToken: process.env.ETSY_ACCESS_TOKEN,
2448
+ };
2449
+ if (!config.apiKey) {
2450
+ console.error('Error: ETSY_API_KEY environment variable is required');
2451
+ process.exit(1);
2452
+ }
2453
+ const server = new EtsyMCPServer(config);
2454
+ server.run().catch((error) => {
2455
+ console.error('Fatal error:', error);
2456
+ process.exit(1);
2457
+ });
2458
+ }
2459
+ //# sourceMappingURL=index.js.map