ebay-mcp 1.18.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 (161) hide show
  1. package/README.md +24 -35
  2. package/build/api/account-management/accountV2.js +22 -22
  3. package/build/api/account-management/finances.js +10 -10
  4. package/build/api/analytics-and-report/analytics.js +1 -1
  5. package/build/api/{client.js → client/ebayApiClient.js} +37 -41
  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} +4 -4
  13. package/build/api/listing-management/feedReportTasks.js +6 -6
  14. package/build/api/listing-management/feedRequest.js +6 -6
  15. package/build/api/listing-management/feedSchedules.js +4 -4
  16. package/build/api/listing-management/feedTasks.js +2 -2
  17. package/build/api/listing-management/items.js +22 -22
  18. package/build/api/listing-management/locations.js +4 -4
  19. package/build/api/listing-management/media.js +14 -14
  20. package/build/api/listing-management/mediaAttach.js +14 -14
  21. package/build/api/listing-management/mediaResources.js +15 -13
  22. package/build/api/listing-management/offerFormat.js +1 -1
  23. package/build/api/listing-management/offers.js +23 -23
  24. package/build/api/listing-management/stores.js +9 -9
  25. package/build/api/listing-metadata/metadata.js +10 -10
  26. package/build/api/marketing-and-promotions/ads.js +34 -34
  27. package/build/api/marketing-and-promotions/campaigns.js +19 -19
  28. package/build/api/marketing-and-promotions/promotions.js +22 -22
  29. package/build/api/marketing-and-promotions/reports.js +7 -7
  30. package/build/api/order-management/dispute.js +11 -9
  31. package/build/api/order-management/fulfillment.js +4 -4
  32. package/build/api/order-management/logistics.js +7 -7
  33. package/build/api/other/browseMappers.js +15 -13
  34. package/build/api/other/browseValidators.js +4 -4
  35. package/build/api/other/edelivery.js +19 -19
  36. package/build/api/other/finding.js +13 -13
  37. package/build/api/other/translation.js +2 -2
  38. package/build/api/other/vero.js +2 -2
  39. package/build/api/shared/download.js +7 -7
  40. package/build/api/shared/request.js +28 -19
  41. package/build/api/trading/listingFormat.js +37 -30
  42. package/build/api/trading/trading.js +33 -33
  43. package/build/auth/credentialSession.js +5 -5
  44. package/build/auth/oauth.js +3 -3
  45. package/build/auth/oauthHelper.js +24 -24
  46. package/build/auth/oauthMetadata.js +4 -4
  47. package/build/auth/oauthMiddleware.js +16 -16
  48. package/build/auth/scopeUtils.js +2 -2
  49. package/build/auth/tokenVerifier.js +23 -23
  50. package/build/config/environment.js +4 -45
  51. package/build/config/oauthScopes.js +132 -0
  52. package/build/index.js +5 -5
  53. package/build/mcp/httpTransport.js +32 -20
  54. package/build/mcp/runtime.js +12 -12
  55. package/build/mcp/toolGating.js +6 -12
  56. package/build/mcp/uiBridge.js +3 -3
  57. package/build/schemas/account-management/account.js +2 -52
  58. package/build/schemas/account-management/accountV2.js +2 -2
  59. package/build/schemas/account-management/finances.js +1 -1
  60. package/build/schemas/analytics/analytics.js +2 -48
  61. package/build/schemas/communication/messages.js +2 -46
  62. package/build/schemas/developer/developer.js +1 -27
  63. package/build/schemas/fulfillment/logistics.js +1 -1
  64. package/build/schemas/fulfillment/orders.js +2 -47
  65. package/build/schemas/inventory-management/feed.js +1 -1
  66. package/build/schemas/inventory-management/inventory.js +2 -50
  67. package/build/schemas/inventory-management/media.js +2 -2
  68. package/build/schemas/inventory-management/mediaDocuments.js +1 -1
  69. package/build/schemas/inventory-management/stores.js +1 -1
  70. package/build/schemas/marketing/ads.js +2 -2
  71. package/build/schemas/marketing/campaigns.js +2 -2
  72. package/build/schemas/marketing/common.js +2 -2
  73. package/build/schemas/marketing/marketing.js +0 -1
  74. package/build/schemas/marketing/promotions.js +2 -2
  75. package/build/schemas/marketing/reports.js +2 -2
  76. package/build/schemas/metadata/metadata.js +2 -90
  77. package/build/schemas/metadata/shipping.js +1 -1
  78. package/build/schemas/other/browse.js +2 -2
  79. package/build/schemas/other/finding.js +2 -2
  80. package/build/schemas/other/otherApis.js +2 -114
  81. package/build/schemas/taxonomy/categoryTree.js +1 -1
  82. package/build/schemas/taxonomy/charity.js +1 -1
  83. package/build/schemas/taxonomy/taxonomy.js +2 -69
  84. package/build/scripts/autoSetup.js +3 -3
  85. package/build/scripts/{buildUi.js → buildMcpApps.js} +13 -13
  86. package/build/scripts/devSync.js +19 -68
  87. package/build/scripts/diagnostics.js +15 -16
  88. package/build/scripts/interactiveSetup.js +25 -25
  89. package/build/scripts/scopeHelper.js +2 -2
  90. package/build/scripts/securityChecker.js +20 -20
  91. package/build/scripts/setup.js +76 -76
  92. package/build/scripts/setupShared.js +5 -30
  93. package/build/scripts/setupValidator.js +9 -9
  94. package/build/scripts/setupWizard.js +23 -23
  95. package/build/scripts/skills.js +14 -17
  96. package/build/scripts/specRefs.js +1 -1
  97. package/build/scripts/specSources.js +144 -0
  98. package/build/serverHttp.js +4 -7
  99. package/build/skills/content.js +13 -13
  100. package/build/skills/install.js +9 -9
  101. package/build/skills/render.js +2 -2
  102. package/build/tools/categories/communication.js +1 -1
  103. package/build/tools/categories/connector.js +19 -16
  104. package/build/tools/categories/fulfillment.js +4 -4
  105. package/build/tools/categories/inventory.js +1 -1
  106. package/build/tools/categories/metadata.js +1 -1
  107. package/build/tools/categories/taxonomy.js +1 -1
  108. package/build/tools/categories/tokenManagement.js +8 -8
  109. package/build/tools/contracts.js +2 -2
  110. package/build/tools/defineTool.js +27 -12
  111. package/build/tools/registry.js +2 -2
  112. package/build/tools/schemas.js +2 -2
  113. package/build/tools/ui/mapHelpers.js +12 -12
  114. package/build/tools/ui/maps.js +101 -97
  115. package/build/tools/ui/viewModels.js +2 -2
  116. package/build/utils/apiStatusFeed.js +12 -12
  117. package/build/utils/communication/feedback.js +3 -3
  118. package/build/utils/communication/message.js +3 -3
  119. package/build/utils/communication/negotiation.js +3 -3
  120. package/build/utils/communication/notification.js +3 -3
  121. package/build/utils/entryModule.js +33 -0
  122. package/build/utils/envFile.js +47 -0
  123. package/build/utils/envParser.js +5 -5
  124. package/build/utils/errors.js +4 -4
  125. package/build/utils/http.js +38 -38
  126. package/build/utils/localFeedFiles.js +3 -3
  127. package/build/utils/logRedaction.js +21 -0
  128. package/build/utils/logger.js +49 -35
  129. package/build/utils/other/compliance.js +3 -3
  130. package/build/utils/other/identity.js +3 -3
  131. package/build/utils/other/translation.js +3 -3
  132. package/build/utils/other/vero.js +3 -3
  133. package/build/utils/schemaHelpers.js +2 -2
  134. package/build/utils/tokenUtils.js +2 -2
  135. package/build/utils/trading/trading.js +1 -1
  136. package/build/utils/version.js +1 -1
  137. package/build/utils/zodIssues.js +14 -0
  138. package/package.json +8 -11
  139. package/README.de.md +0 -426
  140. package/README.es.md +0 -426
  141. package/README.fr.md +0 -426
  142. package/README.ja.md +0 -426
  143. package/README.ko.md +0 -426
  144. package/README.pt-BR.md +0 -426
  145. package/README.ru.md +0 -426
  146. package/README.zh-CN.md +0 -426
  147. package/build/schemas/index.js +0 -76
  148. package/build/schemas/marketing/jsonSchemas.js +0 -118
  149. package/build/scripts/downloadSpecs.js +0 -117
  150. package/build/scripts/updateApiStatusDoc.js +0 -71
  151. package/build/utils/effectSchema.js +0 -203
  152. package/build/utils/effectSchemaAdapter.js +0 -121
  153. package/build/utils/effectSchemaChainMethods.js +0 -146
  154. package/build/utils/effectSchemaRefinements.js +0 -104
  155. package/build/utils/effectSchemaStringMethods.js +0 -63
  156. package/build/utils/effectSchemaTypes.js +0 -47
  157. package/docs/auth/production_scopes.json +0 -111
  158. package/docs/auth/sandbox_scopes.json +0 -142
  159. /package/build/api/{clientRequestError.js → client/ebayClientRequestError.js} +0 -0
  160. /package/build/api/{rateLimitTracker.js → client/rateLimitTracker.js} +0 -0
  161. /package/build/api/{clientTradingError.js → client/tradingApiFailure.js} +0 -0
