ebay-mcp 1.6.2 → 1.7.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.
@@ -1,19 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import { dirname, join } from 'path';
2
+ import { dirname, join, resolve } from 'path';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
4
  import { homedir, platform } from 'os';
5
5
  import axios from 'axios';
6
6
  import chalk from 'chalk';
7
+ import { checkForUpdates } from '../utils/version.js';
7
8
  import { config } from 'dotenv';
8
9
  import { exec } from 'child_process';
9
10
  import { fileURLToPath } from 'url';
11
+ import { getOAuthAuthorizationUrl } from '../config/environment.js';
10
12
  import prompts from 'prompts';
11
- import { getDefaultScopes } from '../config/environment.js';
12
13
  config({ quiet: true });
14
+ checkForUpdates();
13
15
  const __filename = fileURLToPath(import.meta.url);
14
16
  const __dirname = dirname(__filename);
15
17
  const PROJECT_ROOT = join(__dirname, '../..');
16
- const TOTAL_STEPS = 5;
18
+ const TOTAL_STEPS = 6;
17
19
  const ebay = {
18
20
  red: chalk.hex('#E53238'),
19
21
  blue: chalk.hex('#0064D2'),
@@ -29,6 +31,26 @@ const ui = {
29
31
  info: chalk.cyan,
30
32
  hint: chalk.gray,
31
33
  };
34
+ const MARKETPLACE_OPTIONS = [
35
+ { value: 'EBAY_US', label: 'EBAY_US — United States' },
36
+ { value: 'EBAY_GB', label: 'EBAY_GB — United Kingdom' },
37
+ { value: 'EBAY_DE', label: 'EBAY_DE — Germany' },
38
+ { value: 'EBAY_FR', label: 'EBAY_FR — France' },
39
+ { value: 'EBAY_IT', label: 'EBAY_IT — Italy' },
40
+ { value: 'EBAY_ES', label: 'EBAY_ES — Spain' },
41
+ { value: 'EBAY_CA', label: 'EBAY_CA — Canada' },
42
+ { value: 'EBAY_AU', label: 'EBAY_AU — Australia' },
43
+ ];
44
+ const CONTENT_LANGUAGE_OPTIONS = [
45
+ { value: 'en-US', label: 'en-US — English (United States)' },
46
+ { value: 'en-GB', label: 'en-GB — English (United Kingdom)' },
47
+ { value: 'de-DE', label: 'de-DE — German (Germany)' },
48
+ { value: 'fr-FR', label: 'fr-FR — French (France)' },
49
+ { value: 'it-IT', label: 'it-IT — Italian (Italy)' },
50
+ { value: 'es-ES', label: 'es-ES — Spanish (Spain)' },
51
+ { value: 'fr-CA', label: 'fr-CA — French (Canada)' },
52
+ { value: 'nl-BE', label: 'nl-BE — Dutch (Belgium)' },
53
+ ];
32
54
  const LOGO = `
33
55
  ${ebay.red('███████╗')}${ebay.blue('██████╗ ')}${ebay.yellow('█████╗ ')}${ebay.green('██╗ ██╗')}
34
56
  ${ebay.red('██╔════╝')}${ebay.blue('██╔══██╗')}${ebay.yellow('██╔══██╗')}${ebay.green('╚██╗ ██╔╝')}
@@ -37,13 +59,22 @@ const LOGO = `
37
59
  ${ebay.red('███████╗')}${ebay.blue('██████╔╝')}${ebay.yellow('██║ ██║')}${ebay.green(' ██║ ')}
38
60
  ${ebay.red('╚══════╝')}${ebay.blue('╚═════╝ ')}${ebay.yellow('╚═╝ ╚═╝')}${ebay.green(' ╚═╝ ')}
39
61
  `;
62
+ /**
63
+ * Clear the terminal screen.
64
+ */
40
65
  function clearScreen() {
41
66
  console.clear();
42
67
  }
68
+ /**
69
+ * Render the eBay ASCII logo and heading.
70
+ */
43
71
  function showLogo() {
44
72
  console.log(LOGO);
45
- console.log(ui.bold.white(' MCP Server Setup Wizard\n'));
73
+ console.log(ui.bold.white(' MCP Server Setup Wizard by Yosef Hayim Sabag\n'));
46
74
  }
75
+ /**
76
+ * Render a step progress bar with title.
77
+ */
47
78
  function showProgress(step, title) {
48
79
  const filled = '●'.repeat(step);
49
80
  const empty = '○'.repeat(TOTAL_STEPS - step);
@@ -52,21 +83,33 @@ function showProgress(step, title) {
52
83
  console.log(` ${progress} ${ui.bold(`Step ${step}/${TOTAL_STEPS}`)}: ${title}`);
53
84
  console.log(ui.dim('─'.repeat(60)) + '\n');
54
85
  }
86
+ /**
87
+ * Render keyboard hints for the current step.
88
+ */
55
89
  function showKeyboardHints(hints) {
56
90
  const hintText = hints.map((h) => ui.dim(h)).join(' │ ');
57
91
  console.log(`\n ${hintText}\n`);
58
92
  }
93
+ /**
94
+ * Render a tip callout.
95
+ */
59
96
  function showTip(message) {
60
97
  console.log(` ${ebay.yellow('💡 Tip:')} ${ui.dim(message)}\n`);
61
98
  }
99
+ /**
100
+ * Render a success line.
101
+ */
62
102
  function showSuccess(message) {
63
103
  console.log(` ${ui.success('✓')} ${message}`);
64
104
  }
105
+ /**
106
+ * Render an error line.
107
+ */
65
108
  function showError(message) {
66
109
  console.log(` ${ui.error('✗')} ${message}`);
67
110
  }
68
111
  /**
69
- * Open a URL in the default browser (cross-platform)
112
+ * Open a URL in the default browser (cross-platform).
70
113
  */
71
114
  function openBrowser(url) {
72
115
  return new Promise((resolve, reject) => {
@@ -92,6 +135,9 @@ function openBrowser(url) {
92
135
  });
93
136
  });
94
137
  }
138
+ /**
139
+ * Render a warning line.
140
+ */
95
141
  function showWarning(message) {
96
142
  console.log(` ${ui.warning('⚠')} ${message}`);
97
143
  }
@@ -258,9 +304,15 @@ function displayUserInfo(userInfo) {
258
304
  `User ID: ${userInfo.userId?.slice(0, 30)}...`,
259
305
  ]);
260
306
  }
307
+ /**
308
+ * Render an informational line.
309
+ */
261
310
  function showInfo(message) {
262
311
  console.log(` ${ui.info('ℹ')} ${message}`);
263
312
  }
313
+ /**
314
+ * Render a spinner and return a stop callback.
315
+ */
264
316
  function showSpinner(message) {
265
317
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
266
318
  let i = 0;
@@ -274,6 +326,9 @@ function showSpinner(message) {
274
326
  process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r');
275
327
  };
276
328
  }
329
+ /**
330
+ * Render a bordered info box.
331
+ */
277
332
  function showBox(title, content) {
278
333
  const width = 60;
279
334
  const line = '─'.repeat(width - 2);
@@ -286,6 +341,9 @@ function showBox(title, content) {
286
341
  }
287
342
  console.log(` ${ui.dim('└' + line + '┘')}\n`);
288
343
  }
344
+ /**
345
+ * Compute MCP client config paths by OS.
346
+ */
289
347
  function getConfigPaths() {
290
348
  const home = homedir();
291
349
  const os = platform();
@@ -326,6 +384,9 @@ function getConfigPaths() {
326
384
  };
327
385
  return paths;
328
386
  }
387
+ /**
388
+ * Detect installed MCP-compatible clients.
389
+ */
329
390
  function detectLLMClients() {
330
391
  const paths = getConfigPaths();
331
392
  const clients = [];
@@ -342,6 +403,9 @@ function detectLLMClients() {
342
403
  }
343
404
  return clients;
344
405
  }
406
+ /**
407
+ * Write MCP server config for a detected client.
408
+ */
345
409
  function configureLLMClient(client, projectRoot) {
346
410
  try {
347
411
  const configDir = dirname(client.configPath);
@@ -460,6 +524,12 @@ function updateClaudeDesktopConfig(envConfig, environment) {
460
524
  if (envConfig.EBAY_REDIRECT_URI) {
461
525
  envVars.EBAY_REDIRECT_URI = envConfig.EBAY_REDIRECT_URI;
462
526
  }
527
+ if (envConfig.EBAY_MARKETPLACE_ID) {
528
+ envVars.EBAY_MARKETPLACE_ID = envConfig.EBAY_MARKETPLACE_ID;
529
+ }
530
+ if (envConfig.EBAY_CONTENT_LANGUAGE) {
531
+ envVars.EBAY_CONTENT_LANGUAGE = envConfig.EBAY_CONTENT_LANGUAGE;
532
+ }
463
533
  if (envConfig.EBAY_USER_REFRESH_TOKEN) {
464
534
  envVars.EBAY_USER_REFRESH_TOKEN = envConfig.EBAY_USER_REFRESH_TOKEN;
465
535
  }
@@ -502,6 +572,9 @@ function updateClaudeDesktopConfig(envConfig, environment) {
502
572
  };
503
573
  }
504
574
  }
575
+ /**
576
+ * Load existing environment variables from the .env file.
577
+ */
505
578
  function loadExistingConfig() {
506
579
  const envPath = join(PROJECT_ROOT, '.env');
507
580
  const envConfig = {};
@@ -519,6 +592,9 @@ function loadExistingConfig() {
519
592
  }
520
593
  return envConfig;
521
594
  }
595
+ /**
596
+ * Format a date for display in the .env header.
597
+ */
522
598
  function formatDate(date) {
523
599
  const options = {
524
600
  weekday: 'long',
@@ -532,9 +608,18 @@ function formatDate(date) {
532
608
  };
533
609
  return date.toLocaleString('en-US', options);
534
610
  }
611
+ /**
612
+ * Persist setup configuration to the project .env file.
613
+ */
535
614
  function saveConfig(envConfig, environment) {
536
615
  const envPath = join(PROJECT_ROOT, '.env');
537
616
  const now = new Date();
617
+ const marketplaceLine = envConfig.EBAY_MARKETPLACE_ID
618
+ ? `EBAY_MARKETPLACE_ID=${envConfig.EBAY_MARKETPLACE_ID}`
619
+ : '# EBAY_MARKETPLACE_ID=EBAY_US';
620
+ const contentLanguageLine = envConfig.EBAY_CONTENT_LANGUAGE
621
+ ? `EBAY_CONTENT_LANGUAGE=${envConfig.EBAY_CONTENT_LANGUAGE}`
622
+ : '# EBAY_CONTENT_LANGUAGE=en-US';
538
623
  const content = `# eBay MCP Server Configuration
539
624
  # Last Updated: ${formatDate(now)}
540
625
  # Environment: ${environment}
@@ -543,6 +628,8 @@ EBAY_CLIENT_ID=${envConfig.EBAY_CLIENT_ID || ''}
543
628
  EBAY_CLIENT_SECRET=${envConfig.EBAY_CLIENT_SECRET || ''}
544
629
  EBAY_REDIRECT_URI=${envConfig.EBAY_REDIRECT_URI || ''}
545
630
  EBAY_ENVIRONMENT=${environment}
631
+ ${marketplaceLine}
632
+ ${contentLanguageLine}
546
633
 
547
634
  EBAY_USER_REFRESH_TOKEN=${envConfig.EBAY_USER_REFRESH_TOKEN || ''}
548
635
  EBAY_USER_ACCESS_TOKEN=${envConfig.EBAY_USER_ACCESS_TOKEN || ''}
@@ -555,10 +642,12 @@ async function stepWelcome(state) {
555
642
  showLogo();
556
643
  console.log(ui.dim(' Welcome to the eBay MCP Server setup wizard!\n'));
557
644
  console.log(' This wizard will help you:\n');
558
- console.log(` ${ui.success('1.')} Configure your eBay Developer credentials`);
559
- console.log(` ${ui.success('2.')} Set up OAuth authentication`);
560
- console.log(` ${ui.success('3.')} Configure your MCP client (Claude, Cline, etc.)`);
561
- console.log(` ${ui.success('4.')} Validate your setup\n`);
645
+ console.log(` ${ui.success('1.')} Choose environment (sandbox/production)`);
646
+ console.log(` ${ui.success('2.')} Set default marketplace and language (optional)`);
647
+ console.log(` ${ui.success('3.')} Configure your eBay Developer credentials`);
648
+ console.log(` ${ui.success('4.')} Set up OAuth authentication`);
649
+ console.log(` ${ui.success('5.')} Configure your MCP client (Claude, Cline, etc.)`);
650
+ console.log(` ${ui.success('6.')} Validate your setup\n`);
562
651
  if (state.hasExistingConfig) {
563
652
  showInfo('Existing configuration detected. You can update or keep current values.');
564
653
  }
@@ -571,6 +660,9 @@ async function stepWelcome(state) {
571
660
  });
572
661
  return response.continue !== false ? 'continue' : 'cancel';
573
662
  }
