ebay-mcp 1.17.0 → 1.18.2

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.
Files changed (178) hide show
  1. package/README.md +92 -53
  2. package/build/api/account-management/accountV2.js +347 -0
  3. package/build/api/account-management/finances.js +289 -0
  4. package/build/api/analytics-and-report/analytics.js +1 -1
  5. package/build/api/{client.js → client/ebayApiClient.js} +83 -36
  6. package/build/api/{clientTrading.js → client/tradingApiClient.js} +22 -22
  7. package/build/api/communication/feedback.js +21 -21
  8. package/build/api/communication/message.js +23 -23
  9. package/build/api/communication/negotiation.js +7 -7
  10. package/build/api/communication/notification.js +52 -52
  11. package/build/api/developer/developer.js +3 -3
  12. package/build/api/{index.js → ebaySellerApi.js} +28 -4
  13. package/build/api/listing-management/feed.js +91 -0
  14. package/build/api/listing-management/feedReportTasks.js +107 -0
  15. package/build/api/listing-management/feedRequest.js +91 -0
  16. package/build/api/listing-management/feedSchedules.js +95 -0
  17. package/build/api/listing-management/feedTasks.js +91 -0
  18. package/build/api/listing-management/items.js +22 -22
  19. package/build/api/listing-management/locations.js +4 -4
  20. package/build/api/listing-management/media.js +14 -14
  21. package/build/api/listing-management/mediaAttach.js +14 -14
  22. package/build/api/listing-management/mediaResources.js +22 -39
  23. package/build/api/listing-management/offerFormat.js +1 -1
  24. package/build/api/listing-management/offers.js +23 -23
  25. package/build/api/listing-management/stores.js +203 -0
  26. package/build/api/listing-metadata/charity.js +76 -0
  27. package/build/api/listing-metadata/metadata.js +31 -11
  28. package/build/api/listing-metadata/metadataShipping.js +117 -0
  29. package/build/api/listing-metadata/taxonomy.js +52 -7
  30. package/build/api/marketing-and-promotions/ads.js +34 -34
  31. package/build/api/marketing-and-promotions/campaigns.js +21 -21
  32. package/build/api/marketing-and-promotions/promotions.js +24 -24
  33. package/build/api/marketing-and-promotions/reports.js +7 -7
  34. package/build/api/marketing-and-promotions/shared.js +0 -3
  35. package/build/api/order-management/dispute.js +11 -9
  36. package/build/api/order-management/fulfillment.js +4 -4
  37. package/build/api/order-management/logistics.js +150 -0
  38. package/build/api/other/browseMappers.js +15 -13
  39. package/build/api/other/browseValidators.js +4 -4
  40. package/build/api/other/edelivery.js +19 -19
  41. package/build/api/other/finding.js +13 -13
  42. package/build/api/other/translation.js +2 -2
  43. package/build/api/other/vero.js +2 -2
  44. package/build/api/shared/download.js +79 -0
  45. package/build/api/shared/location.js +33 -0
  46. package/build/api/shared/request.js +69 -11
  47. package/build/api/trading/listingFormat.js +37 -30
  48. package/build/api/trading/trading.js +33 -33
  49. package/build/auth/credentialSession.js +5 -5
  50. package/build/auth/oauth.js +3 -3
  51. package/build/auth/oauthHelper.js +24 -24
  52. package/build/auth/oauthMetadata.js +4 -4
  53. package/build/auth/oauthMiddleware.js +16 -16
  54. package/build/auth/scopeUtils.js +24 -3
  55. package/build/auth/tokenVerifier.js +23 -23
  56. package/build/config/environment.js +10 -45
  57. package/build/config/oauthScopes.js +132 -0
  58. package/build/config/toolFamilies.js +4 -0
  59. package/build/index.js +5 -5
  60. package/build/mcp/httpTransport.js +32 -20
  61. package/build/mcp/runtime.js +12 -12
  62. package/build/mcp/toolGating.js +6 -12
  63. package/build/mcp/uiBridge.js +3 -3
  64. package/build/schemas/account-management/account.js +2 -52
  65. package/build/schemas/account-management/accountV2.js +294 -0
  66. package/build/schemas/account-management/finances.js +143 -0
  67. package/build/schemas/analytics/analytics.js +2 -48
  68. package/build/schemas/communication/messages.js +2 -46
  69. package/build/schemas/developer/developer.js +1 -27
  70. package/build/schemas/fulfillment/logistics.js +115 -0
  71. package/build/schemas/fulfillment/orders.js +2 -47
  72. package/build/schemas/inventory-management/feed.js +217 -0
  73. package/build/schemas/inventory-management/inventory.js +2 -50
  74. package/build/schemas/inventory-management/media.js +2 -2
  75. package/build/schemas/inventory-management/mediaDocuments.js +1 -1
  76. package/build/schemas/inventory-management/stores.js +42 -0
  77. package/build/schemas/marketing/ads.js +2 -2
  78. package/build/schemas/marketing/campaigns.js +2 -2
  79. package/build/schemas/marketing/common.js +2 -2
  80. package/build/schemas/marketing/marketing.js +0 -1
  81. package/build/schemas/marketing/promotions.js +2 -2
  82. package/build/schemas/marketing/reports.js +2 -2
  83. package/build/schemas/metadata/metadata.js +2 -90
  84. package/build/schemas/metadata/shipping.js +16 -0
  85. package/build/schemas/other/browse.js +2 -2
  86. package/build/schemas/other/finding.js +2 -2
  87. package/build/schemas/other/otherApis.js +2 -114
  88. package/build/schemas/taxonomy/categoryTree.js +8 -0
  89. package/build/schemas/taxonomy/charity.js +41 -0
  90. package/build/schemas/taxonomy/taxonomy.js +2 -69
  91. package/build/scripts/autoSetup.js +3 -3
  92. package/build/scripts/{buildUi.js → buildMcpApps.js} +13 -13
  93. package/build/scripts/devSync.js +34 -70
  94. package/build/scripts/diagnostics.js +15 -16
  95. package/build/scripts/interactiveSetup.js +25 -25
  96. package/build/scripts/scopeHelper.js +2 -2
  97. package/build/scripts/securityChecker.js +20 -20
  98. package/build/scripts/setup.js +76 -76
  99. package/build/scripts/setupShared.js +5 -30
  100. package/build/scripts/setupValidator.js +9 -9
  101. package/build/scripts/setupWizard.js +23 -23
  102. package/build/scripts/skills.js +14 -17
  103. package/build/scripts/specRefs.js +55 -0
  104. package/build/scripts/specSources.js +144 -0
  105. package/build/serverHttp.js +4 -7
  106. package/build/skills/content.js +13 -13
  107. package/build/skills/install.js +9 -9
  108. package/build/skills/registrySnapshot.js +7 -3
  109. package/build/skills/render.js +2 -2
  110. package/build/tools/categories/accountV2.js +106 -0
  111. package/build/tools/categories/charity.js +21 -0
  112. package/build/tools/categories/communication.js +1 -1
  113. package/build/tools/categories/connector.js +19 -16
  114. package/build/tools/categories/feed.js +183 -0
  115. package/build/tools/categories/finances.js +86 -0
  116. package/build/tools/categories/fulfillment.js +4 -4
  117. package/build/tools/categories/index.js +12 -2
  118. package/build/tools/categories/inventory.js +1 -1
  119. package/build/tools/categories/logistics.js +52 -0
  120. package/build/tools/categories/metadata.js +38 -1
  121. package/build/tools/categories/stores.js +64 -0
  122. package/build/tools/categories/taxonomy.js +18 -1
  123. package/build/tools/categories/tokenManagement.js +8 -8
  124. package/build/tools/contracts.js +2 -2
  125. package/build/tools/defineTool.js +27 -12
  126. package/build/tools/fileResult.js +30 -0
  127. package/build/tools/registry.js +2 -2
  128. package/build/tools/schemas.js +2 -2
  129. package/build/tools/ui/mapHelpers.js +12 -12
  130. package/build/tools/ui/maps.js +101 -97
  131. package/build/tools/ui/viewModels.js +2 -2
  132. package/build/utils/apiStatusFeed.js +12 -12
  133. package/build/utils/communication/feedback.js +3 -3
  134. package/build/utils/communication/message.js +3 -3
  135. package/build/utils/communication/negotiation.js +3 -3
  136. package/build/utils/communication/notification.js +3 -3
  137. package/build/utils/entryModule.js +33 -0
  138. package/build/utils/envFile.js +47 -0
  139. package/build/utils/envParser.js +5 -5
  140. package/build/utils/errors.js +4 -4
  141. package/build/utils/http.js +60 -41
  142. package/build/utils/localFeedFiles.js +61 -0
  143. package/build/utils/localMedia.js +18 -6
  144. package/build/utils/logRedaction.js +21 -0
  145. package/build/utils/logger.js +52 -33
  146. package/build/utils/other/compliance.js +3 -3
  147. package/build/utils/other/identity.js +3 -3
  148. package/build/utils/other/translation.js +3 -3
  149. package/build/utils/other/vero.js +3 -3
  150. package/build/utils/schemaHelpers.js +2 -2
  151. package/build/utils/tokenUtils.js +2 -2
  152. package/build/utils/trading/trading.js +1 -1
  153. package/build/utils/version.js +1 -1
  154. package/build/utils/zodIssues.js +14 -0
  155. package/package.json +9 -12
  156. package/README.de.md +0 -422
  157. package/README.es.md +0 -422
  158. package/README.fr.md +0 -422
  159. package/README.ja.md +0 -422
  160. package/README.ko.md +0 -422
  161. package/README.pt-BR.md +0 -422
  162. package/README.ru.md +0 -422
  163. package/README.zh-CN.md +0 -422
  164. package/build/schemas/index.js +0 -76
  165. package/build/schemas/marketing/jsonSchemas.js +0 -118
  166. package/build/scripts/downloadSpecs.js +0 -117
  167. package/build/scripts/updateApiStatusDoc.js +0 -71
  168. package/build/utils/effectSchema.js +0 -203
  169. package/build/utils/effectSchemaAdapter.js +0 -121
  170. package/build/utils/effectSchemaChainMethods.js +0 -146
  171. package/build/utils/effectSchemaRefinements.js +0 -104
  172. package/build/utils/effectSchemaStringMethods.js +0 -63
  173. package/build/utils/effectSchemaTypes.js +0 -47
  174. package/docs/auth/production_scopes.json +0 -111
  175. package/docs/auth/sandbox_scopes.json +0 -142
  176. /package/build/api/{clientRequestError.js → client/ebayClientRequestError.js} +0 -0
  177. /package/build/api/{rateLimitTracker.js → client/rateLimitTracker.js} +0 -0
  178. /package/build/api/{clientTradingError.js → client/tradingApiFailure.js} +0 -0