@@ -8,7 +8,7 @@ import { Effect, Either } from 'effect';
8
8
  * Create Bearer token authentication middleware
9
9
  *
10
10
  * @param config - Token verifier and RFC 6750 challenge settings.
11
- * @returns Express middleware that verifies bearer tokens and attaches `req.auth`.
11
+ * @returns Express middleware that verifies bearer tokens and attaches `request.auth`.
12
12
  *
13
13
  * @example
14
14
  * ```ts
@@ -17,11 +17,11 @@ import { Effect, Either } from 'effect';
17
17
  */
18
18
  export const createBearerAuthMiddleware = (config) => {
19
19
  const realm = config.realm || 'mcp';
20
- return async (req, res, next) => {
20
+ return async (request, response, next) => {
21
21
  // Extract token from Authorization header
22
- const authHeader = req.headers.authorization;
22
+ const authHeader = request.headers.authorization;
23
23
  if (!authHeader) {
24
- sendUnauthorized(res, realm, config.resourceMetadataUrl, {
24
+ sendUnauthorized(response, realm, config.resourceMetadataUrl, {
25
25
  error: 'invalid_token',
26
26
  error_description: 'No authorization header provided',
27
27
  });
@@ -30,7 +30,7 @@ export const createBearerAuthMiddleware = (config) => {
30
30
  // Check Bearer scheme
31
31
  const parts = authHeader.split(' ');
32
32
  if (parts.length !== 2 || parts[0] !== 'Bearer') {
33
- sendUnauthorized(res, realm, config.resourceMetadataUrl, {
33
+ sendUnauthorized(response, realm, config.resourceMetadataUrl, {
34
34
  error: 'invalid_token',
35
35
  error_description: 'Invalid authorization header format. Expected: Bearer <token>',
36
36
  });
@@ -40,20 +40,20 @@ export const createBearerAuthMiddleware = (config) => {
40
40
  const verified = await Effect.runPromise(Effect.either(config.verifier.verifyToken(token)));
41
41
  if (Either.isLeft(verified)) {
42
42
  const errorMessage = getErrorMessage(verified.left, 'Token verification failed');
43
- sendUnauthorized(res, realm, config.resourceMetadataUrl, {
43
+ sendUnauthorized(response, realm, config.resourceMetadataUrl, {
44
44
  error: 'invalid_token',
45
45
  error_description: errorMessage,
46
46
  });
47
47
  return;
48
48
  }
49
- req.auth = verified.right;
49
+ request.auth = verified.right;
50
50
  next();
51
51
  };
52
52
  };
53
53
  /**
54
54
  * Send 401 Unauthorized response with RFC 6750 compliant WWW-Authenticate header
55
55
  */
56
- const sendUnauthorized = (res, realm, resourceMetadataUrl, challenge) => {
56
+ const sendUnauthorized = (response, realm, resourceMetadataUrl, challenge) => {
57
57
  // Build WWW-Authenticate header per RFC 6750
58
58
  let authenticateValue = `Bearer realm="${realm}", resource_metadata="${resourceMetadataUrl}"`;
59
59
  if (challenge.error) {
@@ -65,8 +65,8 @@ const sendUnauthorized = (res, realm, resourceMetadataUrl, challenge) => {
65
65
  if (challenge.scope) {
66
66
  authenticateValue += `, scope="${challenge.scope}"`;
67
67
  }
68
- res.setHeader('WWW-Authenticate', authenticateValue);
69
- res.status(401).json({
68
+ response.setHeader('WWW-Authenticate', authenticateValue);
69
+ response.status(401).json({
70
70
  error: challenge.error || 'unauthorized',
71
71
  error_description: challenge.error_description || 'Authorization required',
72
72
  });
@@ -82,21 +82,21 @@ const sendUnauthorized = (res, realm, resourceMetadataUrl, challenge) => {
82
82
  * app.get('/admin', requireScopes(['mcp:admin']), handler);
83
83
  * ```