663
+ /**
664
+ * Select the eBay environment for this configuration.
665
+ */
574
666
  async function stepEnvironment(state) {
575
667
  clearScreen();
576
668
  showLogo();
@@ -607,10 +699,128 @@ async function stepEnvironment(state) {
607
699
  state.config.EBAY_ENVIRONMENT = response.environment;
608
700
  return 'continue';
609
701
  }
702
+ /**
703
+ * Configure optional marketplace and content-language defaults.
704
+ */
705
+ async function stepMarketplaceSettings(state) {
706
+ clearScreen();
707
+ showLogo();
708
+ showProgress(2, 'Marketplace Settings');
709
+ if (state.isQuickMode) {
710
+ showInfo('Quick setup enabled. Skipping optional marketplace configuration.');
711
+ await new Promise((r) => setTimeout(r, 600));
712
+ return 'continue';
713
+ }
714
+ console.log(' Configure default marketplace and language for API requests.\n');
715
+ showBox('Marketplace Settings', [
716
+ 'These are optional defaults used for request headers.',
717
+ 'Marketplace can be overridden in many tools; language is global.',
718
+ ]);
719
+ const marketplaceChoices = [
720
+ { title: ui.dim('← Go back'), value: '__back__' },
721
+ { title: 'Skip (leave unset)', value: '' },
722
+ ...MARKETPLACE_OPTIONS.map((option) => ({
723
+ title: option.label,
724
+ value: option.value,
725
+ })),
726
+ { title: 'Other (enter manually)', value: '__custom__' },
727
+ ];
728
+ const currentMarketplace = state.config.EBAY_MARKETPLACE_ID || '';
729
+ const marketplaceDefault = currentMarketplace || 'EBAY_US';
730
+ const marketplaceMatchIndex = marketplaceChoices.findIndex((choice) => choice.value === marketplaceDefault);
731
+ const marketplaceInitial = marketplaceMatchIndex >= 0
732
+ ? marketplaceMatchIndex
733
+ : marketplaceChoices.findIndex((choice) => choice.value === '__custom__');
734
+ const marketplaceResponse = await prompts({
735
+ type: 'select',
736
+ name: 'marketplaceId',
737
+ message: 'Select your default eBay marketplace:',
738
+ choices: marketplaceChoices,
739
+ initial: marketplaceInitial >= 0 ? marketplaceInitial : 0,
740
+ });
741
+ if (marketplaceResponse.marketplaceId === undefined) {
742
+ return 'cancel';
743
+ }
744
+ if (marketplaceResponse.marketplaceId === '__back__') {
745
+ return 'back';
746
+ }
747
+ let marketplaceId = marketplaceResponse.marketplaceId;
748
+ if (marketplaceId === '__custom__') {
749
+ const customMarketplace = await prompts({
750
+ type: 'text',
751
+ name: 'customMarketplaceId',
752
+ message: 'Enter marketplace ID (e.g., EBAY_US, EBAY_DE):',
753
+ initial: currentMarketplace || 'EBAY_US',
754
+ validate: (value) => value.trim().length === 0 ? 'Marketplace ID cannot be empty' : true,
755
+ });
756
+ if (customMarketplace.customMarketplaceId === undefined) {
757
+ return 'cancel';
758
+ }
759
+ marketplaceId = customMarketplace.customMarketplaceId.trim();
760
+ }
761
+ if (marketplaceId) {
762
+ state.config.EBAY_MARKETPLACE_ID = marketplaceId;
763
+ }
764
+ else {
765
+ delete state.config.EBAY_MARKETPLACE_ID;
766
+ }
767
+ const languageChoices = [
768
+ { title: ui.dim('← Go back'), value: '__back__' },
769
+ { title: 'Skip (leave unset)', value: '' },
770
+ ...CONTENT_LANGUAGE_OPTIONS.map((option) => ({
771
+ title: option.label,
772
+ value: option.value,
773
+ })),
774
+ { title: 'Other (enter manually)', value: '__custom__' },
775
+ ];
776
+ const currentLanguage = state.config.EBAY_CONTENT_LANGUAGE || '';
777
+ const languageDefault = currentLanguage || 'en-US';
778
+ const languageMatchIndex = languageChoices.findIndex((choice) => choice.value === languageDefault);
779
+ const languageInitial = languageMatchIndex >= 0
780
+ ? languageMatchIndex
781
+ : languageChoices.findIndex((choice) => choice.value === '__custom__');
782
+ const languageResponse = await prompts({
783
+ type: 'select',
784
+ name: 'contentLanguage',
785
+ message: 'Select your preferred Content-Language:',
786
+ choices: languageChoices,
787
+ initial: languageInitial >= 0 ? languageInitial : 0,
788
+ });
789
+ if (languageResponse.contentLanguage === undefined) {
790
+ return 'cancel';
791
+ }
792
+ if (languageResponse.contentLanguage === '__back__') {
793
+ return 'back';
794
+ }
795
+ let contentLanguage = languageResponse.contentLanguage;
796
+ if (contentLanguage === '__custom__') {
797
+ const customLanguage = await prompts({
798
+ type: 'text',
799
+ name: 'customContentLanguage',
800
+ message: 'Enter Content-Language (e.g., en-US, de-DE):',
801
+ initial: currentLanguage || 'en-US',
802
+ validate: (value) => value.trim().length === 0 ? 'Content-Language cannot be empty' : true,
803
+ });
804
+ if (customLanguage.customContentLanguage === undefined) {
805
+ return 'cancel';
806
+ }
807
+ contentLanguage = customLanguage.customContentLanguage.trim();
808
+ }
809
+ if (contentLanguage) {
810
+ state.config.EBAY_CONTENT_LANGUAGE = contentLanguage;
811
+ }
812
+ else {
813
+ delete state.config.EBAY_CONTENT_LANGUAGE;
814
+ }
815
+ return 'continue';
816
+ }
817
+ /**
818
+ * Collect eBay app credentials from the user.
819
+ */
610
820
  async function stepCredentials(state) {
611
821
  clearScreen();
612
822
  showLogo();
613
- showProgress(2, 'eBay Credentials');
823
+ showProgress(3, 'eBay Credentials');
614
824
  console.log(' Enter your eBay Developer credentials:\n');
615
825
  showTip('Get credentials at: https://developer.ebay.com/my/keys');
616
826
  showKeyboardHints(['Tab: Next field', 'Enter: Submit', 'Ctrl+C: Cancel']);
@@ -659,10 +869,13 @@ async function stepCredentials(state) {
659
869
  state.config.EBAY_REDIRECT_URI = responses.redirectUri;
660
870
  return 'continue';
661
871
  }
872
+ /**
873
+ * Acquire and validate OAuth tokens for the configured credentials.
874
+ */
662
875
  async function stepOAuth(state) {
663
876
  clearScreen();
664
877
  showLogo();
665
- showProgress(3, 'OAuth Setup');
878
+ showProgress(4, 'OAuth Setup');
666
879
  console.log(' Configure user authentication for higher API rate limits:\n');
667
880
  showBox('Rate Limits by Auth Type', [
668
881
  'App Credentials Only: 1,000 req/day',
@@ -853,13 +1066,7 @@ async function stepOAuth(state) {
853
1066
  }
854
1067
  }
855
1068
  else if (tokenChoice.method === 'manual') {
856
- const baseUrl = state.environment === 'production'
857
- ? 'https://auth.ebay.com/oauth2/authorize'
858
- : 'https://auth.sandbox.ebay.com/oauth2/authorize';
859
- // Get scopes from environment config
860
- const scopes = getDefaultScopes(state.environment);
861
- const scopeParam = encodeURIComponent(scopes.join(' '));
862
- const authUrl = `${baseUrl}?client_id=${encodeURIComponent(state.config.EBAY_CLIENT_ID)}&redirect_uri=${encodeURIComponent(state.config.EBAY_REDIRECT_URI)}&response_type=code&scope=${scopeParam}`;
1069
+ const authUrl = getOAuthAuthorizationUrl(state.config.EBAY_CLIENT_ID, state.config.EBAY_REDIRECT_URI, state.environment);
863
1070
  console.log('\n ' + ui.bold('OAuth Authorization URL:'));
864
1071
  console.log(ui.dim(' ' + '─'.repeat(56)));
865
1072
  console.log(` ${ui.info(authUrl)}`);
@@ -1080,10 +1287,13 @@ async function stepOAuth(state) {
1080
1287
  }
1081
1288
  return 'continue';
1082
1289
  }
1290
+ /**
1291
+ * Configure MCP clients with the generated environment variables.
1292
+ */
1083
1293
  async function stepMCPClients(state) {
1084
1294
  clearScreen();
1085
1295
  showLogo();
1086
- showProgress(4, 'MCP Client Setup');
1296
+ showProgress(5, 'MCP Client Setup');
1087
1297
  console.log(' Configure your AI assistant to use the eBay MCP server:\n');
1088
1298
  state.detectedClients = detectLLMClients();
1089
1299
  const detected = state.detectedClients.filter((c) => c.detected);
@@ -1170,10 +1380,13 @@ async function stepMCPClients(state) {
1170
1380
  }
1171
1381
  return 'continue';
1172
1382
  }
1383
+ /**
1384
+ * Finalize setup and display summary information.
1385
+ */
1173
1386
  async function stepComplete(state) {
1174
1387
  clearScreen();
1175
1388
  showLogo();
1176
- showProgress(5, 'Setup Complete');
1389
+ showProgress(6, 'Setup Complete');
1177
1390
  const stopSpinner = showSpinner('Saving configuration...');
1178
1391
  await new Promise((r) => setTimeout(r, 300));
1179
1392
  saveConfig(state.config, state.environment);
@@ -1182,6 +1395,8 @@ async function stepComplete(state) {
1182
1395
  console.log(ui.bold.green('\n 🎉 Setup Complete!\n'));
1183
1396
  showBox('Configuration Summary', [
1184
1397
  `Environment: ${state.environment}`,
1398
+ `Marketplace ID: ${state.config.EBAY_MARKETPLACE_ID || 'Not set'}`,
1399
+ `Content-Lang: ${state.config.EBAY_CONTENT_LANGUAGE || 'Not set'}`,
1185
1400
  `Client ID: ${state.config.EBAY_CLIENT_ID?.slice(0, 20)}...`,
1186
1401
  `Redirect URI: ${state.config.EBAY_REDIRECT_URI?.slice(0, 30)}...`,
1187
1402
  `OAuth Token: ${state.config.EBAY_USER_REFRESH_TOKEN ? '✓ Configured' : '✗ Not set'}`,
@@ -1254,7 +1469,14 @@ async function main() {
1254
1469
  hasExistingConfig: Object.keys(existingConfig).length > 0,
1255
1470
  isQuickMode: args.quick,
1256
1471
  };
1257
- const steps = [stepWelcome, stepEnvironment, stepCredentials, stepOAuth, stepMCPClients];
1472
+ const steps = [
1473
+ stepWelcome,
1474
+ stepEnvironment,
1475
+ stepMarketplaceSettings,
1476
+ stepCredentials,
1477
+ stepOAuth,
1478
+ stepMCPClients,
1479
+ ];
1258
1480
  let stepIndex = 0;
1259
1481
  while (stepIndex < steps.length) {
1260
1482
  const result = await steps[stepIndex](state);
@@ -1275,7 +1497,14 @@ process.on('SIGINT', () => {
1275
1497
  console.log(ui.warning('\n\n Setup interrupted.\n'));
1276
1498
  process.exit(0);
1277
1499
  });
1278
- main().catch((error) => {
1279
- console.error(ui.error('\n Setup failed:'), error);
1280
- process.exit(1);
1281
- });
1500
+ export async function runSetup() {
1501
+ await main();
1502
+ }
1503
+ const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
1504
+ const modulePath = resolve(fileURLToPath(import.meta.url));
1505
+ if (entryPath && modulePath === entryPath) {
1506
+ runSetup().catch((error) => {
1507
+ console.error(ui.error('\n Setup failed:'), error);
1508
+ process.exit(1);
1509
+ });
1510
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Fetches the eBay API Status RSS feed and writes the latest items to docs/API_STATUS.md.
3
+ * Used by the GitHub Action to keep an in-repo snapshot. Run with: npx tsx src/scripts/update-api-status-doc.ts
4
+ */
5
+ import { writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { getApiStatusFeed } from '../utils/api-status-feed.js';
8
+ const DEFAULT_LIMIT = 15;
9
+ const OUT_PATH = join(process.cwd(), 'docs', 'API_STATUS.md');
10
+ function escapeCell(s) {
11
+ return s.replace(/\|/g, '\\|').replace(/\n/g, ' ');
12
+ }
13
+ function buildMarkdown(items) {
14
+ const lines = [
15
+ '# eBay API Status (latest)',
16
+ '',
17
+ 'Auto-updated snapshot from the [eBay API Status RSS feed](https://developer.ebay.com/rss/api-status).',
18
+ 'Full list: [developer.ebay.com/support/api-status](https://developer.ebay.com/support/api-status).',
19
+ '',
20
+ `*Last updated: ${new Date().toISOString()}*`,
21
+ '',
22
+ '| Title | API | Site | Status | Last updated | Link |',
23
+ '|-------|-----|------|--------|--------------|------|',
24
+ ];
25
+ for (const item of items) {
26
+ const link = item.link ? `[Details](${item.link})` : '';
27
+ lines.push(`| ${escapeCell(item.title)} | ${escapeCell(item.api)} | ${escapeCell(item.site)} | ${escapeCell(item.status)} | ${escapeCell(item.lastUpdated)} | ${link} |`);
28
+ }
29
+ lines.push('', '---', '', '*Generated by [ebay-mcp](https://github.com/YosefHayim/ebay-mcp) API status sync.*');
30
+ return lines.join('\n');
31
+ }
32
+ async function main() {
33
+ const { items, error } = await getApiStatusFeed({ limit: DEFAULT_LIMIT });
34
+ if (error && items.length === 0) {
35
+ throw new Error(`Failed to fetch API status feed: ${error}`);
36
+ }
37
+ const markdown = buildMarkdown(items);
38
+ writeFileSync(OUT_PATH, markdown, 'utf8');
39
+ console.log(`Wrote ${items.length} items to ${OUT_PATH}`);
40
+ }
41
+ main().catch((err) => {
42
+ console.error(err);
43
+ process.exitCode = 1;
44
+ });
@@ -12,6 +12,8 @@ import express from 'express';
12
12
  import helmet from 'helmet';
13
13
  import cors from 'cors';
14
14
  import { randomUUID } from 'crypto';
15
+ import { dirname, join, resolve } from 'path';
16
+ import { fileURLToPath } from 'url';
15
17
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
18
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
17
19
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
@@ -21,6 +23,7 @@ import { getToolDefinitions, executeTool } from './tools/index.js';
21
23
  import { TokenVerifier } from './auth/token-verifier.js';
22
24
  import { createBearerAuthMiddleware } from './auth/oauth-middleware.js';
23
25
  import { createMetadataRouter, getProtectedResourceMetadataUrl } from './auth/oauth-metadata.js';
26
+ import { getVersion } from './utils/version.js';
24
27
  // Configuration from environment
25
28
  const CONFIG = {
26
29
  // Server settings
@@ -62,6 +65,9 @@ function getAuthServerMetadataUrl() {
62
65
  */
63
66
  async function createApp() {
64
67
  const app = express();
68
+ const __filename = fileURLToPath(import.meta.url);
69
+ const __dirname = dirname(__filename);
70
+ const projectRoot = join(__dirname, '..');
65
71
  // Enable CORS
66
72
  app.use(cors({
67
73
  // TODO: Restrict origin to known clients in production
@@ -84,6 +90,9 @@ async function createApp() {
84
90
  });
85
91
  // Server URL
86
92
  const serverUrl = `http://${CONFIG.host}:${CONFIG.port}`;
93
+ const iconBaseUrl = `${serverUrl}/icons`;
94
+ // Static assets (icons)
95
+ app.use('/icons', express.static(join(projectRoot, 'public', 'icons')));
87
96
  // Get eBay configuration for metadata
88
97
  const ebayConfig = getEbayConfig();
89
98
  // Add OAuth metadata endpoints
@@ -140,47 +149,55 @@ async function createApp() {
140
149
  /**
141
150
  * Create a new MCP server instance
142
151
  */
143
- function createMcpServer() {
152
+ async function createMcpServer() {
144
153
  const ebayConfig = getEbayConfig();
145
154
  const api = new EbaySellerApi(ebayConfig);
155
+ try {
156
+ await api.initialize();
157
+ }
158
+ catch (error) {
159
+ const message = error instanceof Error ? error.message : String(error);
160
+ console.error(`Failed to initialize eBay API client: ${message}`);
161
+ throw error;
162
+ }
146
163
  const server = new McpServer({
147
164
  name: 'ebay-mcp',
148
- version: '1.4.0',
165
+ version: getVersion(),
149
166
  title: 'eBay API MCP Server',
150
- websiteUrl: 'https://coming-soon.com',
167
+ websiteUrl: 'https://github.com/YosefHayim/ebay-mcp',
151
168
  icons: [
152
169
  {
153
- src: './icons/16x16.png',
170
+ src: `${iconBaseUrl}/16x16.png`,
154
171
  mimeType: 'image/png',
155
172
  sizes: ['16x16'],
156
173
  },
157
174
  {
158
- src: './icons/32x32.png',
175
+ src: `${iconBaseUrl}/32x32.png`,
159
176
  mimeType: 'image/png',
160
177
  sizes: ['32x32'],
161
178
  },
162
179
  {
163
- src: './icons/48x48.png',
180
+ src: `${iconBaseUrl}/48x48.png`,
164
181
  mimeType: 'image/png',
165
182
  sizes: ['48x48'],
166
183
  },
167
184
  {
168
- src: './icons/128x128.png',
185
+ src: `${iconBaseUrl}/128x128.png`,
169
186
  mimeType: 'image/png',
170
187
  sizes: ['128x128'],
171
188
  },
172
189
  {
173
- src: './icons/256x256.png',
190
+ src: `${iconBaseUrl}/256x256.png`,
174
191
  mimeType: 'image/png',
175
192
  sizes: ['256x256'],
176
193
  },
177
194
  {
178
- src: './icons/512x512.png',
195
+ src: `${iconBaseUrl}/512x512.png`,
179
196
  mimeType: 'image/png',
180
197
  sizes: ['512x512'],
181
198
  },
182
199
  {
183
- src: './icons/1024x1024.png',
200
+ src: `${iconBaseUrl}/1024x1024.png`,
184
201
  mimeType: 'image/png',
185
202
  sizes: ['1024x1024'],
186
203
  },
@@ -246,7 +263,7 @@ async function createApp() {
246
263
  console.log(`MCP session closed: ${transport.sessionId}`);
247
264
  }
248
265
  };
249
- const server = createMcpServer();
266
+ const server = await createMcpServer();
250
267
  await server.connect(transport);
251
268
  }
252
269
  else {
@@ -338,7 +355,7 @@ async function main() {
338
355
  }
339
356
  else {
340
357
  console.log('Authorization is DISABLED');
341
- console.log('Set OAUTH_ENABLED=true to enable OAuth protection');
358
+ console.log('Set OAUTH_ENABLED=true (or remove OAUTH_ENABLED=false) to enable OAuth protection');
342
359
  }
343
360
  });
344
361
  // Graceful shutdown
@@ -356,6 +373,8 @@ async function main() {
356
373
  }
357
374
  }
358
375
  // Start server if run directly
359
- if (import.meta.url === `file://${process.argv[1]}`) {
376
+ const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
377
+ const modulePath = resolve(fileURLToPath(import.meta.url));
378
+ if (entryPath && modulePath === entryPath) {
360
379
  await main();
361
380
  }