@@ -0,0 +1,150 @@
1
+ import { requestDownloadEffect, } from '../../api/shared/download.js';
2
+ import { optionalStringEffect, requestGetEffect, requestPostEffect, requireObjectEffect, requireStringEffect, } from '../../api/shared/request.js';
3
+ import { Effect } from 'effect';
4
+ /**
5
+ * Builds the per-call marketplace header; without an override the client's configured
6
+ * `EBAY_MARKETPLACE_ID` header still satisfies eBay's required `X-EBAY-C-MARKETPLACE-ID`.
7
+ */
8
+ const marketplaceConfig = (marketplaceId) => marketplaceId === undefined
9
+ ? undefined
10
+ : { headers: { 'X-EBAY-C-MARKETPLACE-ID': marketplaceId } };
11
+ /**
12
+ * Logistics API - shipping quotes, shipments, and shipping labels (limited release; needs the
13
+ * `sell.logistics` scope). Based on: sell_logistics_v1_oas3.json
14
+ */
15
+ export class LogisticsApi {
16
+ basePath = '/sell/logistics/v1_beta';
17
+ client;
18
+ constructor(client) {
19
+ this.client = client;
20
+ }
21
+ /**
22
+ * Creates a shipping quote with live USPS rates for one package.
23
+ *
24
+ * @param input - Generated ShippingQuoteRequest body plus optional marketplace header override.
25
+ * @returns An Effect that succeeds with eBay's generated ShippingQuote, including its rates.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const quote = await Effect.runPromise(
30
+ * logisticsApi.createShippingQuote({ shippingQuoteRequest, marketplaceId: 'EBAY_US' }),
31
+ * );
32
+ * ```
33
+ *
34
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipping_quote/methods/createShippingQuote
35
+ */
36
+ createShippingQuote = (input) => {
37
+ const client = this.client;
38
+ const path = `${this.basePath}/shipping_quote`;
39
+ return Effect.gen(function* () {
40
+ const endpointInput = yield* requireObjectEffect(input, 'input');
41
+ const shippingQuoteRequest = yield* requireObjectEffect(endpointInput.shippingQuoteRequest, 'shippingQuoteRequest');
42
+ const marketplaceId = yield* optionalStringEffect(endpointInput.marketplaceId, 'marketplaceId');
43
+ return yield* requestPostEffect(client, path, shippingQuoteRequest, marketplaceConfig(marketplaceId));
44
+ });
45
+ };
46
+ /**
47
+ * Retrieves a shipping quote and its rates by ID.
48
+ *
49
+ * @param input - Shipping quote identifier returned by createShippingQuote.
50
+ * @returns An Effect that succeeds with eBay's generated ShippingQuote.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const quote = await Effect.runPromise(
55
+ * logisticsApi.getShippingQuote({ shippingQuoteId: 'QUOTE-1' }),
56
+ * );
57
+ * ```
58
+ *
59
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipping_quote/methods/getShippingQuote
60
+ */
61
+ getShippingQuote = (input) => {
62
+ const client = this.client;
63
+ const basePath = this.basePath;
64
+ return Effect.gen(function* () {
65
+ const endpointInput = yield* requireObjectEffect(input, 'input');
66
+ const shippingQuoteId = yield* requireStringEffect(endpointInput.shippingQuoteId, 'shippingQuoteId');
67
+ return yield* requestGetEffect(client, `${basePath}/shipping_quote/${encodeURIComponent(shippingQuoteId)}`);
68
+ });
69
+ };
70
+ /**
71
+ * Purchases a shipping label by creating a shipment from one rate of a shipping quote. The
72
+ * seller's billing agreement is charged the rate's base cost plus any added options.
73
+ *
74
+ * @param input - Generated CreateShipmentFromQuoteRequest body plus optional marketplace header.
75
+ * @returns An Effect that succeeds with eBay's generated Shipment, including its shipmentId.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const shipment = await Effect.runPromise(
80
+ * logisticsApi.createFromShippingQuote({
81
+ * shipmentRequest: { shippingQuoteId: 'QUOTE-1', rateId: 'RATE-1' },
82
+ * }),
83
+ * );
84
+ * ```
85
+ *
86
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipment/methods/createFromShippingQuote
87
+ */
88
+ createFromShippingQuote = (input) => {
89
+ const client = this.client;
90
+ const path = `${this.basePath}/shipment/create_from_shipping_quote`;
91
+ return Effect.gen(function* () {
92
+ const endpointInput = yield* requireObjectEffect(input, 'input');
93
+ const shipmentRequest = yield* requireObjectEffect(endpointInput.shipmentRequest, 'shipmentRequest');
94
+ yield* requireStringEffect(shipmentRequest.shippingQuoteId, 'shipmentRequest.shippingQuoteId');
95
+ yield* requireStringEffect(shipmentRequest.rateId, 'shipmentRequest.rateId');
96
+ const marketplaceId = yield* optionalStringEffect(endpointInput.marketplaceId, 'marketplaceId');
97
+ return yield* requestPostEffect(client, path, shipmentRequest, marketplaceConfig(marketplaceId));
98
+ });
99
+ };
100
+ /**
101
+ * Retrieves a shipment by ID.
102
+ *
103
+ * @param input - Shipment identifier returned by createFromShippingQuote.
104
+ * @returns An Effect that succeeds with eBay's generated Shipment.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * const shipment = await Effect.runPromise(logisticsApi.getShipment({ shipmentId: 'SHIP-1' }));
109
+ * ```
110
+ *
111
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipment/methods/getShipment
112
+ */
113
+ getShipment = (input) => this.shipmentPath(input, '').pipe(Effect.flatMap((path) => requestGetEffect(this.client, path)));
114
+ /**
115
+ * Cancels a shipment and deletes its shipping label; eBay refunds the total shipping cost.
116
+ * A shipment whose label was already used cannot be canceled.
117
+ *
118
+ * @param input - Shipment identifier returned by createFromShippingQuote.
119
+ * @returns An Effect that succeeds with eBay's generated Shipment and its cancellation status.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * const canceled = await Effect.runPromise(
124
+ * logisticsApi.cancelShipment({ shipmentId: 'SHIP-1' }),
125
+ * );
126
+ * ```
127
+ *
128
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipment/methods/cancelShipment
129
+ */
130
+ cancelShipment = (input) => this.shipmentPath(input, '/cancel').pipe(Effect.flatMap((path) => requestPostEffect(this.client, path)));
131
+ /**
132
+ * Downloads the shipping label generated for a shipment as a PDF.
133
+ *
134
+ * @param input - Shipment identifier returned by createFromShippingQuote.
135
+ * @returns An Effect that succeeds with the label bytes, content type, and file name; fails
136
+ * with DownloadTooLargeError when the file is too large to return inline over MCP.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const label = await Effect.runPromise(
141
+ * logisticsApi.downloadLabelFile({ shipmentId: 'SHIP-1' }),
142
+ * );
143
+ * ```
144
+ *
145
+ * @see https://developer.ebay.com/api-docs/sell/logistics/resources/shipment/methods/downloadLabelFile
146
+ */
147
+ downloadLabelFile = (input) => this.shipmentPath(input, '/download_label_file').pipe(Effect.flatMap((path) => requestDownloadEffect(this.client, path, { headers: { Accept: 'application/pdf' } })));
148
+ /** Validates a shipment ID and builds its URL-encoded resource path plus `suffix`. */
149
+ shipmentPath = (input, suffix) => requireObjectEffect(input, 'input').pipe(Effect.flatMap((endpointInput) => requireStringEffect(endpointInput.shipmentId, 'shipmentId')), Effect.map((shipmentId) => `${this.basePath}/shipment/${encodeURIComponent(shipmentId)}${suffix}`));
150
+ }
@@ -6,16 +6,16 @@
6
6
  import { defined } from '../../utils/objects.js';
