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.
Files changed (169) hide show
  1. package/README.de.md +3 -3
  2. package/README.es.md +3 -3
  3. package/README.fr.md +3 -3
  4. package/README.ja.md +3 -3
  5. package/README.ko.md +3 -3
  6. package/README.md +163 -41
  7. package/README.pt-BR.md +3 -3
  8. package/README.ru.md +3 -3
  9. package/README.zh-CN.md +3 -3
  10. package/build/api/account-management/account.js +633 -297
  11. package/build/api/analytics-and-report/analytics.js +111 -65
  12. package/build/api/client.js +199 -140
  13. package/build/api/clientRequestError.js +23 -0
  14. package/build/api/clientTrading.js +213 -0
  15. package/build/api/clientTradingError.js +4 -0
  16. package/build/api/communication/feedback.js +147 -59
  17. package/build/api/communication/message.js +134 -81
  18. package/build/api/communication/negotiation.js +65 -53
  19. package/build/api/communication/notification.js +458 -164
  20. package/build/api/developer/developer.js +99 -45
  21. package/build/api/index.js +23 -8
  22. package/build/api/listing-management/inventory.js +884 -338
  23. package/build/api/listing-metadata/metadata.js +416 -170
  24. package/build/api/listing-metadata/taxonomy.js +187 -42
  25. package/build/api/marketing-and-promotions/marketing.js +1392 -598
  26. package/build/api/marketing-and-promotions/recommendation.js +49 -24
  27. package/build/api/order-management/dispute.js +226 -45
  28. package/build/api/order-management/fulfillment.js +144 -71
  29. package/build/api/order-management/orderIssueHelpers.js +61 -0
  30. package/build/api/other/compliance.js +50 -38
  31. package/build/api/other/edelivery.js +603 -159
  32. package/build/api/other/finding.js +280 -0
  33. package/build/api/other/identity.js +30 -15
  34. package/build/api/other/translation.js +29 -14
  35. package/build/api/other/vero.js +113 -27
  36. package/build/api/rateLimitTracker.js +55 -0
  37. package/build/api/shared/request.js +189 -98
  38. package/build/api/trading/trading.js +154 -87
  39. package/build/auth/credentialSession.js +250 -0
  40. package/build/auth/oauth.js +298 -171
  41. package/build/auth/{oauth-metadata.js → oauthMetadata.js} +22 -6
  42. package/build/auth/oauthMiddleware.js +104 -0
  43. package/build/auth/{scope-utils.js → scopeUtils.js} +101 -28
  44. package/build/auth/tokenVerifier.js +243 -0
  45. package/build/auth/tokenVerifierError.js +21 -0
  46. package/build/config/environment.js +148 -113
  47. package/build/config/{tool-families.js → toolFamilies.js} +30 -14
  48. package/build/index.js +25 -19
  49. package/build/mcp/{http-transport.js → httpTransport.js} +102 -52
  50. package/build/mcp/readOnlyFilter.js +67 -0
  51. package/build/mcp/runtime.js +31 -13
  52. package/build/mcp/{tool-gating.js → toolGating.js} +77 -49
  53. package/build/mcp/{ui-bridge.js → uiBridge.js} +80 -23
  54. package/build/schemas/account-management/account.js +144 -6
  55. package/build/schemas/analytics/analytics.js +33 -19
  56. package/build/schemas/communication/messages.js +12 -42
  57. package/build/schemas/developer/developer.js +135 -0
  58. package/build/schemas/fulfillment/orders.js +183 -21
  59. package/build/schemas/index.js +27 -19
  60. package/build/schemas/inventory-management/inventory.js +17 -9
  61. package/build/schemas/marketing/marketing.js +480 -25
  62. package/build/schemas/metadata/metadata.js +11 -6
  63. package/build/schemas/other/finding.js +27 -0
  64. package/build/schemas/other/otherApis.js +424 -0
  65. package/build/schemas/taxonomy/taxonomy.js +11 -5
  66. package/build/scripts/{auto-setup.js → autoSetup.js} +87 -74
  67. package/build/scripts/{build-ui.js → buildUi.js} +6 -1
  68. package/build/scripts/{dev-sync.js → devSync.js} +169 -110
  69. package/build/scripts/diagnostics.js +52 -44
  70. package/build/scripts/{download-specs.js → downloadSpecs.js} +29 -21
  71. package/build/scripts/{interactive-setup.js → interactiveSetup.js} +84 -78
  72. package/build/scripts/setup.js +241 -178
  73. package/build/scripts/{setup-shared.js → setupShared.js} +29 -7
  74. package/build/scripts/skills.js +35 -27
  75. package/build/scripts/updateApiStatusDoc.js +71 -0
  76. package/build/serverHttp.js +90 -0
  77. package/build/skills/content.js +124 -118
  78. package/build/skills/index.js +22 -10
  79. package/build/skills/install.js +21 -10
  80. package/build/skills/markers.js +50 -15
  81. package/build/skills/{registry-snapshot.js → registrySnapshot.js} +8 -2
  82. package/build/skills/render.js +41 -14
  83. package/build/skills/targets.js +49 -17
  84. package/build/tools/categories/account.js +166 -264
  85. package/build/tools/categories/analytics.js +11 -22
  86. package/build/tools/categories/browse.js +17 -0
  87. package/build/tools/categories/communication.js +124 -399
  88. package/build/tools/categories/connector.js +23 -20
  89. package/build/tools/categories/developer.js +46 -159
  90. package/build/tools/categories/fulfillment.js +84 -215
  91. package/build/tools/categories/index.js +6 -14
  92. package/build/tools/categories/inventory.js +238 -293
  93. package/build/tools/categories/marketing.js +677 -39
  94. package/build/tools/categories/metadata.js +190 -117
  95. package/build/tools/categories/other.js +81 -208
  96. package/build/tools/categories/taxonomy.js +29 -20
  97. package/build/tools/categories/{token-management.js → tokenManagement.js} +187 -192
  98. package/build/tools/categories/trading.js +16 -47
  99. package/build/tools/contracts.js +34 -24
  100. package/build/tools/{define-tool.js → defineTool.js} +44 -29
  101. package/build/tools/registry.js +87 -29
  102. package/build/tools/schemas.js +3 -57
  103. package/build/tools/ui/archetypes.js +1 -0
  104. package/build/tools/ui/{map-helpers.js → mapHelpers.js} +69 -19
  105. package/build/tools/ui/maps.js +294 -80
  106. package/build/types/index.js +1 -1
  107. package/build/ui/card.html +14 -14
  108. package/build/ui/chart.html +14 -14
  109. package/build/ui/stat.html +87 -0
  110. package/build/ui/table.html +14 -14
  111. package/build/utils/apiStatusFeed.js +95 -0
  112. package/build/utils/cliOutput.js +15 -0
  113. package/build/utils/cliUi.js +108 -0
  114. package/build/utils/communication/feedback.js +57 -77
  115. package/build/utils/communication/message.js +69 -98
  116. package/build/utils/communication/negotiation.js +30 -57
  117. package/build/utils/communication/notification.js +112 -252
  118. package/build/utils/{date-converter.js → dateConverter.js} +62 -30
  119. package/build/utils/effectSchema.js +203 -0
  120. package/build/utils/effectSchemaAdapter.js +121 -0
  121. package/build/utils/effectSchemaChainMethods.js +146 -0
  122. package/build/utils/effectSchemaRefinements.js +104 -0
  123. package/build/utils/effectSchemaStringMethods.js +63 -0
  124. package/build/utils/effectSchemaTypes.js +47 -0
  125. package/build/utils/{env-parser.js → envParser.js} +10 -2
  126. package/build/utils/errors.js +7 -3
  127. package/build/utils/http.js +148 -89
  128. package/build/utils/{llm-client-detector.js → llmClientDetector.js} +295 -274
  129. package/build/utils/logger.js +127 -51
  130. package/build/utils/{oauth-helper.js → oauthHelper.js} +127 -90
  131. package/build/utils/other/compliance.js +18 -38
  132. package/build/utils/other/identity.js +2 -2
  133. package/build/utils/other/translation.js +4 -4
  134. package/build/utils/other/vero.js +36 -20
  135. package/build/utils/schemaHelpers.js +22 -0
  136. package/build/utils/scopeHelper.js +270 -0
  137. package/build/utils/securityChecker.js +316 -0
  138. package/build/utils/{setup-validator.js → setupValidator.js} +113 -94
  139. package/build/utils/{setup-wizard.js → setupWizard.js} +57 -46
  140. package/build/utils/{token-utils.js → tokenUtils.js} +2 -2
  141. package/build/utils/trading/trading.js +35 -0
  142. package/build/utils/typeGuards.js +12 -0
  143. package/build/utils/version.js +65 -29
  144. package/package.json +10 -8
  145. package/build/api/client-trading.js +0 -124
  146. package/build/api/communication/shared.js +0 -59
  147. package/build/api/shared/query-params.js +0 -16
  148. package/build/auth/credential-session.js +0 -166
  149. package/build/auth/oauth-middleware.js +0 -100
  150. package/build/auth/token-verifier.js +0 -186
  151. package/build/schemas/other/other-apis.js +0 -390
  152. package/build/scripts/update-api-status-doc.js +0 -44
  153. package/build/server-http.js +0 -85
  154. package/build/tools/definitions/marketing.js +0 -1381
  155. package/build/tools/tool-handlers/marketing.js +0 -312
  156. package/build/utils/account-management/account.js +0 -818
  157. package/build/utils/api-status-feed.js +0 -84
  158. package/build/utils/cli-ui.js +0 -47
  159. package/build/utils/order-management/dispute.js +0 -369
  160. package/build/utils/order-management/fulfillment.js +0 -205
  161. package/build/utils/other/edelivery.js +0 -235
  162. package/build/utils/schema-helpers.js +0 -13
  163. package/build/utils/scope-helper.js +0 -207
  164. package/build/utils/security-checker.js +0 -248
  165. package/build/utils/type-guards.js +0 -6
  166. /package/build/auth/{oauth-types.js → oauthTypes.js} +0 -0
  167. /package/build/{server-http.d.ts → serverHttp.d.ts} +0 -0
  168. /package/build/tools/ui/{view-models.js → viewModels.js} +0 -0
  169. /package/build/types/{ebay-enums.js → ebayEnums.js} +0 -0
