ebay-mcp 1.13.2 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +3 -3
- package/README.es.md +3 -3
- package/README.fr.md +3 -3
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +163 -41
- package/README.pt-BR.md +3 -3
- package/README.ru.md +3 -3
- package/README.zh-CN.md +3 -3
- package/build/api/account-management/account.js +633 -297
- package/build/api/analytics-and-report/analytics.js +111 -65
- package/build/api/client.js +199 -140
- package/build/api/clientRequestError.js +23 -0
- package/build/api/clientTrading.js +213 -0
- package/build/api/clientTradingError.js +4 -0
- package/build/api/communication/feedback.js +147 -59
- package/build/api/communication/message.js +134 -81
- package/build/api/communication/negotiation.js +65 -53
- package/build/api/communication/notification.js +458 -164
- package/build/api/developer/developer.js +99 -45
- package/build/api/index.js +23 -8
- package/build/api/listing-management/inventory.js +884 -338
- package/build/api/listing-metadata/metadata.js +416 -170
- package/build/api/listing-metadata/taxonomy.js +187 -42
- package/build/api/marketing-and-promotions/marketing.js +1392 -598
- package/build/api/marketing-and-promotions/recommendation.js +49 -24
- package/build/api/order-management/dispute.js +226 -45
- package/build/api/order-management/fulfillment.js +144 -71
- package/build/api/order-management/orderIssueHelpers.js +61 -0
- package/build/api/other/compliance.js +50 -38
- package/build/api/other/edelivery.js +603 -159
- package/build/api/other/finding.js +280 -0
- package/build/api/other/identity.js +30 -15
- package/build/api/other/translation.js +29 -14
- package/build/api/other/vero.js +113 -27
- package/build/api/rateLimitTracker.js +55 -0
- package/build/api/shared/request.js +189 -98
- package/build/api/trading/trading.js +154 -87
- package/build/auth/credentialSession.js +250 -0
- package/build/auth/oauth.js +298 -171
- package/build/auth/{oauth-metadata.js → oauthMetadata.js} +22 -6
- package/build/auth/oauthMiddleware.js +104 -0
- package/build/auth/{scope-utils.js → scopeUtils.js} +101 -28
- package/build/auth/tokenVerifier.js +243 -0
- package/build/auth/tokenVerifierError.js +21 -0
- package/build/config/environment.js +148 -113
- package/build/config/{tool-families.js → toolFamilies.js} +30 -14
- package/build/index.js +25 -19
- package/build/mcp/{http-transport.js → httpTransport.js} +102 -52
- package/build/mcp/readOnlyFilter.js +67 -0
- package/build/mcp/runtime.js +31 -13
- package/build/mcp/{tool-gating.js → toolGating.js} +77 -49
- package/build/mcp/{ui-bridge.js → uiBridge.js} +80 -23
- package/build/schemas/account-management/account.js +144 -6
- package/build/schemas/analytics/analytics.js +33 -19
- package/build/schemas/communication/messages.js +12 -42
- package/build/schemas/developer/developer.js +135 -0
- package/build/schemas/fulfillment/orders.js +183 -21
- package/build/schemas/index.js +27 -19
- package/build/schemas/inventory-management/inventory.js +17 -9
- package/build/schemas/marketing/marketing.js +480 -25
- package/build/schemas/metadata/metadata.js +11 -6
- package/build/schemas/other/finding.js +27 -0
- package/build/schemas/other/otherApis.js +424 -0
- package/build/schemas/taxonomy/taxonomy.js +11 -5
- package/build/scripts/{auto-setup.js → autoSetup.js} +87 -74
- package/build/scripts/{build-ui.js → buildUi.js} +6 -1
- package/build/scripts/{dev-sync.js → devSync.js} +169 -110
- package/build/scripts/diagnostics.js +52 -44
- package/build/scripts/{download-specs.js → downloadSpecs.js} +29 -21
- package/build/scripts/{interactive-setup.js → interactiveSetup.js} +84 -78
- package/build/scripts/setup.js +241 -178
- package/build/scripts/{setup-shared.js → setupShared.js} +29 -7
- package/build/scripts/skills.js +35 -27
- package/build/scripts/updateApiStatusDoc.js +71 -0
- package/build/serverHttp.js +90 -0
- package/build/skills/content.js +124 -118
- package/build/skills/index.js +22 -10
- package/build/skills/install.js +21 -10
- package/build/skills/markers.js +50 -15
- package/build/skills/{registry-snapshot.js → registrySnapshot.js} +8 -2
- package/build/skills/render.js +41 -14
- package/build/skills/targets.js +49 -17
- package/build/tools/categories/account.js +166 -264
- package/build/tools/categories/analytics.js +11 -22
- package/build/tools/categories/browse.js +17 -0
- package/build/tools/categories/communication.js +124 -399
- package/build/tools/categories/connector.js +23 -20
- package/build/tools/categories/developer.js +46 -159
- package/build/tools/categories/fulfillment.js +84 -215
- package/build/tools/categories/index.js +6 -14
- package/build/tools/categories/inventory.js +238 -293
- package/build/tools/categories/marketing.js +677 -39
- package/build/tools/categories/metadata.js +190 -117
- package/build/tools/categories/other.js +81 -208
- package/build/tools/categories/taxonomy.js +29 -20
- package/build/tools/categories/{token-management.js → tokenManagement.js} +187 -192
- package/build/tools/categories/trading.js +16 -47
- package/build/tools/contracts.js +34 -24
- package/build/tools/{define-tool.js → defineTool.js} +44 -29
- package/build/tools/registry.js +87 -29
- package/build/tools/schemas.js +3 -57
- package/build/tools/ui/archetypes.js +1 -0
- package/build/tools/ui/{map-helpers.js → mapHelpers.js} +69 -19
- package/build/tools/ui/maps.js +294 -80
- package/build/types/index.js +1 -1
- package/build/ui/card.html +14 -14
- package/build/ui/chart.html +14 -14
- package/build/ui/stat.html +87 -0
- package/build/ui/table.html +14 -14
- package/build/utils/apiStatusFeed.js +95 -0
- package/build/utils/cliOutput.js +15 -0
- package/build/utils/cliUi.js +108 -0
- package/build/utils/communication/feedback.js +57 -77
- package/build/utils/communication/message.js +69 -98
- package/build/utils/communication/negotiation.js +30 -57
- package/build/utils/communication/notification.js +112 -252
- package/build/utils/{date-converter.js → dateConverter.js} +62 -30
- package/build/utils/effectSchema.js +203 -0
- package/build/utils/effectSchemaAdapter.js +121 -0
- package/build/utils/effectSchemaChainMethods.js +146 -0
- package/build/utils/effectSchemaRefinements.js +104 -0
- package/build/utils/effectSchemaStringMethods.js +63 -0
- package/build/utils/effectSchemaTypes.js +47 -0
- package/build/utils/{env-parser.js → envParser.js} +10 -2
- package/build/utils/errors.js +7 -3
- package/build/utils/http.js +148 -89
- package/build/utils/{llm-client-detector.js → llmClientDetector.js} +295 -274
- package/build/utils/logger.js +127 -51
- package/build/utils/{oauth-helper.js → oauthHelper.js} +127 -90
- package/build/utils/other/compliance.js +18 -38
- package/build/utils/other/identity.js +2 -2
- package/build/utils/other/translation.js +4 -4
- package/build/utils/other/vero.js +36 -20
- package/build/utils/schemaHelpers.js +22 -0
- package/build/utils/scopeHelper.js +270 -0
- package/build/utils/securityChecker.js +316 -0
- package/build/utils/{setup-validator.js → setupValidator.js} +113 -94
- package/build/utils/{setup-wizard.js → setupWizard.js} +57 -46
- package/build/utils/{token-utils.js → tokenUtils.js} +2 -2
- package/build/utils/trading/trading.js +35 -0
- package/build/utils/typeGuards.js +12 -0
- package/build/utils/version.js +65 -29
- package/package.json +10 -8
- package/build/api/client-trading.js +0 -124
- package/build/api/communication/shared.js +0 -59
- package/build/api/shared/query-params.js +0 -16
- package/build/auth/credential-session.js +0 -166
- package/build/auth/oauth-middleware.js +0 -100
- package/build/auth/token-verifier.js +0 -186
- package/build/schemas/other/other-apis.js +0 -390
- package/build/scripts/update-api-status-doc.js +0 -44
- package/build/server-http.js +0 -85
- package/build/tools/definitions/marketing.js +0 -1381
- package/build/tools/tool-handlers/marketing.js +0 -312
- package/build/utils/account-management/account.js +0 -818
- package/build/utils/api-status-feed.js +0 -84
- package/build/utils/cli-ui.js +0 -47
- package/build/utils/order-management/dispute.js +0 -369
- package/build/utils/order-management/fulfillment.js +0 -205
- package/build/utils/other/edelivery.js +0 -235
- package/build/utils/schema-helpers.js +0 -13
- package/build/utils/scope-helper.js +0 -207
- package/build/utils/security-checker.js +0 -248
- package/build/utils/type-guards.js +0 -6
- /package/build/auth/{oauth-types.js → oauthTypes.js} +0 -0
- /package/build/{server-http.d.ts → serverHttp.d.ts} +0 -0
- /package/build/tools/ui/{view-models.js → viewModels.js} +0 -0
- /package/build/types/{ebay-enums.js → ebayEnums.js} +0 -0
package/build/scripts/setup.js
CHANGED
|
@@ -4,20 +4,22 @@ import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
|
4
4
|
import { homedir, platform } from 'os';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
|
+
import { Data, Effect, Either } from 'effect';
|
|
7
8
|
import { checkForUpdates } from '../utils/version.js';
|
|
8
9
|
import { describeHttpError, httpRequest } from '../utils/http.js';
|
|
9
10
|
import { config } from 'dotenv';
|
|
10
11
|
import { fileURLToPath } from 'url';
|
|
11
12
|
import { getOAuthAuthorizationUrl } from '../config/environment.js';
|
|
12
|
-
import { startCallbackServer } from '../utils/
|
|
13
|
-
import { defineWizard, runWizard
|
|
14
|
-
import { loadExistingConfig, quoteEnvValue } from './
|
|
13
|
+
import { startCallbackServer } from '../utils/oauthHelper.js';
|
|
14
|
+
import { defineWizard, runWizard } from '../utils/setupWizard.js';
|
|
15
|
+
import { loadExistingConfig, quoteEnvValue } from './setupShared.js';
|
|
15
16
|
import { runSkillsWizard } from './skills.js';
|
|
16
17
|
import prompts from 'prompts';
|
|
17
|
-
import { configureLLMClient, detectLLMClients } from '../utils/
|
|
18
|
-
import { runSecurityChecks, displaySecurityResults } from '../utils/
|
|
19
|
-
import { validateSetup, displayRecommendations } from '../utils/
|
|
18
|
+
import { configureLLMClient, detectLLMClients } from '../utils/llmClientDetector.js';
|
|
19
|
+
import { runSecurityChecks, displaySecurityResults } from '../utils/securityChecker.js';
|
|
20
|
+
import { validateSetup, displayRecommendations } from '../utils/setupValidator.js';
|
|
20
21
|
import { getErrorMessage } from '../utils/errors.js';
|
|
22
|
+
import process from 'node:process';
|
|
21
23
|
config({ quiet: true });
|
|
22
24
|
checkForUpdates();
|
|
23
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -67,6 +69,9 @@ const ui = {
|
|
|
67
69
|
};
|
|
68
70
|
const DEVELOPER_NAME = 'Yosef Hayim Sabag';
|
|
69
71
|
const DEVELOPER_LINKEDIN = 'https://www.linkedin.com/in/yosef-hayim-sabag/';
|
|
72
|
+
/** Tagged failure raised while the setup wizard captures the OAuth redirect. */
|
|
73
|
+
class SetupCaptureError extends Data.TaggedError('SetupCaptureError') {
|
|
74
|
+
}
|
|
70
75
|
/** Render the wizard's branding header: logo, title, and developer credit. */
|
|
71
76
|
function printWizardBanner() {
|
|
72
77
|
console.log(LOGO);
|
|
@@ -79,29 +84,26 @@ function printWizardBanner() {
|
|
|
79
84
|
function parseAuthorizationCode(input) {
|
|
80
85
|
const trimmed = input.trim();
|
|
81
86
|
if (trimmed.includes('code=') || trimmed.includes('?') || trimmed.includes('&')) {
|
|
82
|
-
try
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
catch {
|
|
95
|
-
// fall through
|
|
87
|
+
const parsedCode = Effect.runSync(Effect.either(Effect.try({
|
|
88
|
+
try: () => {
|
|
89
|
+
const searchParams = trimmed.startsWith('http')
|
|
90
|
+
? new URL(trimmed).searchParams
|
|
91
|
+
: new URLSearchParams(trimmed.startsWith('?') ? trimmed.slice(1) : trimmed);
|
|
92
|
+
const code = searchParams.get('code');
|
|
93
|
+
return code ? decodeURIComponent(code) : null;
|
|
94
|
+
},
|
|
95
|
+
catch: (error) => error,
|
|
96
|
+
})));
|
|
97
|
+
if (Either.isRight(parsedCode) && parsedCode.right) {
|
|
98
|
+
return parsedCode.right;
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
if (trimmed.startsWith('v^1.1#') || trimmed.startsWith('v%5E1.1')) {
|
|
99
|
-
try
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
102
|
+
const decodedToken = Effect.runSync(Effect.either(Effect.try({
|
|
103
|
+
try: () => decodeURIComponent(trimmed),
|
|
104
|
+
catch: (error) => error,
|
|
105
|
+
})));
|
|
106
|
+
return Either.isRight(decodedToken) ? decodedToken.right : trimmed;
|
|
105
107
|
}
|
|
106
108
|
return null;
|
|
107
109
|
}
|
|
@@ -210,63 +212,69 @@ function updateClaudeDesktopConfig(envConfig, environment) {
|
|
|
210
212
|
const configPath = getClaudeDesktopConfigPath();
|
|
211
213
|
if (!existsSync(dirname(configPath)))
|
|
212
214
|
return { success: false, configPath, error: 'Claude Desktop not installed' };
|
|
213
|
-
try
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
const updated = Effect.runSync(Effect.either(Effect.try({
|
|
216
|
+
try: () => {
|
|
217
|
+
let existing = {};
|
|
218
|
+
if (existsSync(configPath)) {
|
|
219
|
+
const parsed = Effect.runSync(Effect.either(Effect.try({
|
|
220
|
+
try: () => JSON.parse(readFileSync(configPath, 'utf-8')),
|
|
221
|
+
catch: (error) => error,
|
|
222
|
+
})));
|
|
223
|
+
if (Either.isLeft(parsed)) {
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
configPath,
|
|
227
|
+
error: `Invalid JSON in config: ${getErrorMessage(parsed.left, 'parse error')}`,
|
|
228
|
+
details: 'Please fix the JSON syntax in your Claude config file',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
existing = parsed.right;
|
|
226
232
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
233
|
+
if (!existing.mcpServers || typeof existing.mcpServers !== 'object')
|
|
234
|
+
existing.mcpServers = {};
|
|
235
|
+
const mcpServers = existing.mcpServers;
|
|
236
|
+
const envVars = { EBAY_ENVIRONMENT: environment };
|
|
237
|
+
if (envConfig.EBAY_CLIENT_ID)
|
|
238
|
+
envVars.EBAY_CLIENT_ID = envConfig.EBAY_CLIENT_ID;
|
|
239
|
+
if (envConfig.EBAY_CLIENT_SECRET)
|
|
240
|
+
envVars.EBAY_CLIENT_SECRET = envConfig.EBAY_CLIENT_SECRET;
|
|
241
|
+
if (envConfig.EBAY_REDIRECT_URI)
|
|
242
|
+
envVars.EBAY_REDIRECT_URI = envConfig.EBAY_REDIRECT_URI;
|
|
243
|
+
if (envConfig.EBAY_MARKETPLACE_ID)
|
|
244
|
+
envVars.EBAY_MARKETPLACE_ID = envConfig.EBAY_MARKETPLACE_ID;
|
|
245
|
+
if (envConfig.EBAY_CONTENT_LANGUAGE)
|
|
246
|
+
envVars.EBAY_CONTENT_LANGUAGE = envConfig.EBAY_CONTENT_LANGUAGE;
|
|
247
|
+
if (envConfig.EBAY_USER_REFRESH_TOKEN)
|
|
248
|
+
envVars.EBAY_USER_REFRESH_TOKEN = envConfig.EBAY_USER_REFRESH_TOKEN;
|
|
249
|
+
if (envConfig.EBAY_USER_ACCESS_TOKEN?.startsWith('v^'))
|
|
250
|
+
envVars.EBAY_USER_ACCESS_TOKEN = envConfig.EBAY_USER_ACCESS_TOKEN;
|
|
251
|
+
if (envConfig.EBAY_APP_ACCESS_TOKEN?.startsWith('v^'))
|
|
252
|
+
envVars.EBAY_APP_ACCESS_TOKEN = envConfig.EBAY_APP_ACCESS_TOKEN;
|
|
253
|
+
mcpServers.ebay = {
|
|
254
|
+
command: 'npx',
|
|
255
|
+
args: ['--yes', '--quiet', 'ebay-mcp'],
|
|
256
|
+
env: { ...envVars, NODE_NO_WARNINGS: '1', NPM_CONFIG_UPDATE_NOTIFIER: 'false' },
|
|
257
|
+
};
|
|
258
|
+
writeFileSync(configPath, JSON.stringify(existing, null, 2));
|
|
259
|
+
const otherServers = Object.keys(mcpServers).filter((k) => k !== 'ebay');
|
|
260
|
+
return {
|
|
261
|
+
success: true,
|
|
262
|
+
configPath,
|
|
263
|
+
details: otherServers.length > 0
|
|
264
|
+
? `Preserved ${otherServers.length} existing server(s): ${otherServers.join(', ')}`
|
|
265
|
+
: `Added ebay server (${Object.keys(mcpServers).length} total)`,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
catch: (error) => error,
|
|
269
|
+
})));
|
|
270
|
+
if (Either.isLeft(updated)) {
|
|
264
271
|
return {
|
|
265
272
|
success: false,
|
|
266
273
|
configPath,
|
|
267
|
-
error: getErrorMessage(
|
|
274
|
+
error: getErrorMessage(updated.left, 'Unknown'),
|
|
268
275
|
};
|
|
269
276
|
}
|
|
277
|
+
return updated.right;
|
|
270
278
|
}
|
|
271
279
|
function formatDate(date) {
|
|
272
280
|
return date.toLocaleString('en-US', {
|
|
@@ -333,8 +341,8 @@ function showSetupProgress(message) {
|
|
|
333
341
|
function displayUserInfo(userInfo) {
|
|
334
342
|
const name = userInfo.individualAccount?.firstName && userInfo.individualAccount?.lastName
|
|
335
343
|
? `${userInfo.individualAccount.firstName} ${userInfo.individualAccount.lastName}`
|
|
336
|
-
: userInfo.businessAccount?.name || '
|
|
337
|
-
const email = userInfo.individualAccount?.email || userInfo.businessAccount?.email || '
|
|
344
|
+
: userInfo.businessAccount?.name || 'No account name';
|
|
345
|
+
const email = userInfo.individualAccount?.email || userInfo.businessAccount?.email || 'No account email';
|
|
338
346
|
const marketplaceMap = {
|
|
339
347
|
EBAY_US: 'eBay United States',
|
|
340
348
|
EBAY_GB: 'eBay United Kingdom',
|
|
@@ -347,12 +355,12 @@ function displayUserInfo(userInfo) {
|
|
|
347
355
|
};
|
|
348
356
|
const marketplace = marketplaceMap[userInfo.registrationMarketplaceId || ''] ||
|
|
349
357
|
userInfo.registrationMarketplaceId ||
|
|
350
|
-
'
|
|
358
|
+
'No marketplace';
|
|
351
359
|
console.log(` ${ui.dim('┌─ eBay Account Verified ─────────────────────────')}`);
|
|
352
360
|
console.log(` ${ui.dim('│')} Username: ${userInfo.username}`);
|
|
353
361
|
console.log(` ${ui.dim('│')} Name: ${name}`);
|
|
354
362
|
console.log(` ${ui.dim('│')} Email: ${email}`);
|
|
355
|
-
console.log(` ${ui.dim('│')} Type: ${userInfo.accountType || '
|
|
363
|
+
console.log(` ${ui.dim('│')} Type: ${userInfo.accountType || 'No account type'}`);
|
|
356
364
|
console.log(` ${ui.dim('│')} Marketplace: ${marketplace}`);
|
|
357
365
|
console.log(` ${ui.dim('└─────────────────────────────────────────────────')}`);
|
|
358
366
|
console.log('');
|
|
@@ -372,7 +380,7 @@ const DEFAULT_CALLBACK_PORT = 3000;
|
|
|
372
380
|
function getCallbackPort() {
|
|
373
381
|
const raw = process.env.EBAY_OAUTH_CALLBACK_PORT;
|
|
374
382
|
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
375
|
-
return Number.isInteger(parsed) && parsed > 0 && parsed <
|
|
383
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed < 65_536 ? parsed : DEFAULT_CALLBACK_PORT;
|
|
376
384
|
}
|
|
377
385
|
/**
|
|
378
386
|
* Exchange an authorization code for tokens, verify the eBay account, mint an app
|
|
@@ -382,62 +390,76 @@ function getCallbackPort() {
|
|
|
382
390
|
async function completeOAuthWithCode(authCode, params, tokens) {
|
|
383
391
|
const { clientId, clientSecret, redirectUri, environment, answers } = params;
|
|
384
392
|
const stopSpinner = showSetupProgress('Exchanging authorization code for tokens');
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
try {
|
|
392
|
-
const userInfo = await fetchEbayUserInfo(result.accessToken, environment);
|
|
393
|
-
showSuccess('Account verified!');
|
|
394
|
-
displayUserInfo(userInfo);
|
|
395
|
-
}
|
|
396
|
-
catch (userError) {
|
|
397
|
-
const userMsg = describeHttpError(userError, 'Unknown');
|
|
398
|
-
showWarning(`Could not fetch account info: ${userMsg}`);
|
|
399
|
-
if (userMsg.toLowerCase().includes('access denied'))
|
|
400
|
-
showInfo('Normal if RuName lacks commerce.identity.readonly scope — tokens are valid.');
|
|
401
|
-
}
|
|
402
|
-
try {
|
|
403
|
-
tokens.appAccessToken = await getAppAccessToken(clientId, clientSecret, environment);
|
|
404
|
-
showSuccess('App access token obtained.');
|
|
405
|
-
}
|
|
406
|
-
catch {
|
|
407
|
-
showWarning('Could not obtain app access token (user tokens still work).');
|
|
408
|
-
}
|
|
409
|
-
showInfo(`Access token expires in: ${Math.floor(result.expiresIn / 60)} minutes`);
|
|
410
|
-
showInfo(`Refresh token expires in: ${Math.floor(result.refreshTokenExpiresIn / 60 / 60 / 24)} days`);
|
|
411
|
-
if (isClaudeDesktopInstalled()) {
|
|
412
|
-
const r = updateClaudeDesktopConfig({
|
|
413
|
-
...answers,
|
|
414
|
-
EBAY_USER_REFRESH_TOKEN: tokens.refreshToken ?? '',
|
|
415
|
-
EBAY_USER_ACCESS_TOKEN: tokens.accessToken ?? '',
|
|
416
|
-
}, environment);
|
|
417
|
-
if (r.success) {
|
|
418
|
-
showSuccess('Claude Desktop config updated.');
|
|
419
|
-
if (r.details)
|
|
420
|
-
showInfo(r.details);
|
|
421
|
-
}
|
|
422
|
-
else
|
|
423
|
-
showWarning(`Could not update Claude Desktop: ${r.error}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
stopSpinner();
|
|
428
|
-
const msg = describeHttpError(error, 'Unknown error');
|
|
393
|
+
const exchanged = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
394
|
+
try: () => exchangeAuthorizationCode(authCode, clientId, clientSecret, redirectUri, environment),
|
|
395
|
+
catch: (error) => error,
|
|
396
|
+
}).pipe(Effect.ensuring(Effect.sync(stopSpinner)))));
|
|
397
|
+
if (Either.isLeft(exchanged)) {
|
|
398
|
+
const msg = describeHttpError(exchanged.left, 'Unknown error');
|
|
429
399
|
showError(`Failed to exchange code: ${msg}`);
|
|
430
400
|
console.log(' Common issues:');
|
|
431
401
|
console.log(' • Authorization code expired (codes are valid for ~5 minutes)');
|
|
432
402
|
console.log(' • Code was already used (each code can only be used once)');
|
|
433
403
|
console.log(' • Redirect URI mismatch\n');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const result = exchanged.right;
|
|
407
|
+
showSuccess('Authorization code exchanged successfully!');
|
|
408
|
+
tokens.refreshToken = result.refreshToken;
|
|
409
|
+
tokens.accessToken = result.accessToken;
|
|
410
|
+
const user = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
411
|
+
try: () => fetchEbayUserInfo(result.accessToken, environment),
|
|
412
|
+
catch: (error) => error,
|
|
413
|
+
})));
|
|
414
|
+
if (Either.isRight(user)) {
|
|
415
|
+
showSuccess('Account verified!');
|
|
416
|
+
displayUserInfo(user.right);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
const userMsg = describeHttpError(user.left, 'Unknown');
|
|
420
|
+
showWarning(`Could not fetch account info: ${userMsg}`);
|
|
421
|
+
if (userMsg.toLowerCase().includes('access denied'))
|
|
422
|
+
showInfo('Normal if RuName lacks commerce.identity.readonly scope — tokens are valid.');
|
|
423
|
+
}
|
|
424
|
+
const appToken = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
425
|
+
try: () => getAppAccessToken(clientId, clientSecret, environment),
|
|
426
|
+
catch: (error) => error,
|
|
427
|
+
})));
|
|
428
|
+
if (Either.isRight(appToken)) {
|
|
429
|
+
tokens.appAccessToken = appToken.right;
|
|
430
|
+
showSuccess('App access token obtained.');
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
showWarning('Could not obtain app access token (user tokens still work).');
|
|
434
|
+
}
|
|
435
|
+
showInfo(`Access token expires in: ${Math.floor(result.expiresIn / 60)} minutes`);
|
|
436
|
+
showInfo(`Refresh token expires in: ${Math.floor(result.refreshTokenExpiresIn / 60 / 60 / 24)} days`);
|
|
437
|
+
if (isClaudeDesktopInstalled()) {
|
|
438
|
+
const r = updateClaudeDesktopConfig({
|
|
439
|
+
...answers,
|
|
440
|
+
EBAY_USER_REFRESH_TOKEN: tokens.refreshToken ?? '',
|
|
441
|
+
EBAY_USER_ACCESS_TOKEN: tokens.accessToken ?? '',
|
|
442
|
+
}, environment);
|
|
443
|
+
if (r.success) {
|
|
444
|
+
showSuccess('Claude Desktop config updated.');
|
|
445
|
+
if (r.details)
|
|
446
|
+
showInfo(r.details);
|
|
447
|
+
}
|
|
448
|
+
else
|
|
449
|
+
showWarning(`Could not update Claude Desktop: ${r.error}`);
|
|
434
450
|
}
|
|
435
451
|
}
|
|
436
452
|
// ─── Setup wizard ─────────────────────────────────────────────────────────────
|
|
437
453
|
/**
|
|
438
454
|
* Runs the interactive eBay MCP setup wizard from the script entry point.
|
|
455
|
+
*
|
|
456
|
+
* @returns A promise that resolves after setup completes.
|
|
457
|
+
* @example
|
|
458
|
+
* ```ts
|
|
459
|
+
* await runSetup();
|
|
460
|
+
* ```
|
|
439
461
|
*/
|
|
440
|
-
export async
|
|
462
|
+
export const runSetup = async () => {
|
|
441
463
|
const existingConfig = loadExistingConfig(PROJECT_ROOT);
|
|
442
464
|
const detectedClients = detectLLMClients();
|
|
443
465
|
const availableClients = detectedClients.filter((c) => c.detected);
|
|
@@ -604,8 +626,6 @@ export async function runSetup() {
|
|
|
604
626
|
],
|
|
605
627
|
});
|
|
606
628
|
const answers = await runWizard(wizardConfig, {
|
|
607
|
-
renderer: new ClackRenderer(),
|
|
608
|
-
quiet: true,
|
|
609
629
|
optionsProvider: (stepId) => {
|
|
610
630
|
if (stepId === 'oauth-method') {
|
|
611
631
|
const hasToken = existingConfig.EBAY_USER_REFRESH_TOKEN?.startsWith('v^1.1#');
|
|
@@ -623,7 +643,6 @@ export async function runSetup() {
|
|
|
623
643
|
];
|
|
624
644
|
}
|
|
625
645
|
}
|
|
626
|
-
return undefined;
|
|
627
646
|
},
|
|
628
647
|
asyncValidate: (stepId, value) => {
|
|
629
648
|
if (stepId === 'oauth-token') {
|
|
@@ -658,18 +677,25 @@ export async function runSetup() {
|
|
|
658
677
|
switch (method) {
|
|
659
678
|
case 'keep': {
|
|
660
679
|
const stopSpinner = showSetupProgress('Verifying existing refresh token...');
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
680
|
+
const verified = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
681
|
+
try: () => verifyRefreshToken(existingConfig.EBAY_USER_REFRESH_TOKEN, clientId, clientSecret, environment),
|
|
682
|
+
catch: (error) => error,
|
|
683
|
+
}).pipe(Effect.ensuring(Effect.sync(stopSpinner)))));
|
|
684
|
+
if (Either.isRight(verified)) {
|
|
685
|
+
const { accessToken, userInfo } = verified.right;
|
|
664
686
|
showSuccess('Refresh token verified!');
|
|
665
687
|
tokens.refreshToken = existingConfig.EBAY_USER_REFRESH_TOKEN;
|
|
666
688
|
tokens.accessToken = accessToken;
|
|
667
689
|
displayUserInfo(userInfo);
|
|
668
|
-
|
|
669
|
-
|
|
690
|
+
const appToken = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
691
|
+
try: () => getAppAccessToken(clientId, clientSecret, environment),
|
|
692
|
+
catch: (error) => error,
|
|
693
|
+
})));
|
|
694
|
+
if (Either.isRight(appToken)) {
|
|
695
|
+
tokens.appAccessToken = appToken.right;
|
|
670
696
|
showSuccess('App access token obtained.');
|
|
671
697
|
}
|
|
672
|
-
|
|
698
|
+
else {
|
|
673
699
|
showWarning('Could not obtain app access token (user tokens still work).');
|
|
674
700
|
}
|
|
675
701
|
if (isClaudeDesktopInstalled()) {
|
|
@@ -686,9 +712,8 @@ export async function runSetup() {
|
|
|
686
712
|
showWarning(`Could not update Claude Desktop: ${r.error}`);
|
|
687
713
|
}
|
|
688
714
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const msg = describeHttpError(error, 'Unknown error');
|
|
715
|
+
else {
|
|
716
|
+
const msg = describeHttpError(verified.left, 'Unknown error');
|
|
692
717
|
showError(`Token verification failed: ${msg}`);
|
|
693
718
|
if (msg.toLowerCase().includes('access denied'))
|
|
694
719
|
showWarning('Token may be missing required OAuth scopes.');
|
|
@@ -720,24 +745,52 @@ export async function runSetup() {
|
|
|
720
745
|
"The tunnel forwards eBay's redirect here, where the code is captured.",
|
|
721
746
|
'Override the local port with EBAY_OAUTH_CALLBACK_PORT.',
|
|
722
747
|
].join('\n'));
|
|
723
|
-
const authUrl = getOAuthAuthorizationUrl(clientId, redirectUri, environment, undefined,
|
|
748
|
+
const authUrl = getOAuthAuthorizationUrl(clientId, redirectUri, environment, undefined, state);
|
|
724
749
|
let server;
|
|
725
750
|
const stopSpinner = showSetupProgress('Waiting for eBay to redirect back (up to 5 min)');
|
|
726
|
-
|
|
727
|
-
const started =
|
|
728
|
-
|
|
751
|
+
const captured = await Effect.runPromise(Effect.either(Effect.gen(function* () {
|
|
752
|
+
const started = yield* Effect.tryPromise({
|
|
753
|
+
try: () => startCallbackServer(callbackPort, 300_000, {
|
|
754
|
+
expectedState: state,
|
|
755
|
+
}),
|
|
756
|
+
catch: (cause) => new SetupCaptureError({
|
|
757
|
+
message: 'Failed to start OAuth callback server',
|
|
758
|
+
cause,
|
|
759
|
+
}),
|
|
729
760
|
});
|
|
730
761
|
server = started.server;
|
|
731
|
-
|
|
762
|
+
yield* Effect.tryPromise({
|
|
763
|
+
try: () => context.openBrowser(authUrl),
|
|
764
|
+
catch: (cause) => new SetupCaptureError({
|
|
765
|
+
message: 'Failed to open OAuth authorization URL',
|
|
766
|
+
cause,
|
|
767
|
+
}),
|
|
768
|
+
});
|
|
732
769
|
showInfo('1. Sign in to your eBay account in the browser');
|
|
733
770
|
showInfo('2. Grant permissions to your app');
|
|
734
771
|
showInfo('3. eBay redirects back automatically — no copy-paste needed');
|
|
735
|
-
const result =
|
|
772
|
+
const result = yield* Effect.tryPromise({
|
|
773
|
+
try: () => started.codePromise,
|
|
774
|
+
catch: (cause) => new SetupCaptureError({
|
|
775
|
+
message: 'Failed while waiting for eBay OAuth redirect',
|
|
776
|
+
cause,
|
|
777
|
+
}),
|
|
778
|
+
});
|
|
779
|
+
if (!result.code) {
|
|
780
|
+
return yield* Effect.fail(new SetupCaptureError({
|
|
781
|
+
message: result.errorDescription ??
|
|
782
|
+
result.error ??
|
|
783
|
+
'No authorization code received',
|
|
784
|
+
}));
|
|
785
|
+
}
|
|
786
|
+
return result.code;
|
|
787
|
+
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
736
788
|
stopSpinner();
|
|
737
|
-
|
|
738
|
-
|
|
789
|
+
server?.close();
|
|
790
|
+
})))));
|
|
791
|
+
if (Either.isRight(captured)) {
|
|
739
792
|
showSuccess('Authorization code captured automatically!');
|
|
740
|
-
await completeOAuthWithCode(
|
|
793
|
+
await completeOAuthWithCode(captured.right, {
|
|
741
794
|
clientId,
|
|
742
795
|
clientSecret,
|
|
743
796
|
redirectUri,
|
|
@@ -746,16 +799,12 @@ export async function runSetup() {
|
|
|
746
799
|
}, tokens);
|
|
747
800
|
context.setNextStep(finalStepId);
|
|
748
801
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
showError(`Auto-capture failed: ${getErrorMessage(error, 'Unknown error')}`);
|
|
802
|
+
else {
|
|
803
|
+
showError(`Auto-capture failed: ${getErrorMessage(captured.left, 'Unknown error')}`);
|
|
752
804
|
showInfo('Falling back to manual paste — copy the redirect URL from your browser.');
|
|
753
805
|
context.showNote('OAuth Authorization URL', authUrl);
|
|
754
806
|
context.setNextStep('oauth-code');
|
|
755
807
|
}
|
|
756
|
-
finally {
|
|
757
|
-
server?.close();
|
|
758
|
-
}
|
|
759
808
|
break;
|
|
760
809
|
}
|
|
761
810
|
case 'manual': {
|
|
@@ -785,17 +834,24 @@ export async function runSetup() {
|
|
|
785
834
|
.replace(/^["']|["']$/g, '');
|
|
786
835
|
tokens.refreshToken = rawToken;
|
|
787
836
|
const stopSpinner = showSetupProgress('Verifying refresh token...');
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
837
|
+
const verified = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
838
|
+
try: () => verifyRefreshToken(rawToken, clientId, clientSecret, environment),
|
|
839
|
+
catch: (error) => error,
|
|
840
|
+
}).pipe(Effect.ensuring(Effect.sync(stopSpinner)))));
|
|
841
|
+
if (Either.isRight(verified)) {
|
|
842
|
+
const { accessToken, userInfo } = verified.right;
|
|
791
843
|
showSuccess('Refresh token verified!');
|
|
792
844
|
tokens.accessToken = accessToken;
|
|
793
845
|
displayUserInfo(userInfo);
|
|
794
|
-
|
|
795
|
-
|
|
846
|
+
const appToken = await Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
847
|
+
try: () => getAppAccessToken(clientId, clientSecret, environment),
|
|
848
|
+
catch: (error) => error,
|
|
849
|
+
})));
|
|
850
|
+
if (Either.isRight(appToken)) {
|
|
851
|
+
tokens.appAccessToken = appToken.right;
|
|
796
852
|
showSuccess('App access token obtained.');
|
|
797
853
|
}
|
|
798
|
-
|
|
854
|
+
else {
|
|
799
855
|
showWarning('Could not obtain app access token (user tokens still work).');
|
|
800
856
|
}
|
|
801
857
|
if (isClaudeDesktopInstalled()) {
|
|
@@ -809,9 +865,8 @@ export async function runSetup() {
|
|
|
809
865
|
showWarning(`Could not update Claude Desktop: ${r.error}`);
|
|
810
866
|
}
|
|
811
867
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const msg = describeHttpError(error, 'Unknown error');
|
|
868
|
+
else {
|
|
869
|
+
const msg = describeHttpError(verified.left, 'Unknown error');
|
|
815
870
|
showError(`Token verification failed: ${msg}`);
|
|
816
871
|
showWarning("Token saved anyway — you may need to re-authenticate if it doesn't work.");
|
|
817
872
|
}
|
|
@@ -864,6 +919,13 @@ export async function runSetup() {
|
|
|
864
919
|
? answers['content-language-custom']
|
|
865
920
|
: answers['content-language'];
|
|
866
921
|
const environment = answers.environment;
|
|
922
|
+
const refreshTokenConfig = {};
|
|
923
|
+
if (tokens.refreshToken) {
|
|
924
|
+
refreshTokenConfig.EBAY_USER_REFRESH_TOKEN = tokens.refreshToken;
|
|
925
|
+
}
|
|
926
|
+
else if (existingConfig.EBAY_USER_REFRESH_TOKEN) {
|
|
927
|
+
refreshTokenConfig.EBAY_USER_REFRESH_TOKEN = existingConfig.EBAY_USER_REFRESH_TOKEN;
|
|
928
|
+
}
|
|
867
929
|
const finalConfig = {
|
|
868
930
|
EBAY_CLIENT_ID: answers['client-id'],
|
|
869
931
|
EBAY_CLIENT_SECRET: answers['client-secret'] || existingConfig.EBAY_CLIENT_SECRET || '',
|
|
@@ -871,11 +933,7 @@ export async function runSetup() {
|
|
|
871
933
|
EBAY_ENVIRONMENT: environment,
|
|
872
934
|
...(marketplaceId ? { EBAY_MARKETPLACE_ID: marketplaceId } : {}),
|
|
873
935
|
...(contentLanguage ? { EBAY_CONTENT_LANGUAGE: contentLanguage } : {}),
|
|
874
|
-
...
|
|
875
|
-
? { EBAY_USER_REFRESH_TOKEN: tokens.refreshToken }
|
|
876
|
-
: existingConfig.EBAY_USER_REFRESH_TOKEN
|
|
877
|
-
? { EBAY_USER_REFRESH_TOKEN: existingConfig.EBAY_USER_REFRESH_TOKEN }
|
|
878
|
-
: {}),
|
|
936
|
+
...refreshTokenConfig,
|
|
879
937
|
...(tokens.accessToken ? { EBAY_USER_ACCESS_TOKEN: tokens.accessToken } : {}),
|
|
880
938
|
...(tokens.appAccessToken ? { EBAY_APP_ACCESS_TOKEN: tokens.appAccessToken } : {}),
|
|
881
939
|
};
|
|
@@ -926,17 +984,17 @@ export async function runSetup() {
|
|
|
926
984
|
showInfo('You can install them later with: npm run skills');
|
|
927
985
|
}
|
|
928
986
|
}
|
|
929
|
-
}
|
|
987
|
+
};
|
|
930
988
|
// ─── Entry point ──────────────────────────────────────────────────────────────
|
|
931
|
-
|
|
989
|
+
const parseArgs = () => {
|
|
932
990
|
const args = process.argv.slice(2);
|
|
933
991
|
return {
|
|
934
992
|
help: args.includes('--help') || args.includes('-h'),
|
|
935
993
|
quick: args.includes('--quick') || args.includes('-q'),
|
|
936
994
|
diagnose: args.includes('--diagnose') || args.includes('-d'),
|
|
937
995
|
};
|
|
938
|
-
}
|
|
939
|
-
|
|
996
|
+
};
|
|
997
|
+
const showHelp = () => {
|
|
940
998
|
console.log(`
|
|
941
999
|
${chalk.bold('eBay MCP Server Setup')} ${chalk.dim('interactive setup')}
|
|
942
1000
|
|
|
@@ -953,8 +1011,8 @@ ${chalk.bold('Examples:')}
|
|
|
953
1011
|
npm run setup --quick Skip optional configuration
|
|
954
1012
|
npm run setup --diagnose Check system health
|
|
955
1013
|
`);
|
|
956
|
-
}
|
|
957
|
-
async
|
|
1014
|
+
};
|
|
1015
|
+
const main = async () => {
|
|
958
1016
|
const args = parseArgs();
|
|
959
1017
|
if (args.help) {
|
|
960
1018
|
showHelp();
|
|
@@ -970,7 +1028,7 @@ async function main() {
|
|
|
970
1028
|
process.exit(0);
|
|
971
1029
|
}
|
|
972
1030
|
await runSetup();
|
|
973
|
-
}
|
|
1031
|
+
};
|
|
974
1032
|
process.on('SIGINT', () => {
|
|
975
1033
|
console.log(ui.warning('\n\n Setup interrupted.\n'));
|
|
976
1034
|
process.exit(0);
|
|
@@ -978,8 +1036,13 @@ process.on('SIGINT', () => {
|
|
|
978
1036
|
const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
|
|
979
1037
|
const modulePath = resolve(fileURLToPath(import.meta.url));
|
|
980
1038
|
if (entryPath && modulePath === entryPath) {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1039
|
+
void Effect.runPromise(Effect.either(Effect.tryPromise({
|
|
1040
|
+
try: () => main(),
|
|
1041
|
+
catch: (error) => error,
|
|
1042
|
+
}))).then((result) => {
|
|
1043
|
+
if (Either.isLeft(result)) {
|
|
1044
|
+
console.error(ui.error('\n Setup failed:'), getErrorMessage(result.left));
|
|
1045
|
+
process.exitCode = 1;
|
|
1046
|
+
}
|
|
984
1047
|
});
|
|
985
1048
|
}
|