7
7
  import { isRecord } from '../../utils/typeGuards.js';
8
8
  /** Read a Browse money field ({ value, currency }). */
9
- const parseBrowseMoney = (value) => {
10
- if (!isRecord(value)) {
9
+ const parseBrowseMoney = (rawMoney) => {
10
+ if (!isRecord(rawMoney)) {
11
11
  return;
12
12
  }
13
- if (typeof value.value === 'string' && typeof value.currency === 'string') {
14
- return { currency: value.currency, value: value.value };
13
+ if (typeof rawMoney.value === 'string' && typeof rawMoney.currency === 'string') {
14
+ return { currency: rawMoney.currency, value: rawMoney.value };
15
15
  }
16
16
  };
17
17
  /** Read a string property when present and non-empty. */
18
- const optionalString = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
18
+ const optionalString = (rawField) => typeof rawField === 'string' && rawField.length > 0 ? rawField : undefined;
19
19
  /** Read a string array property when present. */
20
20
  /**
21
21
  * Pick the availability container out of a Browse item payload.
@@ -23,16 +23,18 @@ const optionalString = (value) => typeof value === 'string' && value.length > 0
23
23
  * eBay returns `estimatedAvailabilities` as either a bare object or a
24
24
  * single-element array, depending on the listing.
25
25
  */
26
- const pickAvailabilityContainer = (value) => {
27
- if (isRecord(value)) {
28
- return value;
26
+ const pickAvailabilityContainer = (estimatedAvailabilities) => {
27
+ if (isRecord(estimatedAvailabilities)) {
28
+ return estimatedAvailabilities;
29
29
  }
30
- if (Array.isArray(value) && isRecord(value[0])) {
31
- return value[0];
30
+ if (Array.isArray(estimatedAvailabilities) && isRecord(estimatedAvailabilities[0])) {
31
+ return estimatedAvailabilities[0];
32
32
  }
33
33
  };
34
- const optionalStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === 'string') && value.length > 0
35
- ? value
34
+ const optionalStringArray = (rawField) => Array.isArray(rawField) &&
35
+ rawField.every((entry) => typeof entry === 'string') &&
36
+ rawField.length > 0
37
+ ? rawField
36
38
  : undefined;
37
39
  /** Seller username and feedback score, both optional on a Browse payload. */
38
40
  const pickSeller = (raw) => {
@@ -118,7 +120,7 @@ export const mapItemSummary = (raw) => {
118
120
  *
119
121
  * @example
120
122
  * ```ts
121
- * const result = mapSearchActiveItemsResponse(rawJson, { query: 'gpu', offset: 0, limit: 20 });
123
+ * const searchPage = mapSearchActiveItemsResponse(rawJson, { query: 'gpu', offset: 0, limit: 20 });
122
124
  * ```
123
125
  */
124
126
  export const mapSearchActiveItemsResponse = (raw, context) => {
@@ -45,17 +45,17 @@ const requireIntegerInRange = (value, parameter, min, max) => {
45
45
  * are legitimately fractional) closes the gap the MCP schema's `.min(0)`
46
46
  * already covers.
47
47
  *
48
- * @param value - Optional price bound already validated as a number >= 0.
48
+ * @param priceBound - Optional price bound already validated as a number >= 0.
49
49
  * @param parameter - Parameter name used in the tagged error.
50
50
  * @returns A tagged input error when the bound is not finite, else undefined.
51
51
  */
52
- const nonFinitePriceError = (value, parameter) => {
53
- if (value === undefined || Number.isFinite(value)) {
52
+ const nonFinitePriceError = (priceBound, parameter) => {
53
+ if (priceBound === undefined || Number.isFinite(priceBound)) {
54
54
  return;
55
55
  }
56
56
  return new EndpointInputError({
57
57
  parameter,
58
- message: `${parameter} must be a finite number; got ${value}`,
58
+ message: `${parameter} must be a finite number; got ${priceBound}`,
59
59
  });
60
60
  };
61
61
  /** Default page size when limit is omitted. */
@@ -75,8 +75,8 @@ export class EDeliveryApi {
75
75
  const path = `${this.basePath}/address_preference`;
76
76
  return Effect.gen(function* () {
77
77
  const validatedInput = yield* requireObjectEffect(input, 'input');
78
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
79
- return yield* requestPostEffect(client, path, body);
78
+ const addressPreference = yield* requireObjectEffect(validatedInput.body, 'body');
79
+ return yield* requestPostEffect(client, path, addressPreference);
80
80
  });
81
81
  };
82
82
  /**
@@ -116,8 +116,8 @@ export class EDeliveryApi {
116
116
  const path = `${this.basePath}/consign_preference`;
117
117
  return Effect.gen(function* () {
118
118
  const validatedInput = yield* requireObjectEffect(input, 'input');
119
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
120
- return yield* requestPostEffect(client, path, body);
119
+ const consignPreference = yield* requireObjectEffect(validatedInput.body, 'body');
120
+ return yield* requestPostEffect(client, path, consignPreference);
121
121
  });
122
122
  };
123
123
  /**
@@ -250,8 +250,8 @@ export class EDeliveryApi {
250
250
  const path = `${this.basePath}/bundle`;
251
251
  return Effect.gen(function* () {
252
252
  const validatedInput = yield* requireObjectEffect(input, 'input');
253
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
254
- return yield* requestPostEffect(client, path, body);
253
+ const bundleRequest = yield* requireObjectEffect(validatedInput.body, 'body');
254
+ return yield* requestPostEffect(client, path, bundleRequest);
255
255
  });
256
256
  };
257
257
  /**
@@ -340,8 +340,8 @@ export class EDeliveryApi {
340
340
  const path = `${this.basePath}/package`;
341
341
  return Effect.gen(function* () {
342
342
  const validatedInput = yield* requireObjectEffect(input, 'input');
343
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
344
- return yield* requestPostEffect(client, path, body);
343
+ const packageRequest = yield* requireObjectEffect(validatedInput.body, 'body');
344
+ return yield* requestPostEffect(client, path, packageRequest);
345
345
  });
346
346
  };
347
347
  /**
@@ -488,7 +488,7 @@ export class EDeliveryApi {
488
488
  *
489
489
  * @example
490
490
  * ```ts
491
- * const response = await Effect.runPromise(
491
+ * const cancelledPackages = await Effect.runPromise(
492
492
  * edeliveryApi.bulkCancelPackages({ body: { requests: { packageIds: 'PKG123' } } }),
493
493
  * );
494
494
  * ```
@@ -500,8 +500,8 @@ export class EDeliveryApi {
500
500
  const path = `${this.basePath}/package/bulk_cancel_packages`;
501
501
  return Effect.gen(function* () {
502
502
  const validatedInput = yield* requireObjectEffect(input, 'input');
503
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
504
- return yield* requestPostEffect(client, path, body);
503
+ const cancelPackagesRequest = yield* requireObjectEffect(validatedInput.body, 'body');
504
+ return yield* requestPostEffect(client, path, cancelPackagesRequest);
505
505
  });
506
506
  };
507
507
  /**
@@ -512,7 +512,7 @@ export class EDeliveryApi {
512
512
  *
513
513
  * @example
514
514
  * ```ts
515
- * const response = await Effect.runPromise(
515
+ * const confirmedPackages = await Effect.runPromise(
516
516
  * edeliveryApi.bulkConfirmPackages({ body: { requests: { packageIds: 'PKG123' } } }),
517
517
  * );
518
518
  * ```
@@ -524,8 +524,8 @@ export class EDeliveryApi {
524
524
  const path = `${this.basePath}/package/bulk_confirm_packages`;
525
525
  return Effect.gen(function* () {
526
526
  const validatedInput = yield* requireObjectEffect(input, 'input');
527
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
528
- return yield* requestPostEffect(client, path, body);
527
+ const confirmPackagesRequest = yield* requireObjectEffect(validatedInput.body, 'body');
528
+ return yield* requestPostEffect(client, path, confirmPackagesRequest);
529
529
  });
530
530
  };
531
531
  /**
@@ -536,7 +536,7 @@ export class EDeliveryApi {
536
536
  *
537
537
  * @example
538
538
  * ```ts
539
- * const response = await Effect.runPromise(
539
+ * const deletedPackages = await Effect.runPromise(
540
540
  * edeliveryApi.bulkDeletePackages({ body: { requests: { packageIds: 'PKG123' } } }),
541
541
  * );
542
542
  * ```
@@ -548,8 +548,8 @@ export class EDeliveryApi {
548
548
  const path = `${this.basePath}/package/bulk_delete_packages`;
549
549
  return Effect.gen(function* () {
550
550
  const validatedInput = yield* requireObjectEffect(input, 'input');
551
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
552
- return yield* requestPostEffect(client, path, body);
551
+ const deletePackagesRequest = yield* requireObjectEffect(validatedInput.body, 'body');
552
+ return yield* requestPostEffect(client, path, deletePackagesRequest);
553
553
  });
554
554
  };
555
555
  /**
@@ -657,8 +657,8 @@ export class EDeliveryApi {
657
657
  const path = `${this.basePath}/complaint`;
658
658
  return Effect.gen(function* () {
659
659
  const validatedInput = yield* requireObjectEffect(input, 'input');
660
- const body = yield* requireObjectEffect(validatedInput.body, 'body');
661
- return yield* requestPostEffect(client, path, body);
660
+ const complaintRequest = yield* requireObjectEffect(validatedInput.body, 'body');
661
+ return yield* requestPostEffect(client, path, complaintRequest);
662
662
  });
663
663
  };
664
664
  }
@@ -72,26 +72,26 @@ export const buildFindCompletedItemsParams = (input) => {
72
72
  return params;
73
73
  };
74
74
  /** Read the first string from a Finding JSON array-or-string field. */
75
- const firstString = (value) => {
76
- if (typeof value === 'string' && value.length > 0) {
77
- return value;
75
+ const firstString = (rawField) => {
76
+ if (typeof rawField === 'string' && rawField.length > 0) {
77
+ return rawField;
78
78
  }
79
- if (Array.isArray(value) && typeof value[0] === 'string' && value[0].length > 0) {
80
- return value[0];
79
+ if (Array.isArray(rawField) && typeof rawField[0] === 'string' && rawField[0].length > 0) {
80
+ return rawField[0];
81
81
  }
82
82
  };
83
83
  /** Read the first object from a Finding JSON array-or-object field. */
84
- const firstRecord = (value) => {
85
- if (isRecord(value)) {
86
- return value;
84
+ const firstRecord = (rawField) => {
85
+ if (isRecord(rawField)) {
86
+ return rawField;
87
87
  }
88
- if (Array.isArray(value) && isRecord(value[0])) {
89
- return value[0];
88
+ if (Array.isArray(rawField) && isRecord(rawField[0])) {
89
+ return rawField[0];
90
90
  }
91
91
  };
92
92
  /** Parse a Finding API money field (`@currencyId` + `__value__`). */
93
- const parseMoney = (value) => {
94
- const money = firstRecord(value);
93
+ const parseMoney = (rawMoney) => {
94
+ const money = firstRecord(rawMoney);
95
95
  if (!money) {
96
96
  return;
97
97
  }
@@ -155,7 +155,7 @@ export const mapFindingItem = (raw) => {
155
155
  *
156
156
  * @example
157
157
  * ```ts
158
- * const result = mapFindCompletedItemsResponse(rawJson, 'iphone 14');
158
+ * const soldListings = mapFindCompletedItemsResponse(rawJson, 'iphone 14');
159
159
  * ```
160
160
  */
161
161
  export const mapFindCompletedItemsResponse = (raw, keywords) => {
@@ -31,8 +31,8 @@ export class TranslationApi {
31
31
  const client = this.client;
32
32
  const path = `${this.basePath}/translate`;
33
33
  return Effect.gen(function* () {
34
- const body = yield* requireObjectEffect(input, 'input');
35
- return yield* requestPostEffect(client, path, body);
34
+ const translateRequest = yield* requireObjectEffect(input, 'input');
35
+ return yield* requestPostEffect(client, path, translateRequest);
36
36
  });
37
37
  };
38
38
  }
@@ -31,8 +31,8 @@ export class VeroApi {
31
31
  const path = `${this.basePath}/vero_report`;
32
32
  return Effect.gen(function* () {
33
33
  const endpointInput = yield* requireObjectEffect(input, 'input');
34
- const body = yield* requireObjectEffect(endpointInput.reportData, 'reportData');
35
- return yield* requestPostEffect(client, path, body);
34
+ const veroReport = yield* requireObjectEffect(endpointInput.reportData, 'reportData');
35
+ return yield* requestPostEffect(client, path, veroReport);
36
36
  });
37
37
  };
38
38
  /**
@@ -0,0 +1,79 @@
1
+ import { EbayApiError } from '../../api/shared/request.js';
2
+ import { Data, Effect } from 'effect';
3
+ /**
4
+ * Largest download returned inline as an embedded MCP resource. Base64 grows the payload by
5
+ * a third, and some eBay exports (Taxonomy fetchItemAspects) exceed 100 MB compressed.
6
+ */
7
+ export const MAX_INLINE_DOWNLOAD_BYTES = 25 * 1024 * 1024;
8
+ const DEFAULT_CONTENT_TYPE = 'application/octet-stream';
9
+ const ENCODED_FILE_NAME = /filename\*\s*=\s*(?:UTF-8|utf-8)?'[^']*'([^;]+)/;
10
+ const QUOTED_FILE_NAME = /filename\s*=\s*"([^"]+)"/;
11
+ const BARE_FILE_NAME = /filename\s*=\s*([^;\s]+)/;
12
+ const decodeFileName = (encoded) => {
13
+ try {
14
+ return decodeURIComponent(encoded);
15
+ }
16
+ catch { }
17
+ };
18
+ /**
19
+ * Reads the file name eBay advertises in a `content-disposition` header.
20
+ *
21
+ * @param header - Raw `content-disposition` value, if the response carried one.
22
+ * @returns The RFC 5987 `filename*` value when present, else the plain `filename`, else undefined.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * fileNameFromDisposition('attachment; filename="report.csv.gz"'); // 'report.csv.gz'
27
+ * ```
28
+ */
29
+ export const fileNameFromDisposition = (header) => {
30
+ if (!header) {
31
+ return;
32
+ }
33
+ const encoded = ENCODED_FILE_NAME.exec(header)?.[1];
34
+ if (encoded) {
35
+ return decodeFileName(encoded.trim());
36
+ }
37
+ return (QUOTED_FILE_NAME.exec(header)?.[1] ?? BARE_FILE_NAME.exec(header)?.[1])?.trim();
38
+ };
39
+ /** Tagged failure for a download too large to return inline through MCP. */
40
+ export class DownloadTooLargeError extends Data.TaggedError('DownloadTooLargeError') {
41
+ get message() {
42
+ return `eBay file from ${this.path} is at least ${this.bytes} bytes, above the ${this.limit}-byte limit for returning files inline over MCP`;
43
+ }
44
+ }
45
+ /** Size of a download: what was read, or the larger declared length when reading stopped early. */
46
+ const downloadSize = (fileResponse) => Math.max(fileResponse.data.length, Number(fileResponse.headers['content-length']) || 0);
47
+ /**
48
+ * Downloads a binary eBay resource as an Effect, keeping its content type and file name.
49
+ * Reading stops as soon as the file is known to exceed {@link MAX_INLINE_DOWNLOAD_BYTES}.
50
+ *
51
+ * @param client - eBay REST client that owns auth and transport details.
52
+ * @param path - eBay REST path, or a full URL when `config.absolute` is set.
53
+ * @param config - Optional query params, headers, host, or timeout overrides for the request.
54
+ * @returns An Effect with the file bytes and metadata; fails with EbayApiError, or with
55
+ * DownloadTooLargeError above {@link MAX_INLINE_DOWNLOAD_BYTES}.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const file = await Effect.runPromise(
60
+ * requestDownloadEffect(client, '/sell/feed/v1/task/TASK-1/download_result_file'),
61
+ * );
62
+ * ```
63
+ */
64
+ export const requestDownloadEffect = (client, path, config = {}) => Effect.tryPromise({
65
+ try: () => client.getForResponse(path, undefined, {
66
+ ...config,
67
+ responseType: 'arraybuffer',
68
+ maxBytes: MAX_INLINE_DOWNLOAD_BYTES,
69
+ }),
70
+ catch: (cause) => new EbayApiError({ method: 'GET', path, cause }), // allow-duplicate
71
+ }).pipe(Effect.filterOrFail((fileResponse) => downloadSize(fileResponse) <= MAX_INLINE_DOWNLOAD_BYTES, (fileResponse) => new DownloadTooLargeError({
72
+ path,
73
+ bytes: downloadSize(fileResponse),
74
+ limit: MAX_INLINE_DOWNLOAD_BYTES,
75
+ })), Effect.map((fileResponse) => ({
76
+ bytes: fileResponse.data,
77
+ contentType: fileResponse.headers['content-type']?.split(';')[0]?.trim() || DEFAULT_CONTENT_TYPE,
78
+ fileName: fileNameFromDisposition(fileResponse.headers['content-disposition']),
79
+ })));
@@ -0,0 +1,33 @@
1
+ import { EbayApiError } from '../../api/shared/request.js';
2
+ import { Effect } from 'effect';
3
+ /**
4
+ * Reads the created resource ID from a `Location` header and checks it points at the
5
+ * expected collection, so a malformed or foreign header never becomes an ID.
6
+ *
7
+ * @param location - `Location` header from the create response, if any.
8
+ * @param collectionPath - eBay REST collection path the resource must live under, e.g. `/sell/feed/v1/order_task`.
9
+ * @returns An Effect with the header and its decoded ID, or an EbayApiError for a missing/invalid header.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const task = yield* locatedResourceId(response.headers.location, '/sell/feed/v1/order_task');
14
+ * ```
15
+ */
16
+ export const locatedResourceId = (location, collectionPath) => Effect.try({
17
+ try: () => {
18
+ if (!location) {
19
+ throw new Error('eBay returned no Location header');
20
+ }
21
+ const url = new URL(location);
22
+ const prefix = `${collectionPath}/`;
23
+ const segment = url.pathname.slice(prefix.length);
24
+ if (url.protocol !== 'https:' ||
25
+ !url.pathname.startsWith(prefix) ||
26
+ !segment ||
27
+ segment.includes('/')) {
28
+ throw new Error('eBay returned an invalid resource Location');
29
+ }
30
+ return { location, id: decodeURIComponent(segment) };
31
+ },
32
+ catch: (cause) => new EbayApiError({ method: 'POST', path: collectionPath, cause }),
33
+ });
@@ -1,3 +1,4 @@
1
+ import { formatZodIssues } from '../../utils/zodIssues.js';
1
2
  import { Data, Effect } from 'effect';
2
3
  /** Tagged failure returned by migrated eBay API endpoint Effects. */
3
4
  export class EbayApiError extends Data.TaggedError('EbayApiError') {
@@ -9,6 +10,41 @@ export class EbayApiError extends Data.TaggedError('EbayApiError') {
9
10
  /** Tagged validation failure returned before an endpoint request is sent. */
10
11
  export class EndpointInputError extends Data.TaggedError('EndpointInputError') {
11
12
  }
13
+ /**
14
+ * Parses endpoint input through its Zod schema (the tool's SSOT), reporting a
15
+ * failure as EndpointInputError so endpoint Effects keep one input-failure type.
16
+ *
17
+ * @param schema - Zod endpoint input schema.
18
+ * @param input - Raw endpoint input.
19
+ * @param parameter - Parameter named in the failure; defaults to `input`.
20
+ * @returns An Effect with the decoded input, or EndpointInputError.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const { documentId } = yield* decodeEndpointInputEffect(documentIdInputSchema, input);
25
+ * ```
26
+ */
27
+ export const decodeEndpointInputEffect = (schema, input, parameter = 'input') => {
28
+ const parsedInput = schema.safeParse(input);
29
+ return parsedInput.success
30
+ ? Effect.succeed(parsedInput.data)
31
+ : Effect.fail(new EndpointInputError({ parameter, message: formatZodIssues(parsedInput.error) }));
32
+ };
33
+ /**
34
+ * Builds the per-call `X-EBAY-C-MARKETPLACE-ID` header for operations whose marketplace
35
+ * differs from the client's configured `EBAY_MARKETPLACE_ID`.
36
+ *
37
+ * @param marketplaceId - eBay marketplace ID, e.g. `EBAY_GB`.
38
+ * @returns A request config carrying only the marketplace header.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * yield* requestGetEffect(client, path, undefined, marketplaceHeader('EBAY_GB'));
43
+ * ```
44
+ */
45
+ export const marketplaceHeader = (marketplaceId) => ({
46
+ headers: { 'X-EBAY-C-MARKETPLACE-ID': marketplaceId },
47
+ });
12
48
  /**
13
49
  * Builds query params from an endpoint-owned allow-list.
14
50
  *
@@ -133,7 +169,7 @@ export const optionalNonNegativeNumberEffect = (value, name) => {
133
169
  *
134
170
  * @example
135
171
  * ```ts
136
- * const request = yield* requireObjectEffect<CreateOffersRequest>(body, 'offerData');
172
+ * const createOffersRequest = yield* requireObjectEffect<CreateOffersRequest>(offerData, 'offerData');
137
173
  * ```
138
174
  */
139
175
  export const requireObjectEffect = (value, name) => {
@@ -156,7 +192,7 @@ export const requireObjectEffect = (value, name) => {
156
192
  *
157
193
  * @example
158
194
  * ```ts
159
- * const response = await Effect.runPromise(requestGetEffect(client, path, params));
195
+ * const policies = await Effect.runPromise(requestGetEffect(client, path, params));
160
196
  * ```
161
197
  */
162
198
  export const requestGetEffect = (client, path, params, config) => Effect.tryPromise({
@@ -173,21 +209,21 @@ export const requestGetEffect = (client, path, params, config) => Effect.tryProm
173
209
  *
174
210
  * @param client - eBay REST client that owns auth and transport details.
175
211
  * @param path - eBay REST path to request.
176
- * @param body - Optional JSON body to send.
212
+ * @param requestBody - Optional JSON body to send.
177
213
  * @param config - Optional per-request headers or params, used for endpoint-specific headers.
178
214
  * @returns An Effect that succeeds with the generated eBay response DTO.
179
215
  *
180
216
  * @example
181
217
  * ```ts
182
- * const response = await Effect.runPromise(requestPostEffect(client, path, body));
218
+ * const createdPolicy = await Effect.runPromise(requestPostEffect(client, path, requestBody));
183
219
  * ```
184
220
  */
185
- export const requestPostEffect = (client, path, body, config) => Effect.tryPromise({
221
+ export const requestPostEffect = (client, path, requestBody, config) => Effect.tryPromise({
186
222
  try: () => {
187
223
  if (config !== undefined) {
188
- return client.post(path, body, config);
224
+ return client.post(path, requestBody, config);
189
225
  }
190
- return body === undefined ? client.post(path) : client.post(path, body);
226
+ return requestBody === undefined ? client.post(path) : client.post(path, requestBody);
191
227
  },
192
228
  catch: (cause) => new EbayApiError({ method: 'POST', path, cause }),
193
229
  });
@@ -196,18 +232,40 @@ export const requestPostEffect = (client, path, body, config) => Effect.tryPromi
196
232
  *
197
233
  * @param client - eBay REST client that owns auth and transport details.
198
234
  * @param path - eBay REST path to request.
199
- * @param body - Optional JSON body to send.
235
+ * @param requestBody - Optional JSON body to send.
200
236
  * @returns An Effect that succeeds with the generated eBay response DTO.
201
237
  *
202
238
  * @example
203
239
  * ```ts
204
- * const response = await Effect.runPromise(requestPutEffect(client, path, body));
240
+ * const updatedPolicy = await Effect.runPromise(requestPutEffect(client, path, requestBody));
205
241
  * ```
206
242
  */
207
- export const requestPutEffect = (client, path, body, config) => Effect.tryPromise({
208
- try: () => config === undefined ? client.put(path, body) : client.put(path, body, config),
243
+ export const requestPutEffect = (client, path, requestBody, config) => Effect.tryPromise({
244
+ try: () => config === undefined
245
+ ? client.put(path, requestBody)
246
+ : client.put(path, requestBody, config),
209
247
  catch: (cause) => new EbayApiError({ method: 'PUT', path, cause }),
210
248
  });
249
+ /**
250
+ * Execute a PATCH request as an Effect with typed eBay API errors.
251
+ *
252
+ * @param client - eBay REST client that owns auth and transport details.
253
+ * @param path - eBay REST path to request.
254
+ * @param requestBody - Optional JSON body to send.
255
+ * @param config - Optional per-request headers or params, used for endpoint-specific headers.
256
+ * @returns An Effect that succeeds with the generated eBay response DTO.
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * await Effect.runPromise(requestPatchEffect(client, path, requestBody, { headers }));
261
+ * ```
262
+ */
263
+ export const requestPatchEffect = (client, path, requestBody, config) => Effect.tryPromise({
264
+ try: () => config === undefined
265
+ ? client.patch(path, requestBody)
266
+ : client.patch(path, requestBody, config),
267
+ catch: (cause) => new EbayApiError({ method: 'PATCH', path, cause }),
268
+ });
211
269
  /**
212
270
  * Execute a DELETE request as an Effect with typed eBay API errors.
213
271
  *