ebay-mcp 1.4.3

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 (205) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +586 -0
  3. package/build/api/account-management/account.d.ts +216 -0
  4. package/build/api/account-management/account.js +305 -0
  5. package/build/api/analytics-and-report/analytics.d.ts +33 -0
  6. package/build/api/analytics-and-report/analytics.js +102 -0
  7. package/build/api/client.d.ts +89 -0
  8. package/build/api/client.js +343 -0
  9. package/build/api/communication/feedback.d.ts +45 -0
  10. package/build/api/communication/feedback.js +119 -0
  11. package/build/api/communication/message.d.ts +55 -0
  12. package/build/api/communication/message.js +131 -0
  13. package/build/api/communication/negotiation.d.ts +39 -0
  14. package/build/api/communication/negotiation.js +97 -0
  15. package/build/api/communication/notification.d.ts +128 -0
  16. package/build/api/communication/notification.js +373 -0
  17. package/build/api/index.d.ts +96 -0
  18. package/build/api/index.js +121 -0
  19. package/build/api/listing-management/inventory.d.ts +216 -0
  20. package/build/api/listing-management/inventory.js +633 -0
  21. package/build/api/listing-metadata/metadata.d.ts +154 -0
  22. package/build/api/listing-metadata/metadata.js +485 -0
  23. package/build/api/listing-metadata/taxonomy.d.ts +38 -0
  24. package/build/api/listing-metadata/taxonomy.js +58 -0
  25. package/build/api/marketing-and-promotions/marketing.d.ts +395 -0
  26. package/build/api/marketing-and-promotions/marketing.js +565 -0
  27. package/build/api/marketing-and-promotions/recommendation.d.ts +20 -0
  28. package/build/api/marketing-and-promotions/recommendation.js +32 -0
  29. package/build/api/order-management/dispute.d.ts +65 -0
  30. package/build/api/order-management/dispute.js +69 -0
  31. package/build/api/order-management/fulfillment.d.ts +80 -0
  32. package/build/api/order-management/fulfillment.js +89 -0
  33. package/build/api/other/compliance.d.ts +26 -0
  34. package/build/api/other/compliance.js +47 -0
  35. package/build/api/other/edelivery.d.ts +153 -0
  36. package/build/api/other/edelivery.js +219 -0
  37. package/build/api/other/identity.d.ts +17 -0
  38. package/build/api/other/identity.js +24 -0
  39. package/build/api/other/translation.d.ts +14 -0
  40. package/build/api/other/translation.js +22 -0
  41. package/build/api/other/vero.d.ts +30 -0
  42. package/build/api/other/vero.js +48 -0
  43. package/build/auth/oauth-metadata.d.ts +46 -0
  44. package/build/auth/oauth-metadata.js +59 -0
  45. package/build/auth/oauth-middleware.d.ts +35 -0
  46. package/build/auth/oauth-middleware.js +99 -0
  47. package/build/auth/oauth-types.d.ts +66 -0
  48. package/build/auth/oauth-types.js +4 -0
  49. package/build/auth/oauth.d.ts +93 -0
  50. package/build/auth/oauth.js +383 -0
  51. package/build/auth/scope-utils.d.ts +70 -0
  52. package/build/auth/scope-utils.js +304 -0
  53. package/build/auth/token-verifier.d.ts +57 -0
  54. package/build/auth/token-verifier.js +172 -0
  55. package/build/config/environment.d.ts +61 -0
  56. package/build/config/environment.js +260 -0
  57. package/build/index.d.ts +1 -0
  58. package/build/index.js +98 -0
  59. package/build/schemas/account-management/account.d.ts +5324 -0
  60. package/build/schemas/account-management/account.js +366 -0
  61. package/build/schemas/analytics/analytics.d.ts +167 -0
  62. package/build/schemas/analytics/analytics.js +191 -0
  63. package/build/schemas/communication/messages.d.ts +1872 -0
  64. package/build/schemas/communication/messages.js +348 -0
  65. package/build/schemas/fulfillment/orders.d.ts +4655 -0
  66. package/build/schemas/fulfillment/orders.js +317 -0
  67. package/build/schemas/index.d.ts +2100 -0
  68. package/build/schemas/index.js +68 -0
  69. package/build/schemas/inventory-management/inventory.d.ts +6419 -0
  70. package/build/schemas/inventory-management/inventory.js +450 -0
  71. package/build/schemas/marketing/marketing.d.ts +14181 -0
  72. package/build/schemas/marketing/marketing.js +1088 -0
  73. package/build/schemas/metadata/metadata.d.ts +5259 -0
  74. package/build/schemas/metadata/metadata.js +614 -0
  75. package/build/schemas/other/other-apis.d.ts +257 -0
  76. package/build/schemas/other/other-apis.js +372 -0
  77. package/build/schemas/taxonomy/taxonomy.d.ts +215 -0
  78. package/build/schemas/taxonomy/taxonomy.js +571 -0
  79. package/build/scripts/auto-setup.d.ts +12 -0
  80. package/build/scripts/auto-setup.js +277 -0
  81. package/build/scripts/diagnostics.d.ts +8 -0
  82. package/build/scripts/diagnostics.js +299 -0
  83. package/build/scripts/download-specs.d.ts +1 -0
  84. package/build/scripts/download-specs.js +116 -0
  85. package/build/scripts/interactive-setup.d.ts +21 -0
  86. package/build/scripts/interactive-setup.js +723 -0
  87. package/build/server-http.d.ts +11 -0
  88. package/build/server-http.js +361 -0
  89. package/build/tools/definitions/account-with-schemas.d.ts +39 -0
  90. package/build/tools/definitions/account-with-schemas.js +170 -0
  91. package/build/tools/definitions/account.d.ts +12 -0
  92. package/build/tools/definitions/account.js +428 -0
  93. package/build/tools/definitions/analytics.d.ts +25 -0
  94. package/build/tools/definitions/analytics.js +66 -0
  95. package/build/tools/definitions/communication.d.ts +12 -0
  96. package/build/tools/definitions/communication.js +151 -0
  97. package/build/tools/definitions/fulfillment.d.ts +12 -0
  98. package/build/tools/definitions/fulfillment.js +326 -0
  99. package/build/tools/definitions/index.d.ts +25 -0
  100. package/build/tools/definitions/index.js +37 -0
  101. package/build/tools/definitions/inventory.d.ts +12 -0
  102. package/build/tools/definitions/inventory.js +429 -0
  103. package/build/tools/definitions/marketing.d.ts +12 -0
  104. package/build/tools/definitions/marketing.js +1095 -0
  105. package/build/tools/definitions/metadata.d.ts +12 -0
  106. package/build/tools/definitions/metadata.js +188 -0
  107. package/build/tools/definitions/other.d.ts +13 -0
  108. package/build/tools/definitions/other.js +309 -0
  109. package/build/tools/definitions/taxonomy.d.ts +25 -0
  110. package/build/tools/definitions/taxonomy.js +64 -0
  111. package/build/tools/definitions/token-management.d.ts +35 -0
  112. package/build/tools/definitions/token-management.js +103 -0
  113. package/build/tools/index.d.ts +11 -0
  114. package/build/tools/index.js +1003 -0
  115. package/build/tools/schemas.d.ts +14764 -0
  116. package/build/tools/schemas.js +667 -0
  117. package/build/tools/tool-definitions.d.ts +35 -0
  118. package/build/tools/tool-definitions.js +3534 -0
  119. package/build/types/application-settings/developerAnalyticsV1BetaOas3.d.ts +197 -0
  120. package/build/types/application-settings/developerAnalyticsV1BetaOas3.js +5 -0
  121. package/build/types/application-settings/developerClientRegistrationV1Oas3.d.ts +155 -0
  122. package/build/types/application-settings/developerClientRegistrationV1Oas3.js +5 -0
  123. package/build/types/application-settings/developerKeyManagementV1Oas3.d.ts +246 -0
  124. package/build/types/application-settings/developerKeyManagementV1Oas3.js +5 -0
  125. package/build/types/ebay-enums.d.ts +1204 -0
  126. package/build/types/ebay-enums.js +1330 -0
  127. package/build/types/ebay.d.ts +143 -0
  128. package/build/types/ebay.js +123 -0
  129. package/build/types/index.d.ts +6 -0
  130. package/build/types/index.js +10 -0
  131. package/build/types/sell-apps/account-management/sellAccountV1Oas3.d.ts +2579 -0
  132. package/build/types/sell-apps/account-management/sellAccountV1Oas3.js +5 -0
  133. package/build/types/sell-apps/analytics-and-report/sellAnalyticsV1Oas3.d.ts +446 -0
  134. package/build/types/sell-apps/analytics-and-report/sellAnalyticsV1Oas3.js +5 -0
  135. package/build/types/sell-apps/communication/commerceFeedbackV1BetaOas3.d.ts +705 -0
  136. package/build/types/sell-apps/communication/commerceFeedbackV1BetaOas3.js +5 -0
  137. package/build/types/sell-apps/communication/commerceMessageV1Oas3.d.ts +590 -0
  138. package/build/types/sell-apps/communication/commerceMessageV1Oas3.js +5 -0
  139. package/build/types/sell-apps/communication/commerceNotificationV1Oas3.d.ts +1276 -0
  140. package/build/types/sell-apps/communication/commerceNotificationV1Oas3.js +5 -0
  141. package/build/types/sell-apps/communication/sellNegotiationV1Oas3.d.ts +277 -0
  142. package/build/types/sell-apps/communication/sellNegotiationV1Oas3.js +5 -0
  143. package/build/types/sell-apps/listing-management/sellInventoryV1Oas3.d.ts +3133 -0
  144. package/build/types/sell-apps/listing-management/sellInventoryV1Oas3.js +5 -0
  145. package/build/types/sell-apps/listing-metadata/sellMetadataV1Oas3.d.ts +2289 -0
  146. package/build/types/sell-apps/listing-metadata/sellMetadataV1Oas3.js +5 -0
  147. package/build/types/sell-apps/markeitng-and-promotions/sellMarketingV1Oas3.d.ts +6650 -0
  148. package/build/types/sell-apps/markeitng-and-promotions/sellMarketingV1Oas3.js +5 -0
  149. package/build/types/sell-apps/markeitng-and-promotions/sellRecommendationV1Oas3.d.ts +172 -0
  150. package/build/types/sell-apps/markeitng-and-promotions/sellRecommendationV1Oas3.js +5 -0
  151. package/build/types/sell-apps/order-management/sellFulfillmentV1Oas3.d.ts +1869 -0
  152. package/build/types/sell-apps/order-management/sellFulfillmentV1Oas3.js +5 -0
  153. package/build/types/sell-apps/other-apis/commerceIdentityV1Oas3.d.ts +178 -0
  154. package/build/types/sell-apps/other-apis/commerceIdentityV1Oas3.js +5 -0
  155. package/build/types/sell-apps/other-apis/commerceTranslationV1BetaOas3.d.ts +128 -0
  156. package/build/types/sell-apps/other-apis/commerceTranslationV1BetaOas3.js +5 -0
  157. package/build/types/sell-apps/other-apis/commerceVeroV1Oas3.d.ts +417 -0
  158. package/build/types/sell-apps/other-apis/commerceVeroV1Oas3.js +5 -0
  159. package/build/types/sell-apps/other-apis/sellComplianceV1Oas3.d.ts +273 -0
  160. package/build/types/sell-apps/other-apis/sellComplianceV1Oas3.js +5 -0
  161. package/build/types/sell-apps/other-apis/sellEdeliveryInternationalShippingOas3.d.ts +2537 -0
  162. package/build/types/sell-apps/other-apis/sellEdeliveryInternationalShippingOas3.js +5 -0
  163. package/build/types/sell-apps/other-apis/sellMarketingV1Oas3.d.ts +6650 -0
  164. package/build/types/sell-apps/other-apis/sellMarketingV1Oas3.js +5 -0
  165. package/build/types/sell-apps/other-apis/sellRecommendationV1Oas3.d.ts +172 -0
  166. package/build/types/sell-apps/other-apis/sellRecommendationV1Oas3.js +5 -0
  167. package/build/utils/account-management/account.d.ts +1094 -0
  168. package/build/utils/account-management/account.js +831 -0
  169. package/build/utils/communication/feedback.d.ts +152 -0
  170. package/build/utils/communication/feedback.js +216 -0
  171. package/build/utils/communication/message.d.ts +174 -0
  172. package/build/utils/communication/message.js +242 -0
  173. package/build/utils/communication/negotiation.d.ts +123 -0
  174. package/build/utils/communication/negotiation.js +150 -0
  175. package/build/utils/communication/notification.d.ts +370 -0
  176. package/build/utils/communication/notification.js +369 -0
  177. package/build/utils/date-converter.d.ts +59 -0
  178. package/build/utils/date-converter.js +160 -0
  179. package/build/utils/llm-client-detector.d.ts +54 -0
  180. package/build/utils/llm-client-detector.js +318 -0
  181. package/build/utils/oauth-helper.d.ts +37 -0
  182. package/build/utils/oauth-helper.js +315 -0
  183. package/build/utils/order-management/dispute.d.ts +346 -0
  184. package/build/utils/order-management/dispute.js +369 -0
  185. package/build/utils/order-management/fulfillment.d.ts +200 -0
  186. package/build/utils/order-management/fulfillment.js +205 -0
  187. package/build/utils/other/compliance.d.ts +49 -0
  188. package/build/utils/other/compliance.js +76 -0
  189. package/build/utils/other/edelivery.d.ts +310 -0
  190. package/build/utils/other/edelivery.js +241 -0
  191. package/build/utils/other/identity.d.ts +13 -0
  192. package/build/utils/other/identity.js +13 -0
  193. package/build/utils/other/translation.d.ts +28 -0
  194. package/build/utils/other/translation.js +41 -0
  195. package/build/utils/other/vero.d.ts +61 -0
  196. package/build/utils/other/vero.js +90 -0
  197. package/build/utils/scope-helper.d.ts +49 -0
  198. package/build/utils/scope-helper.js +207 -0
  199. package/build/utils/security-checker.d.ts +46 -0
  200. package/build/utils/security-checker.js +248 -0
  201. package/build/utils/setup-validator.d.ts +25 -0
  202. package/build/utils/setup-validator.js +305 -0
  203. package/build/utils/token-utils.d.ts +40 -0
  204. package/build/utils/token-utils.js +40 -0
  205. package/package.json +115 -0