@@ -9,8 +9,16 @@ import dotenv from 'dotenv';
9
9
  * `v^1.1#i^1#...`, and a naive parser either keeps stray quotes or trips over
10
10
  * the `#`. Placeholder values (e.g. `your-client-id_here`) are skipped so the
11
11
  * wizard treats them as unset.
12
+ *
13
+ * @param projectRoot - Project root containing the `.env` file.
14
+ * @returns Parsed config values with placeholders removed.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const config = loadExistingConfig(process.cwd());
19
+ * ```
12
20
  */
13
- export function loadExistingConfig(projectRoot) {
21
+ export const loadExistingConfig = (projectRoot) => {
14
22
  const envPath = join(projectRoot, '.env');
15
23
  if (!existsSync(envPath)) {
16
24
  return {};
@@ -23,7 +31,7 @@ export function loadExistingConfig(projectRoot) {
23
31
  }
24
32
  }
25
33
  return envConfig;
26
- }
34
+ };
27
35
  /**
28
36
  * Quote a value for safe inclusion in a `.env` line.
29
37
  *
@@ -34,16 +42,30 @@ export function loadExistingConfig(projectRoot) {
34
42
  * embedded backslashes and double quotes) so dotenv restores it verbatim. This
35
43
  * mirrors the quoting `dotenv-stringify` already applies in the runtime
36
44
  * credential store, keeping both `.env` writers consistent.
45
+ *
46
+ * @param value - Raw environment value to serialize.
47
+ * @returns A dotenv-safe value string.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const line = `EBAY_USER_REFRESH_TOKEN=${quoteEnvValue(token)}`;
52
+ * ```
37
53
  */
