ebay-mcp 1.4.7 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,15 +4,15 @@
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/ebay-mcp)](https://www.npmjs.com/package/ebay-mcp)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/ebay-mcp)](https://www.npmjs.com/package/ebay-mcp)
7
- [![Tests](https://img.shields.io/badge/tests-890%2B%20passing-brightgreen)](tests/)
7
+ [![Tests](https://img.shields.io/badge/tests-914%20passing-brightgreen)](tests/)
8
8
  [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
 
10
10
  [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/yosefhayim-ebay-api-mcp-server-badge.png)](https://mseep.ai/app/yosefhayim-ebay-api-mcp-server)
11
11
  <a href="https://www.buymeacoffee.com/yosefhayim" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
12
12
 
13
- A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providing AI assistants with comprehensive access to eBay's Sell APIs. Includes 275+ tools for inventory management, order fulfillment, marketing campaigns, analytics, and more.
13
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providing AI assistants with comprehensive access to eBay's Sell APIs. Includes **339 tools** for inventory management, order fulfillment, marketing campaigns, analytics, developer tools, and more.
14
14
 
15
- **API Coverage:** 99.1% (~110 of 111 eBay Sell API endpoints)
15
+ **API Coverage:** 82.5% (293 of 355 eBay API endpoints)
16
16
 
17
17
  </div>
18
18
 
@@ -75,12 +75,13 @@ For official eBay API support, please refer to the [eBay Developer Program](http
75
75
 
76
76
  ## Features
77
77
 
78
- - **275+ eBay API Tools** - Comprehensive coverage of eBay Sell APIs across inventory, orders, marketing, analytics, and more
78
+ - **339 eBay API Tools** - Comprehensive coverage of eBay Sell APIs across inventory, orders, marketing, analytics, developer tools, and more
79
79
  - **OAuth 2.0 Support** - Full user token management with automatic refresh
80
80
  - **Type Safety** - Built with TypeScript, Zod validation, and OpenAPI-generated types
81
81
  - **MCP Integration** - STDIO transport for direct integration with AI assistants
82
82
  - **Smart Authentication** - Automatic fallback from user tokens (10k-50k req/day) to client credentials (1k req/day)
83
- - **Well Tested** - 870+ tests with 99%+ function coverage
83
+ - **Well Tested** - 914+ tests with comprehensive coverage
84
+ - **Developer Analytics** - Rate limit monitoring and signing key management
84
85
 
85
86
  ## Quick Start
86
87
 
@@ -115,13 +116,7 @@ Run the interactive setup wizard:
115
116
  npm run setup
116
117
  ```
117
118
 
118
- Or manually configure:
119
-
120
- ```bash
121
- cp .env.example .env
122
- # Edit .env with your credentials
123
- npm run auto-setup
124
- ```
119
+ Or configure manually by copying `.env.example` to `.env` and editing your credentials.
125
120
 
126
121
  ### 4. Configure MCP Client
127
122
 
@@ -284,15 +279,16 @@ Monitor your API usage in the [eBay Developer Portal](https://developer.ebay.com
284
279
 
285
280
  ## Available Tools
286
281
 
287
- The server provides 275+ tools organized into the following categories:
282
+ The server provides **339 tools** organized into the following categories:
288
283
 
289
284
  - **Account Management** - Policies, programs, subscriptions, sales tax
290
- - **Inventory Management** - Items, offers, locations, bulk operations
291
- - **Order Fulfillment** - Orders, shipping, refunds, disputes
292
- - **Marketing & Promotions** - Campaigns, ads, promotions, bidding
285
+ - **Inventory Management** - Items, offers, locations, bulk operations, SKU location mapping
286
+ - **Order Fulfillment** - Orders, shipping, refunds, disputes, payment dispute evidence
287
+ - **Marketing & Promotions** - Campaigns, ads, promotions, bidding, bulk operations
293
288
  - **Analytics** - Traffic reports, seller standards, metrics
294
- - **Communication** - Buyer-seller messaging, negotiations
289
+ - **Communication** - Buyer-seller messaging, negotiations, notifications, feedback
295
290
  - **Metadata & Taxonomy** - Categories, item aspects, policies
291
+ - **Developer Tools** - Rate limits, signing keys, client registration
296
292
  - **Token Management** - OAuth URL generation, token management
297
293
 
298
294
  **Example Tools:**
@@ -357,103 +353,54 @@ Here are some common tasks you can accomplish with the eBay MCP server:
357
353
  - npm or pnpm
358
354
  - eBay Developer Account
359
355
 
360
- ### Setup
356
+ ### Quick Start for Contributors
361
357
 
362
358
  ```bash
363
- # Fork and clone the repository
364
359
  git clone https://github.com/YOUR_USERNAME/ebay-mcp.git
365
360
  cd ebay-mcp
366
-
367
- # Install dependencies
368
361
  npm install
369
-
370
- # Set up environment
371
- cp .env.example .env
372
- # Edit .env with your credentials
373
-
374
- # Build and test
362
+ npm run setup # Interactive setup wizard
375
363
  npm run build
376
364
  npm test
377
365
  ```
378
366
 
379
- ### Development Commands
367
+ ### Commands Reference
380
368
 
381
- ```bash
382
- npm run dev # Run STDIO server
383
- npm run dev:http # Run HTTP server
384
- npm run test # Run tests
385
- npm run test:watch # Run tests in watch mode
386
- npm run typecheck # Type-check code
387
- npm run lint # Lint code
388
- npm run format # Format code
389
- ```
369
+ | Command | Description |
370
+ | ------------------ | -------------------------------------------------- |
371
+ | `npm run build` | Compile TypeScript to JavaScript |
372
+ | `npm start` | Run the MCP server |
373
+ | `npm run dev` | Run server with hot reload |
374
+ | `npm test` | Run test suite |
375
+ | `npm run setup` | Interactive setup wizard |
376
+ | `npm run sync` | Sync specs, generate types, find missing endpoints |
377
+ | `npm run diagnose` | Check configuration and connectivity |
378
+ | `npm run check` | Run typecheck + lint + format check |
379
+ | `npm run fix` | Auto-fix lint and format issues |
390
380
 
391
- ### Docker Support
381
+ ### Adding New API Endpoints
392
382
 
393
- Run the server in a containerized environment:
383
+ When eBay releases new API endpoints, use the sync tool to identify what's missing:
394
384
 
395
385
  ```bash
396
- # Build the Docker image
397
- npm run docker:build
398
-
399
- # Start the container
400
- npm run docker:up
401
-
402
- # View logs
403
- npm run docker:logs
404
-
405
- # Stop the container
406
- npm run docker:down
407
-
408
- # Restart the container
409
- npm run docker:restart
386
+ npm run sync
410
387
  ```
411
388
 
412
- **Docker Compose Configuration:**
413
-
414
- The server can be run with Docker Compose for easy deployment:
415
-
416
- ```bash
417
- docker-compose up -d
418
- ```
389
+ This single command will:
419
390
 
420
- Environment variables should be configured in `.env` file before running Docker commands. The container will automatically use your `.env` configuration.
391
+ 1. Download latest OpenAPI specs from eBay
392
+ 2. Generate TypeScript types from specs
393
+ 3. Analyze which endpoints are implemented
394
+ 4. Report missing endpoints that need tools
421
395
 
422
- **Use Cases for Docker:**
396
+ **Workflow for adding a new endpoint:**
423
397
 
424
- - Production deployments
425
- - Consistent development environments
426
- - CI/CD pipelines
427
- - Isolated testing environments
428
-
429
- ### HTTP Server Mode
430
-
431
- In addition to the default STDIO transport for MCP clients, the server can run in HTTP mode for testing and debugging:
432
-
433
- ```bash
434
- # Development
435
- npm run dev:http
436
-
437
- # Production
438
- npm run start:http
439
- ```
440
-
441
- **HTTP Mode Features:**
442
-
443
- - RESTful API endpoints for all tools
444
- - Interactive API documentation
445
- - Useful for testing tools without an MCP client
446
- - CORS support for web applications
447
- - Helmet security headers
448
-
449
- **When to Use HTTP Mode:**
450
-
451
- - Testing individual tools during development
452
- - Building custom integrations
453
- - Debugging API responses
454
- - Creating web-based interfaces
455
-
456
- **Note:** STDIO mode (default) is recommended for MCP client integration (Claude Desktop, etc.). HTTP mode is primarily for development and custom integrations.
398
+ 1. Run `npm run sync` to identify missing endpoints
399
+ 2. Check `dev-sync-report.json` for the full list
400
+ 3. Create a new tool in `src/tools/definitions/`
401
+ 4. Add the API method in `src/api/`
402
+ 5. Write tests in `tests/`
403
+ 6. Run `npm run check && npm test`
457
404
 
458
405
  ### Project Structure
459
406
 
@@ -464,13 +411,22 @@ ebay-mcp/
464
411
  │ ├── api/ # eBay API implementations
465
412
  │ ├── auth/ # OAuth & token management
466
413
  │ ├── tools/ # MCP tool definitions
467
- │ ├── types/ # TypeScript types
468
- └── utils/ # Validation schemas
414
+ │ ├── types/ # TypeScript types (auto-generated)
415
+ ├── scripts/ # CLI tools (setup, sync, diagnose)
416
+ │ └── utils/ # Shared utilities
417
+ ├── docs/ # OpenAPI specs (auto-downloaded)
469
418
  ├── tests/ # Test suite
470
- ├── docs/ # Documentation
471
419
  └── build/ # Compiled output
472
420
  ```
473
421
 
422
+ ### Docker Support
423
+
424
+ ```bash
425
+ docker-compose up -d # Start container
426
+ docker-compose logs -f # View logs
427
+ docker-compose down # Stop container
428
+ ```
429
+
474
430
  For detailed contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
475
431
 
476
432
  ## Contributing
@@ -541,7 +497,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
541
497
  1. Verify you're using the correct environment (sandbox vs production)
542
498
  2. Ensure you have proper permissions/scopes for the operation
543
499
  3. Check eBay API status: https://developer.ebay.com/support/api-status
544
- 4. Run `npm run diagnose:export` to generate a diagnostic report
500
+ 4. Run `npm run diagnose` to check your configuration
545
501
  5. Review the [eBay API documentation](https://developer.ebay.com/docs) for endpoint requirements
546
502
 
547
503
  ### Diagnostic Tools
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Developer API - Rate limits, client registration, and signing keys
3
+ * Based on:
4
+ * - docs/sell-apps/application-settings/developer_analytics_v1_beta_oas3.json
5
+ * - docs/sell-apps/application-settings/developer_client_registration_v1_oas3.json
6
+ * - docs/sell-apps/application-settings/developer_key_management_v1_oas3.json
7
+ */
8
+ export class DeveloperApi {
9
+ client;
10
+ analyticsBasePath = '/developer/analytics/v1_beta';
11
+ clientBasePath = '/developer/client_registration/v1';
12
+ keyBasePath = '/developer/key_management/v1';
13
+ constructor(client) {
14
+ this.client = client;
15
+ }
16
+ // ========================================
17
+ // RATE LIMITS (Analytics API)
18
+ // ========================================
19
+ /**
20
+ * Get application rate limits
21
+ * Endpoint: GET /rate_limit/
22
+ */
23
+ async getRateLimits(apiContext, apiName) {
24
+ const params = {};
25
+ if (apiContext)
26
+ params.api_context = apiContext;
27
+ if (apiName)
28
+ params.api_name = apiName;
29
+ return await this.client.get(`${this.analyticsBasePath}/rate_limit/`, Object.keys(params).length > 0 ? params : undefined);
30
+ }
31
+ /**
32
+ * Get user rate limits
33
+ * Endpoint: GET /user_rate_limit/
34
+ */
35
+ async getUserRateLimits(apiContext, apiName) {
36
+ const params = {};
37
+ if (apiContext)
38
+ params.api_context = apiContext;
39
+ if (apiName)
40
+ params.api_name = apiName;
41
+ return await this.client.get(`${this.analyticsBasePath}/user_rate_limit/`, Object.keys(params).length > 0 ? params : undefined);
42
+ }
43
+ // ========================================
44
+ // CLIENT REGISTRATION
45
+ // ========================================
46
+ /**
47
+ * Register a new third party financial application with eBay
48
+ * Endpoint: POST /client/register
49
+ * Note: This is primarily for Open Banking / PSD2 compliance
50
+ */
51
+ async registerClient(clientSettings) {
52
+ return await this.client.post(`${this.clientBasePath}/client/register`, clientSettings);
53
+ }
54
+ // ========================================
55
+ // SIGNING KEY MANAGEMENT
56
+ // ========================================
57
+ /**
58
+ * Get all signing keys for the application
59
+ * Endpoint: GET /signing_key
60
+ */
61
+ async getSigningKeys() {
62
+ return await this.client.get(`${this.keyBasePath}/signing_key`);
63
+ }
64
+ /**
65
+ * Create a new signing key
66
+ * Endpoint: POST /signing_key
67
+ */
68
+ async createSigningKey(request) {
69
+ return await this.client.post(`${this.keyBasePath}/signing_key`, request || {});
70
+ }
71
+ /**
72
+ * Get a specific signing key by ID
73
+ * Endpoint: GET /signing_key/{signing_key_id}
74
+ */
75
+ async getSigningKey(signingKeyId) {
76
+ if (!signingKeyId || typeof signingKeyId !== 'string') {
77
+ throw new Error('signingKeyId is required and must be a string');
78
+ }
79
+ return await this.client.get(`${this.keyBasePath}/signing_key/${signingKeyId}`);
80
+ }
81
+ }
@@ -5,6 +5,7 @@ import { FeedbackApi } from '../api/communication/feedback.js';
5
5
  import { MessageApi } from '../api/communication/message.js';
6
6
  import { NegotiationApi } from '../api/communication/negotiation.js';
7
7
  import { NotificationApi } from '../api/communication/notification.js';
8
+ import { DeveloperApi } from '../api/developer/developer.js';
8
9
  import { InventoryApi } from '../api/listing-management/inventory.js';
9
10
  import { MetadataApi } from '../api/listing-metadata/metadata.js';
10
11
  import { TaxonomyApi } from '../api/listing-metadata/taxonomy.js';
@@ -41,6 +42,7 @@ export class EbaySellerApi {
41
42
  vero;
42
43
  translation;
43
44
  edelivery;
45
+ developer;
44
46
  constructor(config) {
45
47
  this.client = new EbayApiClient(config);
46
48
  // Initialize API category handlers
@@ -62,6 +64,7 @@ export class EbaySellerApi {
62
64
  this.vero = new VeroApi(this.client);
63
65
  this.translation = new TranslationApi(this.client);
64
66
  this.edelivery = new EDeliveryApi(this.client);
67
+ this.developer = new DeveloperApi(this.client);
65
68
  }
66
69
  /**
67
70
  * Initialize the API (load tokens from storage)
@@ -119,3 +122,4 @@ export * from '../api/other/edelivery.js';
119
122
  export * from '../api/other/identity.js';
120
123
  export * from '../api/other/translation.js';
121
124
  export * from '../api/other/vero.js';
125
+ export * from '../api/developer/developer.js';
@@ -580,7 +580,7 @@ export class InventoryApi {
580
580
  }
581
581
  }
582
582
  /**
583
- * Get listing's inventory locations
583
+ * Get listing's inventory locations (SKU location mapping)
584
584
  * Endpoint: GET /listing/{listingId}/sku/{sku}/locations
585
585
  * @throws Error if required parameters are missing or invalid
586
586
  */
@@ -598,6 +598,41 @@ export class InventoryApi {
598
598
  throw new Error(`Failed to get listing locations: ${error instanceof Error ? error.message : 'Unknown error'}`);
599
599
  }
600
600
  }
601
+ async createOrReplaceSkuLocationMapping(listingId, sku, locationMapping) {
602
+ if (!listingId || typeof listingId !== 'string') {
603
+ throw new Error('listingId is required and must be a string');
604
+ }
605
+ if (!sku || typeof sku !== 'string') {
606
+ throw new Error('sku is required and must be a string');
607
+ }
608
+ if (!locationMapping || typeof locationMapping !== 'object') {
609
+ throw new Error('locationMapping is required and must be an object');
610
+ }
611
+ try {
612
+ return await this.client.put(`${this.basePath}/listing/${listingId}/sku/${sku}/locations`, locationMapping, {
613
+ headers: {
614
+ 'Content-Type': 'application/json',
615
+ },
616
+ });
617
+ }
618
+ catch (error) {
619
+ throw new Error(`Failed to create or replace SKU location mapping: ${error instanceof Error ? error.message : 'Unknown error'}`);
620
+ }
621
+ }
622
+ async deleteSkuLocationMapping(listingId, sku) {
623
+ if (!listingId || typeof listingId !== 'string') {
624
+ throw new Error('listingId is required and must be a string');
625
+ }
626
+ if (!sku || typeof sku !== 'string') {
627
+ throw new Error('sku is required and must be a string');
628
+ }
629
+ try {
630
+ return await this.client.delete(`${this.basePath}/listing/${listingId}/sku/${sku}/locations`);
631
+ }
632
+ catch (error) {
633
+ throw new Error(`Failed to delete SKU location mapping: ${error instanceof Error ? error.message : 'Unknown error'}`);
634
+ }
635
+ }
601
636
  /**
602
637
  * Publish offer by inventory item group
603
638
  * Endpoint: POST /offer/publish_by_inventory_item_group
@@ -155,15 +155,12 @@ export class MarketingApi {
155
155
  async bulkCreateAdsByListingId(campaignId, body) {
156
156
  return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/bulk_create_ads_by_listing_id`, body);
157
157
  }
158
- /**
159
- * Bulk delete ads by inventory reference
160
- */
161
158
  async bulkDeleteAdsByInventoryReference(campaignId, body) {
162
159
  return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/bulk_delete_ads_by_inventory_reference`, body);
163
160
  }
164
- /**
165
- * Bulk delete ads by listing id
166
- */
161
+ async deleteAdsByInventoryReference(campaignId, body) {
162
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/delete_ads_by_inventory_reference`, body);
163
+ }
167
164
  async bulkDeleteAdsByListingId(campaignId, body) {
168
165
  return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/bulk_delete_ads_by_listing_id`, body);
169
166
  }
@@ -430,13 +427,13 @@ export class MarketingApi {
430
427
  * Get promotion summary (alias for getPromotionSummaryReport)
431
428
  */
432
429
  async getPromotionSummary(marketplaceId) {
433
- return this.getPromotionSummaryReport(marketplaceId);
430
+ return await this.getPromotionSummaryReport(marketplaceId);
434
431
  }
435
432
  /**
436
433
  * Get promotion reports (alias for getPromotionReport)
437
434
  */
438
435
  async getPromotionReports(marketplaceId, promotionStatus, limit, offset) {
439
- return this.getPromotionReport(marketplaceId, promotionStatus, limit, offset);
436
+ return await this.getPromotionReport(marketplaceId, promotionStatus, limit, offset);
440
437
  }
441
438
  /**
442
439
  * Get targeting for a campaign
@@ -562,4 +559,210 @@ export class MarketingApi {
562
559
  async updateNegativeKeywordForAdGroup(adGroupId, negativeKeywordId, body) {
563
560
  return await this.client.put(`${this.basePath}/ad_group/${adGroupId}/negative_keyword/${negativeKeywordId}`, body);
564
561
  }
562
+ /**
563
+ * Delete a campaign
564
+ */
565
+ async deleteCampaign(campaignId) {
566
+ return await this.client.delete(`${this.basePath}/ad_campaign/${campaignId}`);
567
+ }
568
+ /**
569
+ * Launch a campaign
570
+ */
571
+ async launchCampaign(campaignId) {
572
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/launch`, {});
573
+ }
574
+ /**
575
+ * Find campaign by ad reference
576
+ */
577
+ async findCampaignByAdReference(inventoryReferenceId, inventoryReferenceType, listingId) {
578
+ const params = {};
579
+ if (inventoryReferenceId)
580
+ params.inventory_reference_id = inventoryReferenceId;
581
+ if (inventoryReferenceType)
582
+ params.inventory_reference_type = inventoryReferenceType;
583
+ if (listingId)
584
+ params.listing_id = listingId;
585
+ return await this.client.get(`${this.basePath}/ad_campaign/find_campaign_by_ad_reference`, params);
586
+ }
587
+ /**
588
+ * Setup quick campaign
589
+ */
590
+ async setupQuickCampaign(body) {
591
+ return await this.client.post(`${this.basePath}/ad_campaign/setup_quick_campaign`, body);
592
+ }
593
+ /**
594
+ * Suggest budget for a campaign
595
+ */
596
+ async suggestBudget(campaignId) {
597
+ const params = {};
598
+ if (campaignId)
599
+ params.campaign_id = campaignId;
600
+ return await this.client.get(`${this.basePath}/ad_campaign/suggest_budget`, params);
601
+ }
602
+ /**
603
+ * Suggest items for a campaign
604
+ */
605
+ async suggestItems(campaignId) {
606
+ return await this.client.get(`${this.basePath}/ad_campaign/${campaignId}/suggest_items`);
607
+ }
608
+ /**
609
+ * Suggest max CPC for ads
610
+ */
611
+ async suggestMaxCpc(body) {
612
+ return await this.client.post(`${this.basePath}/ad_campaign/suggest_max_cpc`, body);
613
+ }
614
+ /**
615
+ * Update ad rate strategy for a campaign
616
+ */
617
+ async updateAdRateStrategy(campaignId, body) {
618
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/update_ad_rate_strategy`, body);
619
+ }
620
+ /**
621
+ * Update bidding strategy for a campaign
622
+ */
623
+ async updateBiddingStrategy(campaignId, body) {
624
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/update_bidding_strategy`, body);
625
+ }
626
+ /**
627
+ * Update campaign budget
628
+ */
629
+ async updateCampaignBudget(campaignId, body) {
630
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/update_campaign_budget`, body);
631
+ }
632
+ /**
633
+ * Update an ad group
634
+ */
635
+ async updateAdGroup(campaignId, adGroupId, body) {
636
+ return await this.client.put(`${this.basePath}/ad_campaign/${campaignId}/ad_group/${adGroupId}`, body);
637
+ }
638
+ /**
639
+ * Update a keyword
640
+ */
641
+ async updateKeyword(campaignId, keywordId, body) {
642
+ return await this.client.put(`${this.basePath}/ad_campaign/${campaignId}/keyword/${keywordId}`, body);
643
+ }
644
+ /**
645
+ * Bulk create keywords (campaign level)
646
+ */
647
+ async bulkCreateKeyword(campaignId, body) {
648
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/bulk_create_keyword`, body);
649
+ }
650
+ /**
651
+ * Bulk update keywords (campaign level)
652
+ */
653
+ async bulkUpdateKeyword(campaignId, body) {
654
+ return await this.client.post(`${this.basePath}/ad_campaign/${campaignId}/bulk_update_keyword`, body);
655
+ }
656
+ /**
657
+ * Get a report by ID
658
+ */
659
+ async getReport(reportId) {
660
+ return await this.client.get(`${this.basePath}/ad_report/${reportId}`);
661
+ }
662
+ /**
663
+ * Delete a report task
664
+ */
665
+ async deleteReportTask(reportTaskId) {
666
+ return await this.client.delete(`${this.basePath}/ad_report_task/${reportTaskId}`);
667
+ }
668
+ /**
669
+ * Create an item price markdown promotion
670
+ */
671
+ async createItemPriceMarkdownPromotion(body) {
672
+ return await this.client.post(`${this.basePath}/item_price_markdown`, body);
673
+ }
674
+ /**
675
+ * Get an item price markdown promotion
676
+ */
677
+ async getItemPriceMarkdownPromotion(promotionId) {
678
+ return await this.client.get(`${this.basePath}/item_price_markdown/${promotionId}`);
679
+ }
680
+ /**
681
+ * Update an item price markdown promotion
682
+ */
683
+ async updateItemPriceMarkdownPromotion(promotionId, body) {
684
+ return await this.client.put(`${this.basePath}/item_price_markdown/${promotionId}`, body);
685
+ }
686
+ /**
687
+ * Delete an item price markdown promotion
688
+ */
689
+ async deleteItemPriceMarkdownPromotion(promotionId) {
690
+ return await this.client.delete(`${this.basePath}/item_price_markdown/${promotionId}`);
691
+ }
692
+ /**
693
+ * Get listing set for a promotion
694
+ */
695
+ async getListingSet(promotionId) {
696
+ return await this.client.get(`${this.basePath}/promotion/${promotionId}/get_listing_set`);
697
+ }
698
+ /**
699
+ * Pause a promotion
700
+ */
701
+ async pausePromotion(promotionId) {
702
+ return await this.client.post(`${this.basePath}/promotion/${promotionId}/pause`, {});
703
+ }
704
+ /**
705
+ * Resume a promotion
706
+ */
707
+ async resumePromotion(promotionId) {
708
+ return await this.client.post(`${this.basePath}/promotion/${promotionId}/resume`, {});
709
+ }
710
+ /**
711
+ * Get email campaigns
712
+ */
713
+ async getEmailCampaigns(limit, offset) {
714
+ const params = {};
715
+ if (limit)
716
+ params.limit = limit;
717
+ if (offset)
718
+ params.offset = offset;
719
+ return await this.client.get(`${this.basePath}/email_campaign`, params);
720
+ }
721
+ /**
722
+ * Create an email campaign
723
+ */
724
+ async createEmailCampaign(body) {
725
+ return await this.client.post(`${this.basePath}/email_campaign`, body);
726
+ }
727
+ /**
728
+ * Get an email campaign
729
+ */
730
+ async getEmailCampaign(emailCampaignId) {
731
+ return await this.client.get(`${this.basePath}/email_campaign/${emailCampaignId}`);
732
+ }
733
+ /**
734
+ * Update an email campaign
735
+ */
736
+ async updateEmailCampaign(emailCampaignId, body) {
737
+ return await this.client.put(`${this.basePath}/email_campaign/${emailCampaignId}`, body);
738
+ }
739
+ /**
740
+ * Delete an email campaign
741
+ */
742
+ async deleteEmailCampaign(emailCampaignId) {
743
+ return await this.client.delete(`${this.basePath}/email_campaign/${emailCampaignId}`);
744
+ }
745
+ /**
746
+ * Get email campaign audiences
747
+ */
748
+ async getAudiences() {
749
+ return await this.client.get(`${this.basePath}/email_campaign/audience`);
750
+ }
751
+ /**
752
+ * Get email preview for a campaign
753
+ */
754
+ async getEmailPreview(emailCampaignId) {
755
+ return await this.client.get(`${this.basePath}/email_campaign/${emailCampaignId}/email_preview`);
756
+ }
757
+ /**
758
+ * Get email campaign report
759
+ */
760
+ async getEmailReport(limit, offset) {
761
+ const params = {};
762
+ if (limit)
763
+ params.limit = limit;
764
+ if (offset)
765
+ params.offset = offset;
766
+ return await this.client.get(`${this.basePath}/email_campaign/report`, params);
767
+ }
565
768
  }
@@ -160,8 +160,8 @@ export class EbayOAuthClient {
160
160
  userAccessToken: accessToken,
161
161
  userRefreshToken: refreshToken,
162
162
  tokenType: 'Bearer',
163
- userAccessTokenExpiry: accessTokenExpiry ?? (now + 7200 * 1000), // Default 2 hours
164
- userRefreshTokenExpiry: refreshTokenExpiry ?? (now + 18 * 30 * 24 * 60 * 60 * 1000), // Default 18 months
163
+ userAccessTokenExpiry: accessTokenExpiry ?? now + 7200 * 1000, // Default 2 hours
164
+ userRefreshTokenExpiry: refreshTokenExpiry ?? now + 18 * 30 * 24 * 60 * 60 * 1000, // Default 18 months
165
165
  };
166
166
  // Update .env file with new tokens
167
167
  updateEnvFile({
@@ -303,7 +303,8 @@ export class EbayOAuthClient {
303
303
  EBAY_USER_ACCESS_TOKEN: tokenData.access_token,
304
304
  };
305
305
  // If eBay provided a new refresh token, update it too
306
- if (tokenData.refresh_token && tokenData.refresh_token !== process.env.EBAY_USER_REFRESH_TOKEN) {
306
+ if (tokenData.refresh_token &&
307
+ tokenData.refresh_token !== process.env.EBAY_USER_REFRESH_TOKEN) {
307
308
  envUpdates.EBAY_USER_REFRESH_TOKEN = tokenData.refresh_token;
308
309
  // New refresh token updated silently
309
310
  }