@@ -0,0 +1,383 @@
1
+ import axios from 'axios';
2
+ import { getBaseUrl, getDefaultScopes } from '../config/environment.js';
3
+ import { LocaleEnum } from '../types/ebay-enums.js';
4
+ import { readFileSync, writeFileSync } from 'fs';
5
+ import { join } from 'path';
6
+ /**
7
+ * Update .env file with new token values
8
+ */
9
+ function updateEnvFile(updates) {
10
+ try {
11
+ const envPath = join(process.cwd(), '.env');
12
+ let envContent = readFileSync(envPath, 'utf-8');
13
+ // Update each key-value pair
14
+ for (const [key, value] of Object.entries(updates)) {
15
+ // Match the key with or without value, handling comments
16
+ const regex = new RegExp(`^(#\\s*)?${key}=.*$`, 'gm');
17
+ const newLine = `${key}=${value}`;
18
+ if (regex.test(envContent)) {
19
+ // Update existing key (uncomment if needed)
20
+ envContent = envContent.replace(regex, newLine);
21
+ }
22
+ else {
23
+ // Add new key at the end
24
+ envContent += `\n${newLine}`;
25
+ }
26
+ }
27
+ writeFileSync(envPath, envContent, 'utf-8');
28
+ console.log('✅ Updated .env file with new tokens');
29
+ }
30
+ catch (error) {
31
+ console.error('⚠️ Failed to update .env file:', error instanceof Error ? error.message : error);
32
+ console.error(' Please manually update your .env file with the new tokens');
33
+ }
34
+ }
35
+ /**
36
+ * Manages eBay OAuth 2.0 authentication
37
+ * Loads tokens exclusively from environment variables (.env file)
38
+ * Supports both client credentials (app tokens) and user access tokens with refresh
39
+ */
40
+ export class EbayOAuthClient {
41
+ config;
42
+ appAccessToken = null;
43
+ appAccessTokenExpiry = 0;
44
+ userTokens = null;
45
+ constructor(config) {
46
+ this.config = config;
47
+ }
48
+ /**
49
+ * Initialize user tokens from environment variables only
50
+ * If EBAY_USER_REFRESH_TOKEN exists, automatically refresh to get a valid access token
51
+ */
52
+ async initialize() {
53
+ const envRefreshToken = process.env.EBAY_USER_REFRESH_TOKEN;
54
+ const envAccessToken = process.env.EBAY_USER_ACCESS_TOKEN;
55
+ const envAppToken = process.env.EBAY_APP_ACCESS_TOKEN ?? '';
56
+ const locale = this.config?.locale || LocaleEnum.en_US;
57
+ if (envRefreshToken) {
58
+ console.log('📝 Loading refresh token, access token and app to env file...');
59
+ // Create token object with just the refresh token from environment
60
+ // Note: We don't set scopes here - eBay will return the scopes when we refresh
61
+ const now = Date.now();
62
+ this.userTokens = {
63
+ clientId: this.config.clientId,
64
+ clientSecret: this.config.clientSecret,
65
+ userAccessToken: envAccessToken || '', // Can be empty, will be filled by refresh
66
+ userRefreshToken: envRefreshToken,
67
+ redirectUri: this.config.redirectUri,
68
+ envAppToken,
69
+ tokenType: 'Bearer',
70
+ locale,
71
+ userAccessTokenExpiry: now + 7200 * 1000, // Default 2 hours
72
+ userRefreshTokenExpiry: now + 18 * 30 * 24 * 60 * 60 * 1000, // Default 18 months
73
+ // scope is not set - will be populated by the refresh response
74
+ };
75
+ // Immediately refresh to get a valid access token and scopes
76
+ console.log('🔄 Refreshing access token using refresh token from .env...');
77
+ try {
78
+ await this.refreshUserToken();
79
+ console.log('✅ Access token refreshed successfully from .env configuration.');
80
+ await this.getOrRefreshAppAccessToken();
81
+ }
82
+ catch (error) {
83
+ console.error('❌ Failed to refresh access token:', error instanceof Error ? error.message : error);
84
+ console.error(' The EBAY_USER_REFRESH_TOKEN in .env may be invalid or expired.');
85
+ console.error(' Please update EBAY_USER_REFRESH_TOKEN or use ebay_set_user_tokens_with_expiry tool.');
86
+ // Clear invalid tokens
87
+ this.userTokens = null;
88
+ }
89
+ }
90
+ }
91
+ /**
92
+ * Check if user tokens are available
93
+ */
94
+ hasUserTokens() {
95
+ return this.userTokens !== null;
96
+ }
97
+ /**
98
+ * Check if user access token is expired
99
+ */
100
+ isUserAccessTokenExpired(tokens) {
101
+ return tokens.userAccessTokenExpiry ? Date.now() >= tokens.userAccessTokenExpiry : true;
102
+ }
103
+ /**
104
+ * Check if user refresh token is expired
105
+ */
106
+ isUserRefreshTokenExpired(tokens) {
107
+ return tokens.userRefreshTokenExpiry ? Date.now() >= tokens.userRefreshTokenExpiry : true;
108
+ }
109
+ /**
110
+ * Get a valid access token, with priority order:
111
+ * 1. User access token (if available and valid, or refreshable)
112
+ * 2. App access token from client credentials (fallback)
113
+ */
114
+ async getAccessToken() {
115
+ // Try to use user token first
116
+ if (this.userTokens) {
117
+ // Check if access token is still valid
118
+ if (!this.isUserAccessTokenExpired(this.userTokens)) {
119
+ return this.userTokens.userAccessToken;
120
+ }
121
+ // Try to refresh if refresh token is valid
122
+ if (!this.isUserRefreshTokenExpired(this.userTokens)) {
123
+ try {
124
+ await this.refreshUserToken();
125
+ return this.userTokens.userAccessToken;
126
+ }
127
+ catch (error) {
128
+ console.error('Failed to refresh user token, falling back to app access token:', error);
129
+ // Clear invalid tokens
130
+ this.userTokens = null;
131
+ }
132
+ }
133
+ else {
134
+ // Refresh token expired
135
+ console.error('User refresh token expired. User needs to re-authorize.');
136
+ this.userTokens = null;
137
+ throw new Error('User authorization expired. Please update EBAY_USER_REFRESH_TOKEN in .env with a new refresh token.');
138
+ }
139
+ }
140
+ // Fallback to app access token (client credentials)
141
+ if (this.appAccessToken && Date.now() < this.appAccessTokenExpiry) {
142
+ return this.appAccessToken;
143
+ }
144
+ await this.getOrRefreshAppAccessToken();
145
+ return this.appAccessToken;
146
+ }
147
+ /**
148
+ * Set user access token and refresh token
149
+ * Stores tokens in memory and updates .env file for persistence
150
+ */
151
+ setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry) {
152
+ // Store tokens in memory with default expiry
153
+ // Access tokens typically expire in 2 hours (7200 seconds)
154
+ // Refresh tokens typically expire in 18 months
155
+ const now = Date.now();
156
+ this.userTokens = {
157
+ clientId: this.config.clientId,
158
+ clientSecret: this.config.clientSecret,
159
+ redirectUri: this.config.redirectUri,
160
+ userAccessToken: accessToken,
161
+ userRefreshToken: refreshToken,
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
165
+ };
166
+ // Update .env file with new tokens
167
+ updateEnvFile({
168
+ EBAY_USER_ACCESS_TOKEN: accessToken,
169
+ EBAY_USER_REFRESH_TOKEN: refreshToken,
170
+ });
171
+ }
172
+ /**
173
+ * Get or refresh the app access token using the client credentials flow.
174
+ * This method ensures that a valid app access token is always available.
175
+ * Rate limit: 1,000 requests/day
176
+ */
177
+ async getOrRefreshAppAccessToken() {
178
+ // Return cached token if still valid
179
+ if (this.appAccessToken) {
180
+ return this.appAccessToken;
181
+ }
182
+ const authUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
183
+ const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
184
+ // Client credentials flow only supports basic scope
185
+ // User authorization flows can request additional scopes
186
+ const scopeParam = 'https://api.ebay.com/oauth/api_scope';
187
+ try {
188
+ const response = await axios.post(authUrl, new URLSearchParams({
189
+ grant_type: 'client_credentials',
190
+ scope: scopeParam,
191
+ }).toString(), {
192
+ headers: {
193
+ 'Content-Type': 'application/x-www-form-urlencoded',
194
+ Authorization: `Basic ${credentials}`,
195
+ },
196
+ });
197
+ this.appAccessToken = response.data.access_token;
198
+ // Set expiry with 60 second buffer
199
+ this.appAccessTokenExpiry = Date.now() + (response.data.expires_in - 60) * 1000;
200
+ // Update .env file with app access token
201
+ updateEnvFile({
202
+ EBAY_APP_ACCESS_TOKEN: this.appAccessToken,
203
+ });
204
+ return this.appAccessToken;
205
+ }
206
+ catch (error) {
207
+ if (axios.isAxiosError(error)) {
208
+ throw new Error(`Failed to get app access token: ${error.response?.data?.error_description || error.message}`);
209
+ }
210
+ throw error;
211
+ }
212
+ }
213
+ /**
214
+ * Exchange authorization code for user access token
215
+ * Note: After receiving tokens, manually add EBAY_USER_REFRESH_TOKEN to .env file
216
+ */
217
+ async exchangeCodeForToken(code) {
218
+ if (!this.config.redirectUri) {
219
+ throw new Error('Redirect URI is required for authorization code exchange');
220
+ }
221
+ const tokenUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
222
+ const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
223
+ try {
224
+ const response = await axios.post(tokenUrl, new URLSearchParams({
225
+ grant_type: 'authorization_code',
226
+ code,
227
+ redirect_uri: this.config.redirectUri,
228
+ }).toString(), {
229
+ headers: {
230
+ 'Content-Type': 'application/x-www-form-urlencoded',
231
+ Authorization: `Basic ${credentials}`,
232
+ },
233
+ });
234
+ const tokenData = response.data;
235
+ // Store the user tokens in memory
236
+ const now = Date.now();
237
+ this.userTokens = {
238
+ clientId: this.config.clientId,
239
+ clientSecret: this.config.clientSecret,
240
+ redirectUri: this.config.redirectUri,
241
+ userAccessToken: tokenData.access_token,
242
+ userRefreshToken: tokenData.refresh_token,
243
+ tokenType: tokenData.token_type,
244
+ userAccessTokenExpiry: now + tokenData.expires_in * 1000,
245
+ userRefreshTokenExpiry: now + tokenData.refresh_token_expires_in * 1000,
246
+ scope: tokenData.scope,
247
+ };
248
+ // Inform user to save refresh token to .env
249
+ console.log('\nToken exchange successful!');
250
+ console.log('To persist your authentication, add this to your .env file:');
251
+ console.log(`EBAY_USER_REFRESH_TOKEN="${tokenData.refresh_token}"\n`);
252
+ return tokenData;
253
+ }
254
+ catch (error) {
255
+ if (axios.isAxiosError(error)) {
256
+ throw new Error(`Failed to exchange code for token: ${error.response?.data?.error_description || error.message}`);
257
+ }
258
+ throw error;
259
+ }
260
+ }
261
+ /**
262
+ * Refresh user access token using refresh token from .env
263
+ * This method is public and can be called by LLMs when encountering authentication errors
264
+ */
265
+ async refreshUserToken() {
266
+ if (!this.userTokens) {
267
+ throw new Error('No user tokens available to refresh');
268
+ }
269
+ // Use the token endpoint, not the authorization endpoint
270
+ const authUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
271
+ const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
272
+ try {
273
+ // Prepare refresh token request parameters
274
+ // Note: We do NOT include scopes in refresh requests as eBay will return
275
+ // the scopes that were originally granted to the refresh token
276
+ const params = {
277
+ grant_type: 'refresh_token',
278
+ refresh_token: this.userTokens.userRefreshToken,
279
+ };
280
+ const response = await axios.post(authUrl, new URLSearchParams(params).toString(), {
281
+ headers: {
282
+ 'Content-Type': 'application/x-www-form-urlencoded',
283
+ Authorization: `Basic ${credentials}`,
284
+ },
285
+ });
286
+ const tokenData = response.data;
287
+ // Update tokens in memory
288
+ const now = Date.now();
289
+ this.userTokens = {
290
+ clientId: this.config.clientId,
291
+ clientSecret: this.config.clientSecret,
292
+ redirectUri: this.config.redirectUri,
293
+ userAccessToken: tokenData.access_token,
294
+ userRefreshToken: tokenData.refresh_token || this.userTokens.userRefreshToken, // Use new refresh token if provided
295
+ tokenType: tokenData.token_type,
296
+ userAccessTokenExpiry: now + tokenData.expires_in * 1000,
297
+ userRefreshTokenExpiry: tokenData.refresh_token_expires_in
298
+ ? now + tokenData.refresh_token_expires_in * 1000
299
+ : this.userTokens.userRefreshTokenExpiry, // Keep existing expiry if not provided
300
+ // eBay may not return scope in refresh response - preserve existing scope if not returned
301
+ scope: tokenData.scope || this.userTokens.scope,
302
+ };
303
+ // Update .env file with new tokens
304
+ const envUpdates = {
305
+ EBAY_USER_ACCESS_TOKEN: tokenData.access_token,
306
+ };
307
+ // If eBay provided a new refresh token, update it too
308
+ if (tokenData.refresh_token && tokenData.refresh_token !== process.env.EBAY_USER_REFRESH_TOKEN) {
309
+ envUpdates.EBAY_USER_REFRESH_TOKEN = tokenData.refresh_token;
310
+ console.log('\n🔄 eBay issued a new refresh token - updating .env file');
311
+ }
312
+ // Write updates to .env file
313
+ updateEnvFile(envUpdates);
314
+ }
315
+ catch (error) {
316
+ if (axios.isAxiosError(error)) {
317
+ throw new Error(`Failed to refresh token: ${error.response?.data?.error_description || error.message}`);
318
+ }
319
+ throw error;
320
+ }
321
+ }
322
+ /**
323
+ * Check if currently authenticated (either user or app credentials)
324
+ */
325
+ isAuthenticated() {
326
+ if (this.userTokens && !this.isUserAccessTokenExpired(this.userTokens)) {
327
+ return true;
328
+ }
329
+ return this.appAccessToken !== null && Date.now() < this.appAccessTokenExpiry;
330
+ }
331
+ /**
332
+ * Clear all authentication tokens from memory
333
+ * Note: To persist this change, remove EBAY_USER_REFRESH_TOKEN from .env
334
+ */
335
+ clearAllTokens() {
336
+ this.appAccessToken = null;
337
+ this.appAccessTokenExpiry = 0;
338
+ this.userTokens = null;
339
+ }
340
+ /**
341
+ * Get current token info for debugging
342
+ */
343
+ getTokenInfo() {
344
+ const info = {
345
+ hasUserToken: this.userTokens !== null && !this.isUserAccessTokenExpired(this.userTokens),
346
+ hasAppAccessToken: this.appAccessToken !== null && Date.now() < this.appAccessTokenExpiry,
347
+ };
348
+ // Add scope comparison info if user tokens are available
349
+ if (this.userTokens?.scope) {
350
+ const tokenScopes = this.userTokens.scope.split(' ');
351
+ const environmentScopes = getDefaultScopes(this.config.environment);
352
+ const tokenScopeSet = new Set(tokenScopes);
353
+ const missingScopes = environmentScopes.filter((scope) => !tokenScopeSet.has(scope));
354
+ info.scopeInfo = {
355
+ tokenScopes,
356
+ environmentScopes,
357
+ missingScopes,
358
+ };
359
+ }
360
+ return info;
361
+ }
362
+ /**
363
+ * Get internal user tokens (for debugging/status tools)
364
+ * @internal
365
+ */
366
+ getUserTokens() {
367
+ return this.userTokens;
368
+ }
369
+ /**
370
+ * Get internal app access token cached value (for debugging/status tools)
371
+ * @internal
372
+ */
373
+ getCachedAppAccessToken() {
374
+ return this.appAccessToken;
375
+ }
376
+ /**
377
+ * Get internal app access token expiry (for debugging/status tools)
378
+ * @internal
379
+ */
380
+ getCachedAppAccessTokenExpiry() {
381
+ return this.appAccessTokenExpiry;
382
+ }
383
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Utility functions for working with eBay OAuth scopes
3
+ */
4
+ /**
5
+ * Result of scope validation
6
+ */
7
+ export interface ValidationResult {
8
+ isValid: boolean;
9
+ warnings: string[];
10
+ validScopes: string[];
11
+ invalidScopes: string[];
12
+ }
13
+ /**
14
+ * Comparison between two sets of scopes
15
+ */
16
+ export interface ScopeDifference {
17
+ inBothEnvironments: string[];
18
+ productionOnly: string[];
19
+ sandboxOnly: string[];
20
+ }
21
+ /**
22
+ * Scope requirement information for a tool
23
+ */
24
+ export interface ScopeRequirement {
25
+ requiredScopes: string[];
26
+ optionalScopes?: string[];
27
+ minimumScope: string;
28
+ description: string;
29
+ }
30
+ /**
31
+ * Validate scopes and provide detailed validation results
32
+ */
33
+ export declare function validateScopesDetailed(scopes: string[], environment: 'production' | 'sandbox'): ValidationResult;
34
+ /**
35
+ * Get required scopes for a specific tool
36
+ * This maps tool names to their required eBay OAuth scopes
37
+ */
38
+ export declare function getRequiredScopesForTool(toolName: string): ScopeRequirement | null;
39
+ /**
40
+ * Check if a token has all required scopes for a tool
41
+ */
42
+ export declare function hasRequiredScopes(tokenScopes: string[], requiredScopes: string[]): boolean;
43
+ /**
44
+ * Get the differences between production and sandbox scopes
45
+ */
46
+ export declare function getScopeDifferences(): ScopeDifference;
47
+ /**
48
+ * Format scope for display (remove common prefix for readability)
49
+ */
50
+ export declare function formatScopeForDisplay(scope: string): string;
51
+ /**
52
+ * Group scopes by API category
53
+ */
54
+ export declare function groupScopesByCategory(scopes: string[]): Record<string, string[]>;
55
+ /**
56
+ * Check if a scope is readonly
57
+ */
58
+ export declare function isScopeReadonly(scope: string): boolean;
59
+ /**
60
+ * Get the write version of a readonly scope
61
+ */
62
+ export declare function getWriteScope(readonlyScope: string): string | null;
63
+ /**
64
+ * Get the readonly version of a write scope
65
+ */
66
+ export declare function getReadonlyScope(writeScope: string): string | null;
67
+ /**
68
+ * Get scope description from scope name
69
+ */
70
+ export declare function getScopeDescription(scope: string): string;