38
- export function quoteEnvValue(value) {
54
+ export const quoteEnvValue = (value) => {
39
55
  if (value === '' || !/[#\s"'`]/.test(value)) {
40
56
  return value;
41
57
  }
42
58
  return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
43
- }
59
+ };
44
60
  /**
45
61
  * Parse environment with safe sandbox default.
62
+ *
63
+ * @param value - Optional environment value from config or user input.
64
+ * @returns `production` only for an exact production value; otherwise `sandbox`.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const environment = readEnvironment(config.EBAY_ENVIRONMENT);
69
+ * ```
46
70
  */
47
- export function readEnvironment(value) {
48
- return value === 'production' ? 'production' : 'sandbox';
49
- }
71
+ export const readEnvironment = (value) => value === 'production' ? 'production' : 'sandbox';
@@ -3,27 +3,27 @@ import { resolve } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import prompts from 'prompts';
5
5
  import { ALL_PROVIDERS, applyWrite, buildRegistrySnapshot, detectProvider, planWrite, providerLabel, renderSkillTargets, } from '../skills/index.js';
6
- import { ebayPalette, printHeading, printInfo, printRule, printSuccess, printWarning, ui, } from '../utils/cli-ui.js';
6
+ import { ebayPalette, printHeading, printInfo, printRule, printSuccess, printWarning, ui, } from '../utils/cliUi.js';
7
+ import { getErrorMessage } from '../utils/errors.js';
8
+ import { Effect, Either } from 'effect';
9
+ import process from 'node:process';
7
10
  const ALL_LAYERS = ['using', 'contributing'];
8
11
  /** Splits a comma list flag value into trimmed, non-empty tokens. */
9
- function parseList(value) {
10
- return (value ?? '')
11
- .split(',')
12
- .map((token) => token.trim())
13
- .filter(Boolean);
14
- }
12
+ const parseList = (value) => (value ?? '')
13
+ .split(',')
14
+ .map((token) => token.trim())
15
+ .filter(Boolean);
15
16
  /** Reads the value after a `--flag value` or `--flag=value` argument. */
16
- function readFlagValue(argv, flag) {
17
+ const readFlagValue = (argv, flag) => {
17
18
  const inline = argv.find((arg) => arg.startsWith(`${flag}=`));
18
19
  if (inline)
19
20
  return inline.slice(flag.length + 1);
20
21
  const index = argv.indexOf(flag);
21
22
  if (index !== -1 && index + 1 < argv.length)
22
23
  return argv[index + 1];
23
- return undefined;
24
- }
24
+ };
25
25
  /** Parses skills CLI flags, ignoring the leading `skills` subcommand token. */
26
- function parseFlags(argv) {
26
+ const parseFlags = (argv) => {
27
27
  const args = argv.filter((arg) => arg !== 'skills');
28
28
  const providers = parseList(readFlagValue(args, '--providers')).filter((value) => ALL_PROVIDERS.includes(value));
29
29
  const layers = parseList(readFlagValue(args, '--layer') ?? readFlagValue(args, '--layers')).filter((value) => ALL_LAYERS.includes(value));
@@ -36,9 +36,9 @@ function parseFlags(argv) {
36
36
  providers: providers.length > 0 ? providers : undefined,
37
37
  layers: layers.length > 0 ? layers : undefined,
38
38
  };
39
- }
39
+ };
40
40
  /** One-line summary of what an action means, for the preview. */
41
- function actionLabel(action) {
41
+ const actionLabel = (action) => {
42
42
  switch (action) {
43
43
  case 'create':
44
44
  return ui.success('create');
@@ -49,9 +49,9 @@ function actionLabel(action) {
49
49
  case 'skip-foreign':
50
50
  return ui.warning('skip (foreign file)');
51
51
  }
52
- }
52
+ };
53
53
  /** Prints the planned writes so the user can review before anything is written. */
54
- function printPreview(plans) {
54
+ const printPreview = (plans) => {
55
55
  printHeading('Planned changes');
56
56
  printRule();
57
57
  for (const plan of plans) {
@@ -60,9 +60,9 @@ function printPreview(plans) {
60
60
  console.log(` ${ui.dim('→ ' + plan.target.path)}`);
61
61
  }
62
62
  printRule();
63
- }
63
+ };
64
64
  /** Prints CLI usage. */
65
- function printHelp() {
65
+ const printHelp = () => {
66
66
  console.log(`
67
67
  ${ui.bold('ebay-mcp skills')} ${ui.dim('install AI skills for Codex, Claude Code, and Cursor')}
68
68
 
@@ -84,11 +84,9 @@ ${ui.bold('Examples:')}
84
84
  ebay-mcp skills --providers cursor,codex --layer using --dry-run
85
85
  ebay-mcp skills --yes
86
86
  `);
87
- }
87
+ };
88
88
  /** Resolves the providers to use, preferring flags, else detection at the scope. */
89
- function resolveDefaultProviders(scope, cwd, home) {
90
- return ALL_PROVIDERS.filter((provider) => detectProvider(provider, scope, cwd, home));
91
- }
89
+ const resolveDefaultProviders = (scope, cwd, home) => ALL_PROVIDERS.filter((provider) => detectProvider(provider, scope, cwd, home));
92
90
  /**
93
91
  * Runs the skills installer end to end: detect → choose providers/layers/scope →
94
92
  * preview → confirm → write. Interactive by default; non-interactive when `--yes`
@@ -97,9 +95,14 @@ function resolveDefaultProviders(scope, cwd, home) {
97
95
  * Shared by the `ebay-mcp skills` subcommand, `npm run skills`, and the optional
98
96
  * step at the end of `npm run setup`.
99
97
  *
100
- * @param options `argv` overrides process args; `fromSetup` trims the banner.
98
+ * @param options Runtime overrides for arguments and setup embedding.
99
+ * @returns A promise that resolves after the installer exits or writes files.
100
+ * @example
101
+ * ```ts
102
+ * await runSkillsWizard({ argv: ['--providers', 'codex', '--yes'] });
103
+ * ```
101
104
  */
102
- export async function runSkillsWizard(options = {}) {
105
+ export const runSkillsWizard = async (options = {}) => {
103
106
  const flags = parseFlags(options.argv ?? process.argv.slice(2));
104
107
  if (flags.help) {
105
108
  printHelp();
@@ -227,12 +230,17 @@ export async function runSkillsWizard(options = {}) {
227
230
  }
228
231
  printHeading('Done');
229
232
  printInfo(`${written} file(s) written. Restart your AI tool so it picks up the new skills.`);
230
- }
233
+ };
231
234
  const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
232
235
  const modulePath = resolve(fileURLToPath(import.meta.url));
233
236
  if (entryPath && modulePath === entryPath) {
234
- runSkillsWizard().catch((error) => {
235
- console.error(ui.error('\n Skills install failed:'), error);
236
- process.exit(1);
237
+ void Effect.runPromise(Effect.either(Effect.tryPromise({
238
+ try: () => runSkillsWizard(),
239
+ catch: (error) => error,
240
+ }))).then((result) => {
241
+ if (Either.isLeft(result)) {
242
+ console.error(ui.error('\n Skills install failed:'), getErrorMessage(result.left));
243
+ process.exitCode = 1;
244
+ }
237
245
  });
238
246
  }
@@ -0,0 +1,71 @@
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/updateApiStatusDoc.ts
4
+ */
5
+ import { writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { getApiStatusFeed } from '../utils/apiStatusFeed.js';
8
+ import { getErrorMessage } from '../utils/errors.js';
9
+ import { Data, Effect, Either } from 'effect';
10
+ import process from 'node:process';
11
+ const DEFAULT_LIMIT = 15;
12
+ const OUT_PATH = join(process.cwd(), 'docs', 'API_STATUS.md');
13
+ /** Tagged failure raised while updating the checked-in API status snapshot. */
14
+ class ApiStatusDocUpdateError extends Data.TaggedError('ApiStatusDocUpdateError') {
15
+ }
16
+ function escapeCell(s) {
17
+ return s.replace(/\|/g, '\\|').replace(/\n/g, ' ');
18
+ }
19
+ function buildMarkdown(items) {
20
+ const lines = [
21
+ '# eBay API Status (latest)',
22
+ '',
23
+ 'Auto-updated snapshot from the [eBay API Status RSS feed](https://developer.ebay.com/rss/api-status).',
24
+ 'Full list: [developer.ebay.com/support/api-status](https://developer.ebay.com/support/api-status).',
25
+ '',
26
+ `*Last updated: ${new Date().toISOString()}*`,
27
+ '',
28
+ '| Title | API | Site | Status | Last updated | Link |',
29
+ '|-------|-----|------|--------|--------------|------|',
30
+ ];
31
+ for (const item of items) {
32
+ const link = item.link ? `[Details](${item.link})` : '';
33
+ lines.push(`| ${escapeCell(item.title)} | ${escapeCell(item.api)} | ${escapeCell(item.site)} | ${escapeCell(item.status)} | ${escapeCell(item.lastUpdated)} | ${link} |`);
34
+ }
35
+ lines.push('', '---', '', '*Generated by [ebay-mcp](https://github.com/YosefHayim/ebay-mcp) API status sync.*');
36
+ return lines.join('\n');
37
+ }
38
+ const updateApiStatusDoc = () => Effect.gen(function* () {
39
+ const { items, error } = yield* getApiStatusFeed({ limit: DEFAULT_LIMIT }).pipe(Effect.mapError((cause) => new ApiStatusDocUpdateError({
40
+ message: 'Failed to fetch API status feed',
41
+ cause,
42
+ })));
43
+ if (error && items.length === 0) {
44
+ return yield* Effect.fail(new ApiStatusDocUpdateError({
45
+ message: `Failed to fetch API status feed: ${error}`,
46
+ cause: error,
47
+ }));
48
+ }
49
+ const markdown = buildMarkdown(items);
50
+ yield* Effect.try({
51
+ try: () => writeFileSync(OUT_PATH, markdown, 'utf8'),
52
+ catch: (cause) => new ApiStatusDocUpdateError({
53
+ message: `Failed to write API status doc to ${OUT_PATH}`,
54
+ cause,
55
+ }),
56
+ });
57
+ return items.length;
58
+ });
59
+ async function main() {
60
+ const count = await Effect.runPromise(updateApiStatusDoc());
61
+ console.log(`Wrote ${count} items to ${OUT_PATH}`);
62
+ }
63
+ void Effect.runPromise(Effect.either(Effect.tryPromise({
64
+ try: main,
65
+ catch: (error) => error,
66
+ }))).then((result) => {
67
+ if (Either.isLeft(result)) {
68
+ console.error(getErrorMessage(result.left, String(result.left)));
69
+ process.exitCode = 1;
70
+ }
71
+ });
@@ -0,0 +1,90 @@
1
+ /**
2
+ * eBay API MCP Server with HTTP Transport and OAuth 2.1 Authorization.
3
+ */
4
+ import { resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { validateEnvironmentConfig } from './config/environment.js';
7
+ import { createHttpMcpApp, createHttpTransportConfigFromEnv, getHttpServerUrl, } from './mcp/httpTransport.js';
8
+ import { getErrorMessage } from './utils/errors.js';
9
+ import { serverLogger } from './utils/logger.js';
10
+ import { Effect, Either } from 'effect';
11
+ import process from 'node:process';
12
+ const CONFIG = createHttpTransportConfigFromEnv(process.env);
13
+ function logEnvironmentValidation() {
14
+ const validation = validateEnvironmentConfig();
15
+ validation.infos.forEach((info) => {
16
+ serverLogger.info(info);
17
+ });
18
+ if (validation.warnings.length > 0) {
19
+ serverLogger.warn('Environment Configuration Warnings:');
20
+ validation.warnings.forEach((warning) => {
21
+ serverLogger.warn(warning);
22
+ });
23
+ }
24
+ if (!validation.isValid) {
25
+ serverLogger.error('Environment Configuration Errors:');
26
+ validation.errors.forEach((error) => {
27
+ serverLogger.error(error);
28
+ });
29
+ serverLogger.error('Please fix the configuration errors and restart the server.');
30
+ process.exit(1);
31
+ }
32
+ }
33
+ function logConfiguration() {
34
+ serverLogger.info('Configuration:');
35
+ serverLogger.info(`Host: ${CONFIG.host}`);
36
+ serverLogger.info(`Port: ${CONFIG.port}`);
37
+ serverLogger.info(`OAuth Enabled: ${CONFIG.authEnabled}`);
38
+ if (CONFIG.authEnabled) {
39
+ serverLogger.info(`Auth Server: ${CONFIG.oauth.authServerUrl}`);
40
+ serverLogger.info(`Required Scopes: ${CONFIG.oauth.requiredScopes.join(', ')}`);
41
+ serverLogger.info(`Verification Method: ${CONFIG.oauth.useIntrospection ? 'Introspection' : 'JWT'}`);
42
+ }
43
+ }
44
+ function logStartupUrls(serverUrl) {
45
+ serverLogger.info('Server is running!');
46
+ serverLogger.info(`MCP endpoint: ${serverUrl}/`);
47
+ serverLogger.info(`Protected Resource Metadata: ${serverUrl}/.well-known/oauth-protected-resource`);
48
+ serverLogger.info(`Health check: ${serverUrl}/health`);
49
+ if (CONFIG.authEnabled) {
50
+ serverLogger.info('Authorization is ENABLED');
51
+ serverLogger.info('Clients must provide valid Bearer tokens to access MCP endpoints');
52
+ }
53
+ else {
54
+ serverLogger.warn('Authorization is DISABLED');
55
+ serverLogger.warn('Set OAUTH_ENABLED=true (or remove OAUTH_ENABLED=false) to enable OAuth protection');
56
+ }
57
+ }
58
+ async function main() {
59
+ const started = await Effect.runPromise(Effect.either(Effect.gen(function* () {
60
+ serverLogger.info('Starting eBay API MCP Server (HTTP + OAuth)...');
61
+ logEnvironmentValidation();
62
+ logConfiguration();
63
+ const app = yield* Effect.tryPromise({
64
+ try: () => createHttpMcpApp(CONFIG),
65
+ catch: (error) => error,
66
+ });
67
+ const serverUrl = getHttpServerUrl(CONFIG);
68
+ const server = app.listen(CONFIG.port, CONFIG.host, () => {
69
+ logStartupUrls(serverUrl);
70
+ });
71
+ process.on('SIGINT', () => {
72
+ serverLogger.info('Shutting down...');
73
+ server.close(() => {
74
+ serverLogger.info('Server closed');
75
+ process.exit(0);
76
+ });
77
+ });
78
+ })));
79
+ if (Either.isLeft(started)) {
80
+ serverLogger.error('Fatal error starting server', {
81
+ error: getErrorMessage(started.left, String(started.left)),
82
+ });
83
+ process.exit(1);
84
+ }
85
+ }
86
+ const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
87
+ const modulePath = resolve(fileURLToPath(import.meta.url));
88
+ if (entryPath && modulePath === entryPath) {
89
+ await main();
90
+ }
@@ -1,10 +1,10 @@
1
1
  /** Renders the live family index as a Markdown table. */
2
- function familyTable(snapshot) {
2
+ const familyTable = (snapshot) => {
3
3
  const rows = snapshot.families
4
4
  .map((family) => `| ${family.title} | ${family.count} | ${family.blurb} |`)
5
5
  .join('\n');
6
6
  return ['| Family | Tools | Covers |', '| --- | --- | --- |', rows].join('\n');
7
- }
7
+ };
8
8
  /**
9
9
  * Builds the "using" skill: how a developer's AI should drive the eBay MCP
10
10
  * tools. Workflow-first and lean — the per-tool schemas are already visible via
@@ -14,67 +14,70 @@ function familyTable(snapshot) {
14
14
  *
15
15
  * @param snapshot Live tool count and family index.
16
16
  * @returns The provider-neutral skill document.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const doc = buildUsingDoc(snapshot);
21
+ * ```
17
22
  */
18
- export function buildUsingDoc(snapshot) {
19
- return {
20
- slug: 'ebay-mcp-using',
21
- title: 'Using the eBay MCP tools',
22
- description: `Drive eBay's Sell APIs through the ebay-mcp server: ${snapshot.toolCount} tools for listings, orders, marketing, and analytics. Use when creating or revising listings, fulfilling orders, issuing refunds, running Promoted Listings campaigns, or debugging eBay auth/rate-limit errors.`,
23
- intro: `The \`ebay-mcp\` server exposes **${snapshot.toolCount} tools across 100% of eBay's Sell APIs**, running locally over MCP. Every tool is named \`ebay_<verb>_<noun>\` (plus two ChatGPT-connector tools, \`search\`/\`fetch\`). You already see each tool's input schema via \`tools/list\` — this skill covers what discovery can't: which tools to chain, what eBay requires first, and how to read its errors.`,
24
- sections: [
25
- {
26
- heading: 'Authentication & environment',
27
- body: [
28
- '- **Two auth tiers.** An app token allows ≈1,000 requests/day; user OAuth allows ≈10k–50k/day. Most write operations require user OAuth.',
29
- '- **A 401 / "access denied" is almost always a scope or environment problem**, not a malformed request. Check `ebay_get_token_status`, then re-run `npm run setup` if a scope is missing.',
30
- '- **Sandbox and production are separate worlds** — separate credentials and separate data. Confirm `EBAY_ENVIRONMENT` before investigating "missing" data.',
31
- '- **Marketplace & language headers matter.** Calls honor `EBAY_MARKETPLACE_ID` (e.g. `EBAY_US`) and `Content-Language` (e.g. `en-US`); a wrong pair causes empty or rejected results.',
32
- ].join('\n'),
33
- },
34
- {
35
- heading: 'Core workflows',
36
- body: [
37
- '**Publish a fixed-price listing (REST Inventory model).**',
38
- '1. One-time account setup: `ebay_opt_in_to_program` (business-policy management), then `ebay_create_payment_policy`, `ebay_create_return_policy`, `ebay_create_fulfillment_policy` for reusable policy IDs, and `ebay_create_or_replace_inventory_location` for a `merchantLocationKey`.',
39
- '2. `ebay_create_inventory_item` (SKU + product details + availability).',
40
- '3. `ebay_create_offer` (SKU, marketplace, price, the policy IDs, location).',
41
- '4. `ebay_publish_offer` (offerId) → a live `listingId`. Use `ebay_bulk_publish_offer` for many at once.',
42
- '',
43
- '**Pick the right category & required item specifics first.**',
44
- '`ebay_get_default_category_tree_id` (marketplace) → `ebay_get_category_suggestions` (query) → `ebay_get_item_aspects_for_category` to learn the required aspects to put on the inventory item.',
45
- '',
46
- '**Fulfill an order.**',
47
- '`ebay_get_orders` (filter unfulfilled) → `ebay_get_order` (detail) → `ebay_create_shipping_fulfillment` (tracking number + carrier).',
48
- '',
49
- '**Refund or handle a payment dispute.**',
50
- 'Voluntary refund: `ebay_issue_refund`. Disputes: `ebay_get_payment_dispute_summaries` → `ebay_get_payment_dispute` → `ebay_accept_payment_dispute` (or contest it).',
51
- '',
52
- '**Promote listings (Promoted Listings).**',
53
- '`ebay_create_campaign` → `ebay_bulk_create_ads_by_inventory_reference`. The Marketing family also covers promotions and reports.',
54
- '',
55
- '**Legacy XML path (Trading API).**',
56
- '`ebay_create_listing` / `ebay_revise_listing` / `ebay_relist_item` / `ebay_end_listing`, list via `ebay_get_active_listings`. Use only when you specifically need the legacy flow — do not mix it with the REST Inventory model for the same SKU.',
57
- '',
58
- '**Diagnose failing calls.**',
59
- '`ebay_get_token_status` (auth valid/expiring?) → `ebay_get_rate_limits` / `ebay_get_user_rate_limits` (quota hit?) → `ebay_get_api_status` (eBay-side outage?).',
60
- ].join('\n'),
61
- },
62
- {
63
- heading: 'Gotchas & disambiguation',
64
- body: [
65
- '- **Two listing models.** REST Inventory (`inventory_item` → `offer` → `publish`) vs legacy Trading (XML). Pick one per SKU and stay in it.',
66
- '- **Offers need policies + a location first.** A "policy not found" error usually means the one-time account setup was skipped.',
67
- '- **`search` and `fetch` are ChatGPT-connector only** — ignore them when driving the Sell API directly.',
68
- '- **Write actions are real.** `publish`, `issue_refund`, `end_listing`, and any `bulk_*` change live seller data — confirm intent before calling.',
69
- ].join('\n'),
70
- },
71
- {
72
- heading: `Tool families (${snapshot.toolCount} tools)`,
73
- body: familyTable(snapshot),
74
- },
75
- ],
76
- };
77
- }
23
+ export const buildUsingDoc = (snapshot) => ({
24
+ slug: 'ebay-mcp-using',
25
+ title: 'Using the eBay MCP tools',
26
+ description: `Drive eBay's Sell APIs through the ebay-mcp server: ${snapshot.toolCount} tools for listings, orders, marketing, and analytics. Use when creating or revising listings, fulfilling orders, issuing refunds, running Promoted Listings campaigns, or debugging eBay auth/rate-limit errors.`,
27
+ intro: `The \`ebay-mcp\` server exposes **${snapshot.toolCount} tools across 100% of eBay's Sell APIs**, running locally over MCP. Every tool is named \`ebay_<verb>_<noun>\` (plus two ChatGPT-connector tools, \`search\`/\`fetch\`). You already see each tool's input schema via \`tools/list\` — this skill covers what discovery can't: which tools to chain, what eBay requires first, and how to read its errors.`,
28
+ sections: [
29
+ {
30
+ heading: 'Authentication & environment',
31
+ body: [
32
+ '- **Two auth tiers.** An app token allows ≈1,000 requests/day; user OAuth allows ≈10k–50k/day. Most write operations require user OAuth.',
33
+ '- **A 401 / "access denied" is almost always a scope or environment problem**, not a malformed request. Check `ebay_get_token_status`, then re-run `npm run setup` if a scope is missing.',
34
+ '- **Sandbox and production are separate worlds** — separate credentials and separate data. Confirm `EBAY_ENVIRONMENT` before investigating "missing" data.',
35
+ '- **Marketplace & language headers matter.** Calls honor `EBAY_MARKETPLACE_ID` (e.g. `EBAY_US`) and `Content-Language` (e.g. `en-US`); a wrong pair causes empty or rejected results.',
36
+ ].join('\n'),
37
+ },
38
+ {
39
+ heading: 'Core workflows',
40
+ body: [
41
+ '**Publish a fixed-price listing (REST Inventory model).**',
42
+ '1. One-time account setup: `ebay_opt_in_to_program` (business-policy management), then `ebay_create_payment_policy`, `ebay_create_return_policy`, `ebay_create_fulfillment_policy` for reusable policy IDs, and `ebay_create_inventory_location` for a `merchantLocationKey`.',
43
+ '2. `ebay_create_or_replace_inventory_item` (SKU + product details + availability).',
44
+ '3. `ebay_create_offer` (SKU, marketplace, price, the policy IDs, location).',
45
+ '4. `ebay_publish_offer` (offerId) → a live `listingId`. Use `ebay_bulk_publish_offer` for many at once.',
46
+ '',
47
+ '**Pick the right category & required item specifics first.**',
48
+ '`ebay_get_default_category_tree_id` (marketplace) → `ebay_get_category_suggestions` (query) → `ebay_get_item_aspects_for_category` to learn the required aspects to put on the inventory item.',
49
+ '',
50
+ '**Fulfill an order.**',
51
+ '`ebay_get_orders` (filter unfulfilled) → `ebay_get_order` (detail) → `ebay_create_shipping_fulfillment` (tracking number + carrier).',
52
+ '',
53
+ '**Refund or handle a payment dispute.**',
54
+ 'Voluntary refund: `ebay_issue_refund`. Disputes: `ebay_get_payment_dispute_summaries` → `ebay_get_payment_dispute` → `ebay_accept_payment_dispute` (or contest it).',
55
+ '',
56
+ '**Promote listings (Promoted Listings).**',
57
+ '`ebay_create_campaign` → `ebay_bulk_create_ads_by_inventory_reference`. The Marketing family also covers promotions and reports.',
58
+ '',
59
+ '**Legacy XML path (Trading API).**',
60
+ '`ebay_create_listing` / `ebay_revise_listing` / `ebay_relist_item` / `ebay_end_listing`, list via `ebay_get_active_listings`. Use only when you specifically need the legacy flow — do not mix it with the REST Inventory model for the same SKU.',
61
+ '',
62
+ '**Diagnose failing calls.**',
63
+ '`ebay_get_token_status` (auth valid/expiring?) → `ebay_get_rate_limits` / `ebay_get_user_rate_limits` (quota hit?) → `ebay_get_api_status` (eBay-side outage?).',
64
+ ].join('\n'),
65
+ },
66
+ {
67
+ heading: 'Gotchas & disambiguation',
68
+ body: [
69
+ '- **Two listing models.** REST Inventory (`inventory_item` → `offer` → `publish`) vs legacy Trading (XML). Pick one per SKU and stay in it.',
70
+ '- **Offers need policies + a location first.** A "policy not found" error usually means the one-time account setup was skipped.',
71
+ '- **`search` and `fetch` are ChatGPT-connector only** — ignore them when driving the Sell API directly.',
72
+ '- **Write actions are real.** `publish`, `issue_refund`, `end_listing`, and any `bulk_*` change live seller data — confirm intent before calling.',
73
+ ].join('\n'),
74
+ },
75
+ {
76
+ heading: `Tool families (${snapshot.toolCount} tools)`,
77
+ body: familyTable(snapshot),
78
+ },
79
+ ],
80
+ });
78
81
  /**
79
82
  * Builds the "contributing" skill: how an AI working on the ebay-mcp repo should
80
83
  * operate. Mirrors AGENTS.md (validation commands, module map, the add-a-tool
@@ -82,60 +85,63 @@ export function buildUsingDoc(snapshot) {
82
85
  *
83
86
  * @param snapshot Live tool count and family index.
84
87
  * @returns The provider-neutral skill document.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const doc = buildContributingDoc(snapshot);
92
+ * ```
85
93
  */
86
- export function buildContributingDoc(snapshot) {
87
- return {
88
- slug: 'ebay-mcp-contributing',
89
- title: 'Contributing to ebay-mcp',
90
- description: `Work on the ebay-mcp server itself: ${snapshot.toolCount} tools across eBay's Sell APIs (TypeScript/ESM, Zod, OpenAPI-generated types). Use when adding or changing eBay tools/endpoints, wiring the registry, or running the project's checks.`,
91
- intro: `A local MCP server exposing **${snapshot.toolCount} tools across 100% of eBay's Sell APIs** — TypeScript/Node.js (ESM), \`@modelcontextprotocol/sdk\`, Zod validation, OpenAPI-generated types. Entry points: \`src/index.ts\` (STDIO) and \`src/server-http.ts\` (HTTP).`,
92
- sections: [
93
- {
94
- heading: 'Validation (run before a PR)',
95
- body: [
96
- '```bash',
97
- 'npm run check # tsc --noEmit + eslint + prettier --check (must pass)',
98
- 'npm test # vitest run',
99
- 'npm run build # tsc + tsc-alias → build/',
100
- '```',
101
- ].join('\n'),
102
- },
103
- {
104
- heading: 'Module map (`src/`)',
105
- body: [
106
- '| Path | Owns |',
107
- '| --- | --- |',
108
- '| `index.ts` / `server-http.ts` | MCP entry points (STDIO / HTTP) |',
109
- '| `api/` | eBay API client implementations (one area per file) |',
110
- '| `auth/` | OAuth 2.0 flow and token management |',
111
- '| `config/` | Environment loading, constants, marketplace defaults |',
112
- '| `tools/` | Tool wiring — `registry.ts`, `contracts.ts`, `define-tool.ts`, and `categories/` (13 family files that co-locate each tool definition with its handler via `defineTool`) |',
113
- "| `skills/` | Agent-skills generator (this skill's source) |",
114
- '| `schemas/` | Shared Zod schemas |',
115
- "| `types/` | TypeScript types — **auto-generated** from OpenAPI specs (don't hand-edit) |",
116
- '| `scripts/` | CLI tooling: `setup.ts`, `skills.ts`, `dev-sync.ts`, `diagnostics.ts` |',
117
- '| `utils/` | Shared utilities (logging, http, errors) |',
118
- ].join('\n'),
119
- },
120
- {
121
- heading: 'Add a tool or endpoint',
122
- body: [
123
- '1. `npm run sync` — download the latest eBay specs, regenerate types, report missing endpoints.',
124
- '2. Add the API method in `src/api/`.',
125
- '3. Add a `defineTool({ ... handler })` entry in the matching `src/tools/categories/<family>.ts` — definition and handler live together; `registry.ts` derives everything from `categories/index.ts`.',
126
- '4. Add tests in `tests/`.',
127
- '5. `npm run check && npm test`.',
128
- ].join('\n'),
129
- },
130
- {
131
- heading: 'Conventions',
132
- body: [
133
- '- **No `any`** — specific types; prefer narrowing over assertions. `types/` is generated, so model new shapes from the specs.',
134
- '- Validate tool inputs with Zod; derive related schemas rather than duplicating fields.',
135
- '- Commit with Conventional Commits (releases are changeset-driven).',
136
- '- Logs go to **stderr** only — stdout is reserved for the MCP protocol.',
137
- ].join('\n'),
138
- },
139
- ],
140
- };
141
- }
94
+ export const buildContributingDoc = (snapshot) => ({
95
+ slug: 'ebay-mcp-contributing',
96
+ title: 'Contributing to ebay-mcp',
97
+ description: `Work on the ebay-mcp server itself: ${snapshot.toolCount} tools across eBay's Sell APIs (TypeScript/ESM, Effect-backed validation, OpenAPI-generated types). Use when adding or changing eBay tools/endpoints, wiring the registry, or running the project's checks.`,
98
+ intro: `A local MCP server exposing **${snapshot.toolCount} tools across 100% of eBay's Sell APIs** — TypeScript/Node.js (ESM), \`@modelcontextprotocol/sdk\`, Effect-backed validation, OpenAPI-generated types. Entry points: \`src/index.ts\` (STDIO) and \`src/serverHttp.ts\` (HTTP).`,
99
+ sections: [
100
+ {
101
+ heading: 'Validation (run before a PR)',
102
+ body: [
103
+ '```bash',
104
+ 'npm run check # tsc --noEmit + eslint + prettier --check (must pass)',
105
+ 'npm test # vitest run',
106
+ 'npm run build # tsc + tsc-alias → build/',
107
+ '```',
108
+ ].join('\n'),
109
+ },
110
+ {
111
+ heading: 'Module map (`src/`)',
112
+ body: [
113
+ '| Path | Owns |',
114
+ '| --- | --- |',
115
+ '| `index.ts` / `serverHttp.ts` | MCP entry points (STDIO / HTTP) |',
116
+ '| `api/` | eBay API client implementations (one area per file) |',
117
+ '| `auth/` | OAuth 2.0 flow and token management |',
118
+ '| `config/` | Environment loading, constants, marketplace defaults |',
119
+ '| `tools/` | Tool wiring — `registry.ts`, `contracts.ts`, `defineTool.ts`, and `categories/` (13 family files that co-locate each tool definition with its handler via `defineTool`) |',
120
+ "| `skills/` | Agent-skills generator (this skill's source) |",
121
+ '| `schemas/` | Shared Effect-backed schemas |',
122
+ "| `types/` | TypeScript types — **auto-generated** from OpenAPI specs (don't hand-edit) |",
123
+ '| `scripts/` | CLI tooling: `setup.ts`, `skills.ts`, `devSync.ts`, `diagnostics.ts` |',
124
+ '| `utils/` | Shared utilities (logging, http, errors) |',
125
+ ].join('\n'),
126
+ },
127
+ {
128
+ heading: 'Add a tool or endpoint',
129
+ body: [
130
+ '1. `npm run sync` — download the latest eBay specs, regenerate types, report missing endpoints.',
131
+ '2. Add the API method in `src/api/`.',
132
+ '3. Add a `defineTool({ ... handler })` entry in the matching `src/tools/categories/<family>.ts` — definition and handler live together; `registry.ts` derives everything from `categories/index.ts`.',
133
+ '4. Add tests in `tests/`.',
134
+ '5. `npm run check && npm test`.',
135
+ ].join('\n'),
136
+ },
137
+ {
138
+ heading: 'Conventions',
139
+ body: [
140
+ '- **No `any`** — specific types; prefer narrowing over assertions. `types/` is generated, so model new shapes from the specs.',
141
+ '- Validate tool inputs with Effect-backed schemas from `@/utils/effectSchema.js`; derive related schemas rather than duplicating fields.',
142
+ '- Commit with Conventional Commits (releases are changeset-driven).',
143
+ '- Logs go to **stderr** only — stdout is reserved for the MCP protocol.',
144
+ ].join('\n'),
145
+ },
146
+ ],
147
+ });