84
84
  */
85
- export const requireScopes = (requiredScopes) => (req, res, next) => {
86
- if (!req.auth) {
87
- res.status(401).json({
85
+ export const requireScopes = (requiredScopes) => (request, response, next) => {
86
+ if (!request.auth) {
87
+ response.status(401).json({
88
88
  error: 'unauthorized',
89
89
  error_description: 'No authentication information found',
90
90
  });
91
91
  return;
92
92
  }
93
- const hasRequiredScopes = requiredScopes.every((scope) => req.auth.scopes.includes(scope));
93
+ const hasRequiredScopes = requiredScopes.every((scope) => request.auth.scopes.includes(scope));
94
94
  if (!hasRequiredScopes) {
95
- res.status(403).json({
95
+ response.status(403).json({
96
96
  error: 'insufficient_scope',
97
97
  error_description: `Missing required scopes: ${requiredScopes.join(', ')}`,
98
98
  required_scopes: requiredScopes,
99
- provided_scopes: req.auth.scopes,
99
+ provided_scopes: request.auth.scopes,
100
100
  });
101
101
  return;
102
102
  }
@@ -28,7 +28,7 @@ const optionalConsent = (tools, scope, description) => Object.fromEntries(tools.
28
28
  *
29
29
  * @example
30
30
  * ```ts
31
- * const result = validateScopesDetailed(scopes, 'production');
31
+ * const scopeValidation = validateScopesDetailed(scopes, 'production');
32
32
  * ```
33
33
  */
34
34
  export const validateScopesDetailed = (scopes, environment) => {
@@ -381,7 +381,7 @@ export const getReadonlyScope = (writeScope) => {
381
381
  'commerce.feedback',
382
382
  ];
383
383
  const scopeType = writeScope.split('/').pop();
384
- if (scopeType && hasReadonly.some((s) => scopeType.includes(s))) {
384
+ if (scopeType && hasReadonly.some((readonlyMarker) => scopeType.includes(readonlyMarker))) {
385
385
  return `${writeScope}.readonly`;
386
386
  }
387
387
  return null;
@@ -93,7 +93,7 @@ export class TokenVerifier {
93
93
  }
94
94
  // Prepare introspection request. Client credentials authenticate via HTTP
95
95
  // Basic (RFC 7662 §2.1) rather than form body fields.
96
- const requestData = {
96
+ const introspectionRequest = {
97
97
  token,
98
98
  token_type_hint: 'access_token',
99
99
  };
@@ -104,17 +104,17 @@ export class TokenVerifier {
104
104
  headers.Authorization = `Basic ${Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64')}`;
105
105
  }
106
106
  const params = new URLSearchParams({
107
- token: requestData.token,
107
+ token: introspectionRequest.token,
108
108
  });
109
109
  // Make introspection request
110
- const data = yield* this.requestVerifierData('verifyViaIntrospection', 'introspectionRequest', 'Token introspection failed', {
110
+ const introspection = yield* this.requestVerifierData('verifyViaIntrospection', 'introspectionRequest', 'Token introspection failed', {
111
111
  method: 'POST',
112
112
  url: introspectionEndpoint,
113
113
  body: params,
114
114
  headers,
115
115
  });
116
116
  // Check if token is active
117
- if (!data.active) {
117
+ if (!introspection.active) {
118
118
  return yield* Effect.fail(tokenVerifierError({
119
119
  operation: 'verifyViaIntrospection',
120
120
  reason: 'inactiveToken',
@@ -122,15 +122,15 @@ export class TokenVerifier {
122
122
  }));
123
123
  }
124
124
  // Validate audience
125
- if (!this.validateAudience(data.aud)) {
125
+ if (!this.validateAudience(introspection.aud)) {
126
126
  return yield* Effect.fail(tokenVerifierError({
127
127
  operation: 'verifyViaIntrospection',
128
128
  reason: 'invalidAudience',
129
- message: `Invalid audience. Expected: ${this.config.expectedAudience}, Got: ${data.aud}`,
129
+ message: `Invalid audience. Expected: ${this.config.expectedAudience}, Got: ${introspection.aud}`,
130
130
  }));
131
131
  }
132
132
  // Validate scopes
133
- const scopes = data.scope ? data.scope.split(' ') : [];
133
+ const scopes = introspection.scope ? introspection.scope.split(' ') : [];
134
134
  if (this.config.requiredScopes) {
135
135
  const hasRequiredScopes = this.config.requiredScopes.every((scope) => scopes.includes(scope));
136
136
  if (!hasRequiredScopes) {
@@ -143,11 +143,11 @@ export class TokenVerifier {
143
143
  }
144
144
  return {
145
145
  token,
146
- clientId: data.client_id || 'missing-client-id',
146
+ clientId: introspection.client_id || 'missing-client-id',
147
147
  scopes,
148
- expiresAt: data.exp,
149
- audience: data.aud,
150
- subject: data.sub,
148
+ expiresAt: introspection.exp,
149
+ audience: introspection.aud,
150
+ subject: introspection.sub,
151
151
  };
152
152
  });
153
153
  /**
@@ -179,7 +179,7 @@ export class TokenVerifier {
179
179
  cause,
180
180
  }),
181
181
  });
182
- const { payload } = yield* Effect.tryPromise({
182
+ const { payload: claims } = yield* Effect.tryPromise({
183
183
  try: () => jwtVerify(token, JWKS, {
184
184
  issuer: metadata.issuer,
185
185
  audience: this.config.expectedAudience,
@@ -192,11 +192,11 @@ export class TokenVerifier {
192
192
  }),
193
193
  });
194
194
  let scopes = [];
195
- if (typeof payload.scope === 'string') {
196
- scopes = payload.scope.split(' ');
195
+ if (typeof claims.scope === 'string') {
196
+ scopes = claims.scope.split(' ');
197
197
  }
198
- else if (Array.isArray(payload.scope)) {
199
- scopes = payload.scope.filter((scope) => typeof scope === 'string');
198
+ else if (Array.isArray(claims.scope)) {
199
+ scopes = claims.scope.filter((scope) => typeof scope === 'string');
200
200
  }
201
201
  if (this.config.requiredScopes) {
202
202
  const hasRequiredScopes = this.config.requiredScopes.every((scope) => scopes.includes(scope));
@@ -209,19 +209,19 @@ export class TokenVerifier {
209
209
  }
210
210
  }
211
211
  let clientId = 'missing-client-id';
212
- if (typeof payload.client_id === 'string') {
213
- clientId = payload.client_id;
212
+ if (typeof claims.client_id === 'string') {
213
+ clientId = claims.client_id;
214
214
  }
215
- else if (typeof payload.azp === 'string') {
216
- clientId = payload.azp;
215
+ else if (typeof claims.azp === 'string') {
216
+ clientId = claims.azp;
217
217
  }
218
218
  return {
219
219
  token,
220
220
  clientId,
221
221
  scopes,
222
- expiresAt: payload.exp,
223
- audience: payload.aud,
224
- subject: payload.sub,
222
+ expiresAt: claims.exp,
223
+ audience: claims.aud,
224
+ subject: claims.sub,
225
225
  };
226
226
  });
227
227
  /**
@@ -1,17 +1,13 @@
1
1
  import { config } from 'dotenv';
2
- import { existsSync, readFileSync } from 'fs';
2
+ import { existsSync } from 'fs';
3
3
  import { fileURLToPath } from 'url';
4
- import { dirname, join } from 'path';
5
4
  import { CREDENTIAL_ENV_PATH } from '../config/credentialFile.js';
6
5
  import { getToolGatingConfigError } from '../config/toolFamilies.js';
7
6
  import { getMediaAccessConfig } from '../config/mediaAccess.js';
8
- import { getErrorMessage } from '../utils/errors.js';
7
+ import { productionScopes, sandboxScopes } from '../config/oauthScopes.js';
9
8
  import { getVersion } from '../utils/version.js';
10
9
  import { Effect, Either } from 'effect';
11
10
  import process from 'node:process';
12
- // Get the current directory for loading scope files and .env
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = dirname(__filename);
15
11
  // Load .env from the package root (two levels up from src/config/), not process.cwd().
16
12
  // MCP servers inherit cwd from the host (e.g. Claude Code's project dir), so
17
13
  // process.cwd() may point to an unrelated project with a different .env.
@@ -46,40 +42,6 @@ export const getLoggingConfig = () => ({
46
42
  export const getUiRuntimeConfig = () => ({
47
43
  enabled: process.env.EBAY_MCP_UI !== 'off',
48
44
  });
49
- /** Loads and parses scopes from one checked-in scope JSON file. */
50
- const loadScopes = (fileName, label) => {
51
- const loaded = Effect.runSync(Effect.either(Effect.try({
52
- try: () => {
53
- const scopesPath = join(__dirname, '../../docs/auth', fileName);
54
- const scopesData = readFileSync(scopesPath, 'utf-8');
55
- const scopes = JSON.parse(scopesData);
56
- // Filter out empty objects and extract unique scope strings
57
- const uniqueScopes = new Set();
58
- scopes.forEach((item) => {
59
- if (item.Scope) {
60
- uniqueScopes.add(item.Scope);
61
- }
62
- });
63
- return Array.from(uniqueScopes);
64
- },
65
- catch: (error) => error,
66
- })));
67
- if (Either.isLeft(loaded)) {
68
- const error = loaded.left;
69
- writeConfigDiagnostic(`Failed to load ${label} scopes: ${getErrorMessage(error)}`);
70
- // Return a minimal set of core scopes as fallback
71
- return ['https://api.ebay.com/oauth/api_scope'];
72
- }
73
- return loaded.right;
74
- };
75
- /**
76
- * Loads and parses production scopes from the checked-in eBay scope table.
77
- */
78
- const getProductionScopes = () => loadScopes('production_scopes.json', 'production');
79
- /**
80
- * Loads and parses sandbox scopes from the checked-in eBay scope table.
81
- */
82
- const getSandboxScopes = () => loadScopes('sandbox_scopes.json', 'sandbox');
83
45
  /**
84
46
  * Gets default OAuth scopes for the specified eBay environment.
85
47
  *
@@ -91,10 +53,7 @@ const getSandboxScopes = () => loadScopes('sandbox_scopes.json', 'sandbox');
91
53
  * ```
92
54
  */
93
55
  export const getDefaultScopes = (environment) => {
94
- if (environment === 'production') {
95
- return getProductionScopes();
96
- }
97
- return getSandboxScopes();
56
+ return [...(environment === 'production' ? productionScopes : sandboxScopes)];
98
57
  };
99
58
  /** Optional scope requiring keyset eligibility and fresh user consent. */
100
59
  export const POST_ORDER_DOCUMENT_SCOPE = 'https://api.ebay.com/oauth/api_scope/commerce.post_order.document';
@@ -159,7 +118,7 @@ export const getRequestedScopes = (environment, env = process.env) => {
159
118
  * @returns Requested scopes plus warnings for environment-only or unknown scopes.
160
119
  * @example
161
120
  * ```ts
162
- * const result = validateScopes(scopes, 'sandbox');
121
+ * const scopeValidation = validateScopes(scopes, 'sandbox');
163
122
  * ```
164
123
  */
165
124
  export const validateScopes = (scopes, environment) => {
@@ -0,0 +1,132 @@
1
+ /**
2
+ * OAuth scopes eBay grants to seller apps, copied from the developer portal scope tables.
3
+ * Each comment is eBay's description of the scope below it.
4
+ */
5
+ /** Scopes available to apps in the eBay production environment. */
6
+ export const productionScopes = [
7
+ // View public data from eBay
8
+ 'https://api.ebay.com/oauth/api_scope',
9
+ // View your eBay marketing activities, such as ad campaigns and listing promotions
10
+ 'https://api.ebay.com/oauth/api_scope/sell.marketing.readonly',
11
+ // View and manage your eBay marketing activities, such as ad campaigns and listing promotions
12
+ 'https://api.ebay.com/oauth/api_scope/sell.marketing',
13
+ // View your inventory and offers
14
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory.readonly',
15
+ // View and manage your inventory and offers
16
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory',
17
+ // View your account settings
18
+ 'https://api.ebay.com/oauth/api_scope/sell.account.readonly',
19
+ // View and manage your account settings
20
+ 'https://api.ebay.com/oauth/api_scope/sell.account',
21
+ // View your order fulfillments
22
+ 'https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly',
23
+ // View and manage your order fulfillments
24
+ 'https://api.ebay.com/oauth/api_scope/sell.fulfillment',
25
+ // View your selling analytics data, such as performance reports
26
+ 'https://api.ebay.com/oauth/api_scope/sell.analytics.readonly',
27
+ // View and manage your payment and order information to display this information to you and allow you to initiate refunds using the third party application
28
+ 'https://api.ebay.com/oauth/api_scope/sell.finances',
29
+ // View and manage disputes and related details (including payment and order information).
30
+ 'https://api.ebay.com/oauth/api_scope/sell.payment.dispute',
31
+ // View a user's basic information, such as username or business account details, from their eBay member account
32
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.readonly',
33
+ // View and manage your reputation data, such as feedback.
34
+ 'https://api.ebay.com/oauth/api_scope/sell.reputation',
35
+ // View your reputation data, such as feedback.
36
+ 'https://api.ebay.com/oauth/api_scope/sell.reputation.readonly',
37
+ // View and manage your event notification subscriptions
38
+ 'https://api.ebay.com/oauth/api_scope/commerce.notification.subscription',
39
+ // View your event notification subscriptions
40
+ 'https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly',
41
+ // View and manage eBay stores
42
+ 'https://api.ebay.com/oauth/api_scope/sell.stores',
43
+ // View eBay stores
44
+ 'https://api.ebay.com/oauth/api_scope/sell.stores.readonly',
45
+ // Allows access to eDelivery International Shipping APIs.
46
+ 'https://api.ebay.com/oauth/scope/sell.edelivery',
47
+ // Allows access to APIs that are related to eBay's Verified Rights Owner (VeRO) program.
48
+ 'https://api.ebay.com/oauth/api_scope/commerce.vero',
49
+ // Enables applications to manage and enhance inventory listings through the Inventory Mapping Public API.
50
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory.mapping',
51
+ // Allows access to eBay Message APIs.
52
+ 'https://api.ebay.com/oauth/api_scope/commerce.message',
53
+ // Allows access to Feedback APIs.
54
+ 'https://api.ebay.com/oauth/api_scope/commerce.feedback',
55
+ // View and manage shipping information
56
+ 'https://api.ebay.com/oauth/api_scope/commerce.shipping',
57
+ // Allows readonly access to Feedback APIs.
58
+ 'https://api.ebay.com/oauth/api_scope/commerce.feedback.readonly',
59
+ ];
60
+ /** Scopes available to apps in the eBay sandbox environment. */
61
+ export const sandboxScopes = [
62
+ // View public data from eBay
63
+ 'https://api.ebay.com/oauth/api_scope',
64
+ // View your order details
65
+ 'https://api.ebay.com/oauth/api_scope/buy.order.readonly',
66
+ // Purchase eBay items off eBay
67
+ 'https://api.ebay.com/oauth/api_scope/buy.guest.order',
68
+ // View your eBay marketing activities, such as ad campaigns and listing promotions
69
+ 'https://api.ebay.com/oauth/api_scope/sell.marketing.readonly',
70
+ // View and manage your eBay marketing activities, such as ad campaigns and listing promotions
71
+ 'https://api.ebay.com/oauth/api_scope/sell.marketing',
72
+ // View your inventory and offers
73
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory.readonly',
74
+ // View and manage your inventory and offers
75
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory',
76
+ // View your account settings
77
+ 'https://api.ebay.com/oauth/api_scope/sell.account.readonly',
78
+ // View and manage your account settings
79
+ 'https://api.ebay.com/oauth/api_scope/sell.account',
80
+ // View your order fulfillments
81
+ 'https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly',
82
+ // View and manage your order fulfillments
83
+ 'https://api.ebay.com/oauth/api_scope/sell.fulfillment',
84
+ // View your selling analytics data, such as performance reports
85
+ 'https://api.ebay.com/oauth/api_scope/sell.analytics.readonly',
86
+ // This scope would allow signed in users read only access to marketplace insights.
87
+ 'https://api.ebay.com/oauth/api_scope/sell.marketplace.insights.readonly',
88
+ // This scope would allow signed in user to read catalog data.
89
+ 'https://api.ebay.com/oauth/api_scope/commerce.catalog.readonly',
90
+ // This scope would allow signed in user to access shopping carts
91
+ 'https://api.ebay.com/oauth/api_scope/buy.shopping.cart',
92
+ // View and manage bidding activities for auctions
93
+ 'https://api.ebay.com/oauth/api_scope/buy.offer.auction',
94
+ // View a user's basic information, such as username or business account details, from their eBay member account
95
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.readonly',
96
+ // View a user's personal email information from their eBay member account
97
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.email.readonly',
98
+ // View a user's personal telephone information from their eBay member account
99
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.phone.readonly',
100
+ // View a user's personal address information from their eBay member account
101
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.address.readonly',
102
+ // View a user's first and last name from their eBay member account
103
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.name.readonly',
104
+ // View a user's eBay member account status
105
+ 'https://api.ebay.com/oauth/api_scope/commerce.identity.status.readonly',
106
+ // View and manage your payment and order information to display this information to you and allow you to initiate refunds using the third party application
107
+ 'https://api.ebay.com/oauth/api_scope/sell.finances',
108
+ // View and manage disputes and related details (including payment and order information).
109
+ 'https://api.ebay.com/oauth/api_scope/sell.payment.dispute',
110
+ // View and manage your item drafts.
111
+ 'https://api.ebay.com/oauth/api_scope/sell.item.draft',
112
+ // View and manage your item information.
113
+ 'https://api.ebay.com/oauth/api_scope/sell.item',
114
+ // View and manage your reputation data, such as feedback.
115
+ 'https://api.ebay.com/oauth/api_scope/sell.reputation',
116
+ // View your reputation data, such as feedback.
117
+ 'https://api.ebay.com/oauth/api_scope/sell.reputation.readonly',
118
+ // View and manage your event notification subscriptions
119
+ 'https://api.ebay.com/oauth/api_scope/commerce.notification.subscription',
120
+ // View your event notification subscriptions
121
+ 'https://api.ebay.com/oauth/api_scope/commerce.notification.subscription.readonly',
122
+ // View and manage eBay stores
123
+ 'https://api.ebay.com/oauth/api_scope/sell.stores',
124
+ // View eBay stores
125
+ 'https://api.ebay.com/oauth/api_scope/sell.stores.readonly',
126
+ // Allows access to APIs that are related to eBay's Verified Rights Owner (VeRO) program.
127
+ 'https://api.ebay.com/oauth/api_scope/commerce.vero',
128
+ // Allows access to Feedback APIs.
129
+ 'https://api.ebay.com/oauth/api_scope/commerce.feedback',
130
+ // Enables applications to manage and enhance inventory listings through the Inventory Mapping Public API.
131
+ 'https://api.ebay.com/oauth/api_scope/sell.inventory.mapping',
132
+ ];
package/build/index.js CHANGED
@@ -12,13 +12,13 @@ import { Effect, Either } from 'effect';
12
12
  import process from 'node:process';
13
13
  const args = process.argv.slice(2);
14
14
  const runCliCommand = async (label, command) => {
15
- const result = await Effect.runPromise(Effect.either(Effect.tryPromise({
15
+ const commandOutcome = await Effect.runPromise(Effect.either(Effect.tryPromise({
16
16
  try: command,
17
17
  catch: (error) => error,
18
18
  })));
19
- if (Either.isLeft(result)) {
19
+ if (Either.isLeft(commandOutcome)) {
20
20
  serverLogger.error(`${label} failed`, {
21
- error: getErrorMessage(result.left, String(result.left)),
21
+ error: getErrorMessage(commandOutcome.left, String(commandOutcome.left)),
22
22
  });
23
23
  process.exit(1);
24
24
  }
@@ -68,8 +68,8 @@ class EbayMcpServer {
68
68
  // Validate environment configuration
69
69
  const validation = validateEnvironmentConfig();
70
70
  // Log informational notices (e.g. proxy auth mode)
71
- validation.infos.forEach((info) => {
72
- serverLogger.info(info);
71
+ validation.infos.forEach((notice) => {
72
+ serverLogger.info(notice);
73
73
  });
74
74
  // Log warnings
75
75
  if (validation.warnings.length > 0) {
@@ -15,6 +15,17 @@ import { getErrorMessage } from '../utils/errors.js';
15
15
  import { serverLogger } from '../utils/logger.js';
16
16
  import { getVersion } from '../utils/version.js';
17
17
  import { Effect, Either } from 'effect';
18
+ const LOOPBACK_ORIGIN = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
19
+ const parseCorsOrigins = (value) => {
20
+ const origins = (value || '')
21
+ .split(',')
22
+ .map((origin) => origin.trim())
23
+ .filter(Boolean);
24
+ if (origins.length === 0) {
25
+ return [LOOPBACK_ORIGIN];
26
+ }
27
+ return origins;
28
+ };
18
29
  const getProjectRoot = () => {
19
30
  const filename = fileURLToPath(import.meta.url);
20
31
  const directory = dirname(filename);
@@ -46,6 +57,7 @@ export const createHttpTransportConfigFromEnv = (env) => ({
46
57
  useIntrospection: env.OAUTH_USE_INTROSPECTION !== 'false',
47
58
  },
48
59
  authEnabled: env.OAUTH_ENABLED !== 'false',
60
+ corsOrigins: parseCorsOrigins(env.MCP_CORS_ORIGINS),
49
61
  projectRoot: getProjectRoot(),
50
62
  staticAuthToken: env.MCP_AUTH_TOKEN,
51
63
  });
@@ -114,12 +126,12 @@ async function createAuthMiddleware(config, serverUrl) {
114
126
  if (config.staticAuthToken) {
115
127
  const staticToken = config.staticAuthToken;
116
128
  serverLogger.info('Static bearer auth enabled via MCP_AUTH_TOKEN.');
117
- return (req, res, next) => {
118
- if (req.headers.authorization === `Bearer ${staticToken}`) {
129
+ return (request, response, next) => {
130
+ if (request.headers.authorization === `Bearer ${staticToken}`) {
119
131
  next();
120
132
  return;
121
133
  }
122
- res.status(401).json({
134
+ response.status(401).json({
123
135
  error: 'unauthorized',
124
136
  error_description: 'Missing or invalid bearer token',
125
137
  });
@@ -154,17 +166,17 @@ async function createAuthMiddleware(config, serverUrl) {
154
166
  });
155
167
  }
156
168
  function createRequestLogger() {
157
- return (req, res, next) => {
169
+ return (request, response, next) => {
158
170
  const start = Date.now();
159
- res.on('finish', () => {
171
+ response.on('finish', () => {
160
172
  const duration = Date.now() - start;
161
- serverLogger.http(`${req.method} ${req.path} -> ${res.statusCode} (${duration}ms)`);
173
+ serverLogger.http(`${request.method} ${request.path} -> ${response.statusCode} (${duration}ms)`);
162
174
  });
163
175
  next();
164
176
  };
165
177
  }
166
- function sendInvalidSession(res) {
167
- res.status(400).json({
178
+ function sendInvalidSession(response) {
179
+ response.status(400).json({
168
180
  error: 'invalid_session',
169
181
  error_description: 'Invalid or missing session ID',
170
182
  });
@@ -185,7 +197,7 @@ export const createHttpMcpApp = async (config) => {
185
197
  const serverUrl = getHttpServerUrl(config);
186
198
  const ebayConfig = config.ebayConfig ?? getEbayConfig();
187
199
  app.use(cors({
188
- origin: '*',
200
+ origin: config.corsOrigins,
189
201
  exposedHeaders: ['Mcp-Session-Id'],
190
202
  }));
191
203
  app.use(express.json());
@@ -201,8 +213,8 @@ export const createHttpMcpApp = async (config) => {
201
213
  ebayEnvironment: ebayConfig.environment,
202
214
  ebayScopes: getDefaultScopes(ebayConfig.environment),
203
215
  }));
204
- app.get('/health', (_req, res) => {
205
- res.json({
216
+ app.get('/health', (_request, response) => {
217
+ response.json({
206
218
  status: 'healthy',
207
219
  timestamp: new Date().toISOString(),
208
220
  oauth_enabled: config.authEnabled,
@@ -210,14 +222,14 @@ export const createHttpMcpApp = async (config) => {
210
222
  });
211
223
  const authMiddleware = await createAuthMiddleware(config, serverUrl);
212
224
  const transports = new Map();
213
- const mcpPostHandler = async (req, res) => {
214
- const sessionHeader = req.headers['mcp-session-id'];
225
+ const mcpPostHandler = async (request, response) => {
226
+ const sessionHeader = request.headers['mcp-session-id'];
215
227
  const sessionId = typeof sessionHeader === 'string' ? sessionHeader : undefined;
216
228
  let transport;
217
229
  if (sessionId && transports.has(sessionId)) {
218
230
  transport = transports.get(sessionId);
219
231
  }
220
- else if (!sessionId && isInitializeRequest(req.body)) {
232
+ else if (!sessionId && isInitializeRequest(request.body)) {
221
233
  transport = new StreamableHTTPServerTransport({
222
234
  sessionIdGenerator: () => randomUUID(),
223
235
  onsessioninitialized: (initializedSessionId) => {
@@ -235,7 +247,7 @@ export const createHttpMcpApp = async (config) => {
235
247
  await server.connect(transport);
236
248
  }
237
249
  else {
238
- res.status(400).json({
250
+ response.status(400).json({
239
251
  jsonrpc: '2.0',
240
252
  error: {
241
253
  code: -32_000,
@@ -245,17 +257,17 @@ export const createHttpMcpApp = async (config) => {
245
257
  });
246
258
  return;
247
259
  }
248
- await transport.handleRequest(req, res, req.body);
260
+ await transport.handleRequest(request, response, request.body);
249
261
  };
250
- const handleSessionRequest = async (req, res) => {
251
- const sessionHeader = req.headers['mcp-session-id'];
262
+ const handleSessionRequest = async (request, response) => {
263
+ const sessionHeader = request.headers['mcp-session-id'];
252
264
  const sessionId = typeof sessionHeader === 'string' ? sessionHeader : undefined;
253
265
  if (!(sessionId && transports.has(sessionId))) {
254
- sendInvalidSession(res);
266
+ sendInvalidSession(response);
255
267
  return;
256
268
  }
257
269
  const transport = transports.get(sessionId);
258
- await transport.handleRequest(req, res);
270
+ await transport.handleRequest(request, response);
259
271
  };
260
272
  const mcpMiddleware = authMiddleware ? [authMiddleware, mcpPostHandler] : [mcpPostHandler];
261
273
  const sessionMiddleware = authMiddleware