ebay-mcp 1.6.1 → 1.7.1

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.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/yosefhayim-ebay-api-mcp-server-badge.png)](https://mseep.ai/app/yosefhayim-ebay-api-mcp-server)
12
12
  <a href="https://www.buymeacoffee.com/yosefhayim" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
13
13
 
14
- A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providing AI assistants with comprehensive access to eBay's Sell APIs. Includes **387 tools** for inventory management, order fulfillment, marketing campaigns, analytics, developer tools, and more.
14
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server providing AI assistants with comprehensive access to eBay's Sell APIs. Includes **325 tools** for inventory management, order fulfillment, marketing campaigns, analytics, developer tools, and more.
15
15
 
16
16
  **API Coverage:** 100% (270 unique eBay API endpoints)
17
17
 
@@ -99,6 +99,7 @@ For official eBay API support, please refer to the [eBay Developer Program](http
99
99
  - [⚠️ Disclaimer](#️-disclaimer)
100
100
  - [Features](#features)
101
101
  - [Quick Start](#quick-start)
102
+ - [Demo](#demo)
102
103
  - [Visual Setup Guide](#visual-setup-guide)
103
104
  - [Configuration](#configuration)
104
105
  - [Available Tools](#available-tools)
@@ -112,7 +113,7 @@ For official eBay API support, please refer to the [eBay Developer Program](http
112
113
 
113
114
  ## Features
114
115
 
115
- - **387 eBay API Tools** - 100% coverage of eBay Sell APIs across inventory, orders, marketing, analytics, developer tools, and more
116
+ - **325 eBay API Tools** - 100% coverage of eBay Sell APIs across inventory, orders, marketing, analytics, developer tools, and more
116
117
  - **9 AI Clients Supported** - Auto-configuration for Claude Desktop, Cursor, Zed, Cline, Continue.dev, Windsurf, Roo Code, Claude Code CLI, and Amazon Q
117
118
  - **OAuth 2.0 Support** - Full user token management with automatic refresh
118
119
  - **Type Safety** - Built with TypeScript, Zod validation, and OpenAPI-generated types
@@ -147,122 +148,65 @@ npm install
147
148
  npm run build
148
149
  ```
149
150
 
150
- ### 3. Configure
151
+ ### 3. Run Setup Wizard
151
152
 
152
- Run the interactive setup wizard:
153
+ The interactive setup wizard handles everything for you:
153
154
 
154
155
  ```bash
155
156
  npm run setup
156
157
  ```
157
158
 
158
- Or configure manually by copying `.env.example` to `.env` and editing your credentials.
159
+ The wizard will:
160
+ - Configure your eBay credentials
161
+ - Set up OAuth authentication (for higher rate limits)
162
+ - Auto-detect and configure your MCP client (Claude Desktop, etc.)
163
+ - Save all configuration automatically
159
164
 
160
165
  ---
161
166
 
162
- ## Visual Setup Guide
167
+ ## Demo
163
168
 
164
- Follow these steps to configure OAuth authentication for higher rate limits (10k-50k requests/day):
169
+ See the eBay MCP Server in action with Claude Desktop:
165
170
 
166
- ### Step 1: Copy Client ID and Client Secret
171
+ https://github.com/user-attachments/assets/0173c8df-221c-4943-a4ce-cd20bce79f4b
167
172
 
168
- Navigate to the [eBay Developer Portal](https://developer.ebay.com/my/keys) and copy your **App ID (Client ID)** and **Cert ID (Client Secret)** from either Sandbox or Production environment.
173
+ ---
169
174
 
170
- ![Step 1 - Copy credentials from eBay Developer Portal](public/screenshot-guides/STEP%20-%201%20-%20COPY%20CLIENT%20ID%20AND%20CLIENT%20SECRET%20TO%20ENV%20FILE.png)
175
+ ## Visual Setup Guide
171
176
 
172
- Add these credentials to your `.env` file:
173
- ```bash
174
- EBAY_CLIENT_ID=your_app_id_here
175
- EBAY_CLIENT_SECRET=your_cert_id_here
176
- EBAY_ENVIRONMENT=sandbox # or "production"
177
- ```
177
+ The setup wizard (`npm run setup`) handles OAuth authentication automatically. Here's where to find your credentials in the eBay Developer Portal:
178
178
 
179
- ### Step 2: Copy Your Redirect URL (RuName)
179
+ ### Finding Your Credentials
180
180
 
181
- In the Developer Portal, go to your application's **User Tokens** settings and copy the **RuName** (eBay Redirect URL). This is required for OAuth authentication.
181
+ **Step 1:** Navigate to [eBay Developer Portal](https://developer.ebay.com/my/keys) and copy your **App ID (Client ID)** and **Cert ID (Client Secret)**:
182
182
 
183
- ![Step 2 - Copy RuName from eBay Sign-in Settings](public/screenshot-guides/STEP%20-%202%20-%20COPY%20REDIRECT%20URL.png)
183
+ ![Step 1 - Copy credentials from eBay Developer Portal](public/screenshot-guides/STEP%20-%201%20-%20COPY%20CLIENT%20ID%20AND%20CLIENT%20SECRET%20TO%20ENV%20FILE.png)
184
184
 
185
- Add the RuName to your `.env` file:
186
- ```bash
187
- EBAY_REDIRECT_URI=your_runame_here
188
- ```
185
+ **Step 2:** In your app's **User Tokens** settings, copy the **RuName** (eBay Redirect URL):
189
186
 
190
- ### Step 3: Run Setup and Complete OAuth Login
187
+ ![Step 2 - Copy RuName from eBay Sign-in Settings](public/screenshot-guides/STEP%20-%202%20-%20COPY%20REDIRECT%20URL.png)
191
188
 
192
- Run the setup wizard which will open your browser automatically:
189
+ ### Running the Setup Wizard
193
190
 
194
- ```bash
195
- npm run setup
196
- ```
191
+ Run `npm run setup` and enter your credentials when prompted. The wizard will:
197
192
 
198
- Sign in to your eBay account when prompted to authorize the application.
193
+ 1. Open your browser for OAuth login automatically
194
+ 2. Guide you through the eBay sign-in process
199
195
 
200
196
  ![Step 3 - Sign in to eBay during OAuth flow](public/screenshot-guides/STEP%203%20-%20RUN%20COMMAND%20NPM%20RUN%20SETUP%20AND%20PREFORM%20OAUTH%20LOGIN.png)
201
197
 
202
- ### Step 4: Paste the Authorization Code
203
-
204
- After granting permissions, you'll be redirected to a URL containing an authorization code. Copy the **code** parameter from the URL and paste it into the setup wizard terminal.
198
+ 3. Ask you to paste the authorization code from the callback URL
205
199
 
206
200
  ![Step 4 - Paste authorization code into setup wizard](public/screenshot-guides/STEP%20-%204%20-%20PASTE%20INTO%20THE%20SETUP%20WIZARD.png)
207
201
 
208
- The setup wizard will automatically exchange the code for tokens and save your refresh token to the `.env` file.
202
+ 4. Exchange the code for tokens and save them automatically
203
+ 5. Configure your MCP client (Claude Desktop, etc.)
209
204
 
210
205
  **Success!** You now have user token authentication with 10k-50k requests/day instead of the default 1k/day.
211
206
 
212
207
  ---
213
208
 
214
- ### 4. Configure MCP Client
215
-
216
- Add this server to your MCP client configuration:
217
-
218
- **Claude Desktop:**
219
-
220
- Edit your Claude Desktop config file:
221
-
222
- - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
223
- - Windows: `%APPDATA%/Claude/claude_desktop_config.json`
224
- - Linux: `~/.config/Claude/claude_desktop_config.json`
225
-
226
- Add the server configuration:
227
-
228
- ```json
229
- {
230
- "mcpServers": {
231
- "ebay": {
232
- "command": "npx",
233
- "args": ["-y", "ebay-mcp"],
234
- "env": {
235
- "EBAY_CLIENT_ID": "your_client_id",
236
- "EBAY_CLIENT_SECRET": "your_client_secret",
237
- "EBAY_ENVIRONMENT": "sandbox",
238
- "EBAY_REDIRECT_URI": "your_runame"
239
- }
240
- }
241
- }
242
- }
243
- ```
244
-
245
- **Alternative: Use locally installed version**
246
-
247
- If you installed from source:
248
-
249
- ```json
250
- {
251
- "mcpServers": {
252
- "ebay": {
253
- "command": "node",
254
- "args": ["/absolute/path/to/ebay-mcp/build/index.js"],
255
- "env": {
256
- "EBAY_CLIENT_ID": "your_client_id",
257
- "EBAY_CLIENT_SECRET": "your_client_secret",
258
- "EBAY_ENVIRONMENT": "sandbox"
259
- }
260
- }
261
- }
262
- }
263
- ```
264
-
265
- ### 5. Use
209
+ ### 4. Use
266
210
 
267
211
  Restart your MCP client (Claude Desktop, etc.) and start using eBay tools through your AI assistant.
268
212
 
@@ -272,32 +216,21 @@ Restart your MCP client (Claude Desktop, etc.) and start using eBay tools throug
272
216
 
273
217
  ### Environment Variables
274
218
 
275
- Create a `.env` file with your eBay credentials:
219
+ The setup wizard (`npm run setup`) automatically creates and configures your `.env` file. For reference, these are the environment variables used:
276
220
 
277
221
  ```bash
278
222
  EBAY_CLIENT_ID=your_client_id
279
223
  EBAY_CLIENT_SECRET=your_client_secret
280
224
  EBAY_ENVIRONMENT=sandbox # or "production"
281
225
  EBAY_REDIRECT_URI=your_runame
282
-
283
- # Optional: For higher rate limits (10k-50k req/day)
284
- EBAY_USER_REFRESH_TOKEN=your_refresh_token
226
+ EBAY_USER_REFRESH_TOKEN=your_refresh_token # For higher rate limits
285
227
  ```
286
228
 
287
229
  ### OAuth Authentication
288
230
 
289
- **Client Credentials (Automatic):**
290
-
291
- - Default authentication method
292
- - 1,000 requests/day
293
- - No setup required beyond client ID and secret
231
+ **Client Credentials (Default):** 1,000 requests/day - works automatically with just Client ID and Secret.
294
232
 
295
- **User Tokens (Recommended for Production):**
296
-
297
- - 10,000-50,000 requests/day
298
- - Use `ebay_get_oauth_url` tool to generate authorization URL
299
- - Add `EBAY_USER_REFRESH_TOKEN` to `.env` after OAuth flow
300
- - Tokens refresh automatically
233
+ **User Tokens (Recommended):** 10,000-50,000 requests/day - the setup wizard handles the OAuth flow automatically. Tokens refresh automatically.
301
234
 
302
235
  For detailed OAuth setup and comprehensive configuration guide, see the [Configuration Documentation](docs/auth/CONFIGURATION.md).
303
236
 
@@ -327,7 +260,7 @@ This server supports **9 AI clients** with auto-configuration via `npm run setup
327
260
 
328
261
  ```bash
329
262
  npm install -g ebay-mcp
330
- npx ebay-mcp # Interactive setup wizard - auto-detects installed clients
263
+ npx ebay-mcp setup # Interactive setup wizard - auto-detects installed clients
331
264
  ```
332
265
 
333
266
  The setup wizard will automatically detect which AI clients you have installed and configure them for you.
@@ -377,7 +310,7 @@ Monitor your API usage in the [eBay Developer Portal](https://developer.ebay.com
377
310
 
378
311
  ## Available Tools
379
312
 
380
- The server provides **387 tools** with **100% API coverage** organized into the following categories:
313
+ The server provides **325 tools** with **100% API coverage** organized into the following categories:
381
314
 
382
315
  - **Account Management** - Policies, programs, subscriptions, sales tax
383
316
  - **Inventory Management** - Items, offers, locations, bulk operations, SKU location mapping
@@ -631,7 +564,7 @@ Log files are stored in `~/.ebay-mcp/logs/`:
631
564
 
632
565
  1. Verify you're using the correct environment (sandbox vs production)
633
566
  2. Ensure you have proper permissions/scopes for the operation
634
- 3. Check eBay API status: https://developer.ebay.com/support/api-status
567
+ 3. Check current API status with the `ebay_get_api_status` tool or the [eBay API Status](https://developer.ebay.com/support/api-status) page
635
568
  4. Run `npm run diagnose` to check your configuration
636
569
  5. Review the [eBay API documentation](https://developer.ebay.com/docs) for endpoint requirements
637
570
 
@@ -669,12 +602,20 @@ If you're still experiencing issues:
669
602
 
670
603
  ## Resources
671
604
 
605
+ ### API Status
606
+
607
+ Check current eBay API health, incidents, and fixes:
608
+
609
+ - [eBay API Status](https://developer.ebay.com/support/api-status) - Official status page
610
+ - [API Status RSS feed](https://developer.ebay.com/rss/api-status) - Latest issues and resolutions (XML)
611
+ - **`ebay_get_api_status`** - MCP tool that returns the latest items from this feed (filter by status or API name, optional limit)
612
+ - [Latest snapshot (auto-updated)](docs/API_STATUS.md) - In-repo digest of recent status items
613
+
672
614
  ### Documentation
673
615
 
674
616
  - [eBay Developer Portal](https://developer.ebay.com/) - API documentation and credentials
675
617
  - [eBay API License Agreement](https://developer.ebay.com/join/api-license-agreement) - Terms of use and compliance requirements
676
618
  - [eBay Data Handling Requirements](https://developer.ebay.com/api-docs/static/data-handling-update.html) - Important data protection and privacy guidelines
677
- - [eBay API Status](https://developer.ebay.com/support/api-status) - Real-time API health and status
678
619
  - [MCP Documentation](https://modelcontextprotocol.io/) - Model Context Protocol specification
679
620
  - [OAuth Quick Reference](docs/auth/OAUTH_QUICK_REFERENCE.md) - **Complete OAuth authentication guide with scopes, troubleshooting, and examples**
680
621
  - [OAuth Setup Guide](docs/auth/) - Detailed authentication configuration
@@ -149,7 +149,9 @@ export class EbayApiClient {
149
149
  attempt: `${retryCount + 1}/3`,
150
150
  delayMs: Math.min(delay, 5000),
151
151
  });
152
- await new Promise((resolve) => setTimeout(resolve, Math.min(delay, 5000)));
152
+ await new Promise((resolve) => {
153
+ setTimeout(resolve, Math.min(delay, 5000));
154
+ });
153
155
  return await this.httpClient.request(config);
154
156
  }
155
157
  }
@@ -168,8 +170,8 @@ export class EbayApiClient {
168
170
  * Validate that access token is available before making API request
169
171
  */
170
172
  validateAccessToken() {
171
- if (!this.authClient.hasUserTokens()) {
172
- throw new Error('Access token is missing. Please provide your access token and refresh token by calling ebay_set_user_tokens tool in order to perform API requests.');
173
+ if (!this.config.clientId || !this.config.clientSecret) {
174
+ throw new Error('Missing required eBay credentials. Please set EBAY_CLIENT_ID and EBAY_CLIENT_SECRET in your .env file.');
173
175
  }
174
176
  }
175
177
  /**
@@ -225,8 +227,8 @@ export class EbayApiClient {
225
227
  /**
226
228
  * Set user access and refresh tokens
227
229
  */
228
- async setUserTokens(accessToken, refreshToken) {
229
- await this.authClient.setUserTokens(accessToken, refreshToken);
230
+ async setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry) {
231
+ this.authClient.setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry);
230
232
  }
231
233
  /**
232
234
  * Get token information for debugging
@@ -87,8 +87,8 @@ export class EbaySellerApi {
87
87
  /**
88
88
  * Set user access and refresh tokens
89
89
  */
90
- async setUserTokens(accessToken, refreshToken) {
91
- await this.client.setUserTokens(accessToken, refreshToken);
90
+ async setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry) {
91
+ await this.client.setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry);
92
92
  }
93
93
  /**
94
94
  * Get OAuth client for advanced operations
@@ -18,7 +18,7 @@ export class IdentityApi {
18
18
  async getUser() {
19
19
  const config = this.client.getConfig();
20
20
  const identityBaseUrl = getIdentityBaseUrl(config.environment);
21
- const fullUrl = `${identityBaseUrl}${this.basePath}/user/`;
21
+ const fullUrl = `${identityBaseUrl}${this.basePath}/user`;
22
22
  return await this.client.getWithFullUrl(fullUrl);
23
23
  }
24
24
  }
@@ -1,7 +1,7 @@
1
1
  import axios from 'axios';
2
2
  import { getBaseUrl, getDefaultScopes } from '../config/environment.js';
3
3
  import { LocaleEnum } from '../types/ebay-enums.js';
4
- import { readFileSync, writeFileSync } from 'fs';
4
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
5
5
  import { join } from 'path';
6
6
  import { authLogger } from '../utils/logger.js';
7
7
  /**
@@ -10,7 +10,7 @@ import { authLogger } from '../utils/logger.js';
10
10
  function updateEnvFile(updates) {
11
11
  try {
12
12
  const envPath = join(process.cwd(), '.env');
13
- let envContent = readFileSync(envPath, 'utf-8');
13
+ let envContent = existsSync(envPath) ? readFileSync(envPath, 'utf-8') : '';
14
14
  // Update each key-value pair
15
15
  for (const [key, value] of Object.entries(updates)) {
16
16
  // Match the key with or without value, handling comments
@@ -28,7 +28,7 @@ function updateEnvFile(updates) {
28
28
  writeFileSync(envPath, envContent, 'utf-8');
29
29
  // Tokens updated silently - console output interferes with MCP JSON protocol
30
30
  }
31
- catch (error) {
31
+ catch (_error) {
32
32
  // Silent failure - error logging interferes with MCP JSON protocol
33
33
  // If needed, check .env file manually
34
34
  }
@@ -180,7 +180,7 @@ export class EbayOAuthClient {
180
180
  */
181
181
  async getOrRefreshAppAccessToken() {
182
182
  // Return cached token if still valid
183
- if (this.appAccessToken) {
183
+ if (this.appAccessToken && Date.now() < this.appAccessTokenExpiry) {
184
184
  return this.appAccessToken;
185
185
  }
186
186
  const authUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
@@ -1,8 +1,9 @@
1
1
  import { config } from 'dotenv';
2
- import { readFileSync } from 'fs';
2
+ import { existsSync, readFileSync } from 'fs';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { dirname, join } from 'path';
5
5
  import { LocaleEnum } from '../types/ebay-enums.js';
6
+ import { getVersion } from '../utils/version.js';
6
7
  // Load .env silently - suppress dotenv output to keep stdout clean for MCP JSON-RPC
7
8
  config({ quiet: true });
8
9
  // Get the current directory for loading scope files
@@ -58,7 +59,16 @@ function getSandboxScopes() {
58
59
  * Get default scopes for the specified environment
59
60
  */
60
61
  export function getDefaultScopes(environment) {
61
- return environment === 'production' ? getProductionScopes() : getSandboxScopes();
62
+ if (environment === 'production') {
63
+ return getProductionScopes();
64
+ }
65
+ const sandboxScopes = getSandboxScopes();
66
+ const productionScopes = getProductionScopes();
67
+ const productionWithoutEdelivery = productionScopes.filter((scope) => scope !== 'https://api.ebay.com/oauth/scope/sell.edelivery');
68
+ const mergedScopes = new Set([...sandboxScopes, ...productionWithoutEdelivery]);
69
+ mergedScopes.add('https://api.ebay.com/oauth/api_scope/sell.edelivery');
70
+ mergedScopes.add('https://api.ebay.com/oauth/scope/sell.edelivery');
71
+ return Array.from(mergedScopes);
62
72
  }
63
73
  /**
64
74
  * Validate scopes against environment and return warnings for invalid scopes
@@ -172,14 +182,15 @@ export function getAuthUrl(clientIdOrEnvironment, redirectUri, environment, loca
172
182
  }
173
183
  // Otherwise, generate the full OAuth authorization URL
174
184
  const clientId = clientIdOrEnvironment;
175
- const env = environment || 'sandbox';
185
+ const env = environment ?? 'sandbox';
176
186
  const scope = getDefaultScopes(env);
177
187
  if (!(clientId && redirectUri)) {
178
188
  console.error('clientId, redirectUri (RuName), and scope are required,please initialize the class properly.');
179
189
  return '';
180
190
  }
181
- const authDomain = env === 'production' ? 'https://auth.ebay.com' : 'https://auth.sandbox.ebay.com';
182
- const params = new URLSearchParams({
191
+ // Build consent URL with auth2 endpoint (eBay's OAuth consent endpoint)
192
+ const consentDomain = env === 'production' ? 'https://auth2.ebay.com' : 'https://auth2.sandbox.ebay.com';
193
+ const consentParams = new URLSearchParams({
183
194
  client_id: clientId,
184
195
  redirect_uri: redirectUri,
185
196
  response_type: responseType,
@@ -188,7 +199,10 @@ export function getAuthUrl(clientIdOrEnvironment, redirectUri, environment, loca
188
199
  locale,
189
200
  ...(state ? { state } : {}),
190
201
  });
191
- return `${authDomain}/oauth2/authorize?${params.toString()}`;
202
+ const consentUrl = `${consentDomain}/oauth2/consents?${consentParams.toString()}`;
203
+ // Build signin URL that redirects to consent
204
+ const signinDomain = env === 'production' ? 'https://signin.ebay.com' : 'https://signin.sandbox.ebay.com';
205
+ return `${signinDomain}/signin?ru=${encodeURIComponent(consentUrl)}&sgfl=oauth2&AppName=${encodeURIComponent(clientId)}`;
192
206
  }
193
207
  /**
194
208
  * Generate the OAuth authorization URL for user consent
@@ -227,34 +241,52 @@ environment, scopes, locale, state) {
227
241
  const ruParam = encodeURIComponent(`${authorizeEndpoint}?${params.toString()}`);
228
242
  return `${signinDomain}/signin?ru=${ruParam}&sgfl=oauth2_login&AppName=${clientId}`;
229
243
  }
244
+ const iconUrl = (size) => {
245
+ const url = new URL(`../../public/icons/${size}.png`, import.meta.url);
246
+ const path = fileURLToPath(url);
247
+ if (!existsSync(path)) {
248
+ console.warn(`[eBay MCP] Icon not found at ${path}. Ensure public/icons is included in the package.`);
249
+ }
250
+ return url.toString();
251
+ };
230
252
  export const mcpConfig = {
231
253
  name: 'eBay API Model Context Protocol Server',
232
- version: '1.4.2',
254
+ version: getVersion(),
233
255
  title: 'eBay API Model Context Protocol Server',
234
- websiteUrl: 'https://github.com/ebay/ebay-mcp-server',
256
+ websiteUrl: 'https://github.com/YosefHayim/ebay-mcp',
235
257
  icons: [
236
258
  {
237
- src: './48x48.png',
259
+ src: iconUrl('16x16'),
260
+ mimeType: 'image/png',
261
+ sizes: ['16x16'],
262
+ },
263
+ {
264
+ src: iconUrl('32x32'),
265
+ mimeType: 'image/png',
266
+ sizes: ['32x32'],
267
+ },
268
+ {
269
+ src: iconUrl('48x48'),
238
270
  mimeType: 'image/png',
239
271
  sizes: ['48x48'],
240
272
  },
241
273
  {
242
- src: './128x128.png',
274
+ src: iconUrl('128x128'),
243
275
  mimeType: 'image/png',
244
276
  sizes: ['128x128'],
245
277
  },
246
278
  {
247
- src: './256x256.png',
279
+ src: iconUrl('256x256'),
248
280
  mimeType: 'image/png',
249
281
  sizes: ['256x256'],
250
282
  },
251
283
  {
252
- src: './512x512.png',
284
+ src: iconUrl('512x512'),
253
285
  mimeType: 'image/png',
254
286
  sizes: ['512x512'],
255
287
  },
256
288
  {
257
- src: './1024x1024.png',
289
+ src: iconUrl('1024x1024'),
258
290
  mimeType: 'image/png',
259
291
  sizes: ['1024x1024'],
260
292
  },
package/build/index.js CHANGED
@@ -5,6 +5,20 @@ import { EbaySellerApi } from './api/index.js';
5
5
  import { getEbayConfig, mcpConfig, validateEnvironmentConfig } from './config/environment.js';
6
6
  import { getToolDefinitions, executeTool } from './tools/index.js';
7
7
  import { serverLogger, toolLogger, getLogPaths } from './utils/logger.js';
8
+ import { checkForUpdates } from './utils/version.js';
9
+ checkForUpdates({ defer: true });
10
+ const args = process.argv.slice(2);
11
+ if (args.includes('setup')) {
12
+ try {
13
+ const { runSetup } = await import('./scripts/setup.js');
14
+ await runSetup();
15
+ process.exit(0);
16
+ }
17
+ catch (error) {
18
+ console.error('Setup failed:', error instanceof Error ? error.message : error);
19
+ process.exit(1);
20
+ }
21
+ }
8
22
  /**
9
23
  * eBay API MCP Server
10
24
  * Provides access to eBay APIs through Model Context Protocol
@@ -1,5 +1,5 @@
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';
@@ -9,7 +9,9 @@ import { exec } from 'child_process';
9
9
  import { fileURLToPath } from 'url';
10
10
  import prompts from 'prompts';
11
11
  import { getDefaultScopes } from '../config/environment.js';
12
+ import { checkForUpdates } from '../utils/version.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, '../..');
@@ -853,13 +855,25 @@ async function stepOAuth(state) {
853
855
  }
854
856
  }
855
857
  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';
858
+ // Build the OAuth consent URL (auth2 endpoint)
859
+ const consentDomain = state.environment === 'production'
860
+ ? 'https://auth2.ebay.com'
861
+ : 'https://auth2.sandbox.ebay.com';
859
862
  // Get scopes from environment config
860
863
  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}`;
864
+ // Build the consent URL parameters
865
+ const consentParams = new URLSearchParams({
866
+ client_id: state.config.EBAY_CLIENT_ID,
867
+ redirect_uri: state.config.EBAY_REDIRECT_URI,
868
+ response_type: 'code',
869
+ scope: scopes.join(' '),
870
+ });
871
+ const consentUrl = `${consentDomain}/oauth2/consents?${consentParams.toString()}`;
872
+ // Build the signin URL that redirects to consent
873
+ const signinDomain = state.environment === 'production'
874
+ ? 'https://signin.ebay.com'
875
+ : 'https://signin.sandbox.ebay.com';
876
+ const authUrl = `${signinDomain}/signin?ru=${encodeURIComponent(consentUrl)}&sgfl=oauth2&AppName=${encodeURIComponent(state.config.EBAY_CLIENT_ID)}`;
863
877
  console.log('\n ' + ui.bold('OAuth Authorization URL:'));
864
878
  console.log(ui.dim(' ' + '─'.repeat(56)));
865
879
  console.log(` ${ui.info(authUrl)}`);
@@ -1275,7 +1289,14 @@ process.on('SIGINT', () => {
1275
1289
  console.log(ui.warning('\n\n Setup interrupted.\n'));
1276
1290
  process.exit(0);
1277
1291
  });
1278
- main().catch((error) => {
1279
- console.error(ui.error('\n Setup failed:'), error);
1280
- process.exit(1);
1281
- });
1292
+ export async function runSetup() {
1293
+ await main();
1294
+ }
1295
+ const entryPath = process.argv[1] ? resolve(process.argv[1]) : undefined;
1296
+ const modulePath = resolve(fileURLToPath(import.meta.url));
1297
+ if (entryPath && modulePath === entryPath) {
1298
+ runSetup().catch((error) => {
1299
+ console.error(ui.error('\n Setup failed:'), error);
1300
+ process.exit(1);
1301
+ });
1302
+ }
@@ -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
  }
@@ -1,5 +1,48 @@
1
1
  import { z } from 'zod';
2
2
  export const developerTools = [
3
+ {
4
+ name: 'ebay_get_api_status',
5
+ description: 'Get the latest eBay API status and incidents from the official RSS feed. Returns recent issues, fixes, and outages for eBay APIs (e.g. Trading API, Inventory API, Sandbox). Use when the user asks about API status, outages, or fixes.',
6
+ inputSchema: {
7
+ limit: z
8
+ .number()
9
+ .int()
10
+ .min(1)
11
+ .max(50)
12
+ .optional()
13
+ .describe('Maximum number of items to return (default 20)'),
14
+ status: z
15
+ .enum(['Resolved', 'Unresolved'])
16
+ .optional()
17
+ .describe('Filter by status: Resolved or Unresolved'),
18
+ api: z
19
+ .string()
20
+ .optional()
21
+ .describe('Filter by API name (e.g. "Trading API", "Inventory API", "Sandbox")'),
22
+ },
23
+ outputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ items: {
27
+ type: 'array',
28
+ items: {
29
+ type: 'object',
30
+ properties: {
31
+ title: { type: 'string' },
32
+ summary: { type: 'string' },
33
+ link: { type: 'string' },
34
+ api: { type: 'string' },
35
+ site: { type: 'string' },
36
+ status: { type: 'string' },
37
+ lastUpdated: { type: 'string' },
38
+ },
39
+ },
40
+ },
41
+ error: { type: 'string' },
42
+ },
43
+ description: 'Latest API status items from eBay developer feed',
44
+ },
45
+ },
3
46
  {
4
47
  name: 'ebay_get_rate_limits',
5
48
  description: 'Get application rate limits for eBay APIs. Returns call quota, remaining calls, and time until reset for each API resource.',
@@ -1,5 +1,7 @@
1
1
  import { getOAuthAuthorizationUrl, validateScopes } from '../config/environment.js';
2
2
  import { accountTools, analyticsTools, communicationTools, developerTools, fulfillmentTools, inventoryTools, marketingTools, metadataTools, otherApiTools, taxonomyTools, tokenManagementTools, } from '../tools/definitions/index.js';
3
+ import { chatGptTools } from '../tools/tool-definitions.js';
4
+ import { getApiStatusFeed } from '../utils/api-status-feed.js';
3
5
  import { convertToTimestamp, validateTokenExpiry } from '../utils/date-converter.js';
4
6
  // Import Zod schemas for input validation
5
7
  import { getAwaitingFeedbackSchema, getFeedbackRatingSummarySchema, getFeedbackSchema, leaveFeedbackForBuyerSchema, respondToFeedbackSchema, } from '../utils/communication/feedback.js';
@@ -10,7 +12,9 @@ import { createDestinationSchema, createSubscriptionFilterSchema, createSubscrip
10
12
  * Get all tool definitions for the MCP server
11
13
  */
12
14
  export function getToolDefinitions() {
15
+ const chatConnectorTools = chatGptTools.filter((tool) => tool.name === 'search' || tool.name === 'fetch');
13
16
  return [
17
+ ...chatConnectorTools,
14
18
  ...tokenManagementTools,
15
19
  ...accountTools,
16
20
  ...inventoryTools,
@@ -33,14 +37,42 @@ export async function executeTool(api, toolName, args) {
33
37
  case 'search': {
34
38
  // For this example, we'll treat the query as a search for inventory items.
35
39
  // A more robust implementation might search across different types of content.
36
- const response = await api.inventory.getInventoryItems(args.limit ?? 10);
37
- const results = response.inventoryItems?.map((item, index) => ({
38
- id: `item-${index}`,
40
+ const requestedLimitRaw = args.limit;
41
+ const limit = typeof requestedLimitRaw === 'number' && Number.isFinite(requestedLimitRaw)
42
+ ? Math.max(Math.floor(requestedLimitRaw), 1)
43
+ : 10;
44
+ const query = args.query?.toLowerCase().trim();
45
+ const pageSize = query ? Math.min(Math.max(limit, 50), 200) : limit;
46
+ const matches = [];
47
+ let offset = 0;
48
+ while (matches.length < limit) {
49
+ const response = await api.inventory.getInventoryItems(pageSize, offset);
50
+ const pageItems = response.inventoryItems ?? [];
51
+ if (pageItems.length === 0) {
52
+ break;
53
+ }
54
+ // Filter to only include items with valid SKUs (required for getInventoryItem calls)
55
+ const itemsWithSku = pageItems.filter((item) => typeof item.sku === 'string' && item.sku.trim() !== '');
56
+ const filtered = query
57
+ ? itemsWithSku.filter((item) => (item.product?.title ?? '').toLowerCase().includes(query))
58
+ : itemsWithSku;
59
+ matches.push(...filtered);
60
+ offset += pageSize;
61
+ const total = response.total;
62
+ if (typeof total === 'number' && offset >= total) {
63
+ break;
64
+ }
65
+ if (!query || pageItems.length < pageSize) {
66
+ break;
67
+ }
68
+ }
69
+ const results = matches.slice(0, limit).map((item) => ({
70
+ id: item.sku,
39
71
  title: item.product?.title ?? 'No Title',
40
72
  // The URL should be a canonical link to the item, which we don't have here.
41
73
  // We'll use a placeholder.
42
74
  url: `https://www.ebay.com/`, // Placeholder URL
43
- })) ?? [];
75
+ }));
44
76
  // Format the response as required by the ChatGPT connector spec.
45
77
  return {
46
78
  content: [
@@ -207,7 +239,7 @@ export async function executeTool(api, toolName, args) {
207
239
  refreshExpiry = convertToTimestamp(refreshTokenExpiry);
208
240
  }
209
241
  // Set tokens (will use defaults if expiry times not provided)
210
- await api.setUserTokens(accessToken, refreshToken);
242
+ await api.setUserTokens(accessToken, refreshToken, accessExpiry, refreshExpiry);
211
243
  // If autoRefresh is enabled, attempt to get a fresh access token
212
244
  // (The OAuth client will handle refresh internally if needed)
213
245
  if (autoRefresh) {
@@ -1124,6 +1156,15 @@ export async function executeTool(api, toolName, args) {
1124
1156
  },
1125
1157
  ],
1126
1158
  };
1159
+ // Developer API - API Status (public RSS feed)
1160
+ case 'ebay_get_api_status': {
1161
+ const result = await getApiStatusFeed({
1162
+ limit: args.limit,
1163
+ status: args.status,
1164
+ api: args.api,
1165
+ });
1166
+ return { items: result.items, ...(result.error && { error: result.error }) };
1167
+ }
1127
1168
  // Developer API - Rate Limits
1128
1169
  case 'ebay_get_rate_limits':
1129
1170
  return await api.developer.getRateLimits(args.apiContext, args.apiName);
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Fetches and parses the eBay API Status RSS feed (public, no auth).
3
+ * Used by the ebay_get_api_status MCP tool and optionally by the docs sync script.
4
+ */
5
+ import { XMLParser } from 'fast-xml-parser';
6
+ import axios from 'axios';
7
+ const RSS_URL = 'https://developer.ebay.com/rss/api-status';
8
+ function stripHtml(html) {
9
+ return html
10
+ .replace(/<[^>]+>/g, ' ')
11
+ .replace(/\s+/g, ' ')
12
+ .trim();
13
+ }
14
+ function normalizeString(value) {
15
+ if (value == null)
16
+ return '';
17
+ if (typeof value === 'string')
18
+ return value.trim();
19
+ if (typeof value === 'number' || typeof value === 'boolean')
20
+ return String(value).trim();
21
+ return '';
22
+ }
23
+ function parseItem(raw) {
24
+ const summary = normalizeString(raw.summary) ||
25
+ stripHtml(normalizeString(raw.description)).slice(0, 300) ||
26
+ '';
27
+ return {
28
+ title: normalizeString(raw.title) || 'Untitled',
29
+ summary: summary || normalizeString(raw.title),
30
+ link: normalizeString(raw.link) || '',
31
+ api: normalizeString(raw.api) || '',
32
+ site: normalizeString(raw.site) || '',
33
+ status: normalizeString(raw.status) || '',
34
+ lastUpdated: normalizeString(raw.lastUpdated) || '',
35
+ };
36
+ }
37
+ function ensureArray(value) {
38
+ if (value == null)
39
+ return [];
40
+ return Array.isArray(value) ? value : [value];
41
+ }
42
+ /**
43
+ * Fetches the eBay API Status RSS feed, parses it, and returns items
44
+ * optionally filtered by status and API name, limited to `limit` items.
45
+ */
46
+ export async function getApiStatusFeed(options = {}) {
47
+ const { limit = 20, status: statusFilter, api: apiFilter } = options;
48
+ try {
49
+ const response = await axios.get(RSS_URL, {
50
+ timeout: 15_000,
51
+ responseType: 'text',
52
+ headers: { Accept: 'application/rss+xml, application/xml, text/xml' },
53
+ });
54
+ const xml = response.data;
55
+ const parser = new XMLParser({
56
+ ignoreAttributes: true,
57
+ trimValues: true,
58
+ parseTagValue: false,
59
+ });
60
+ const parsed = parser.parse(xml);
61
+ const channel = parsed?.rss?.channel;
62
+ if (!channel) {
63
+ return { items: [], error: 'RSS feed missing channel' };
64
+ }
65
+ const rawItems = ensureArray(channel.item);
66
+ let items = rawItems.map(parseItem);
67
+ if (statusFilter) {
68
+ items = items.filter((i) => i.status.toLowerCase() === statusFilter.toLowerCase());
69
+ }
70
+ if (apiFilter?.trim()) {
71
+ const needle = apiFilter.trim().toLowerCase();
72
+ items = items.filter((i) => i.api.toLowerCase().includes(needle));
73
+ }
74
+ items = items.slice(0, Math.min(limit, 50));
75
+ return { items };
76
+ }
77
+ catch (err) {
78
+ const message = axios.isAxiosError(err) && err.response?.status
79
+ ? `Feed unavailable (HTTP ${err.response.status})`
80
+ : err instanceof Error
81
+ ? err.message
82
+ : 'Failed to fetch API status feed';
83
+ return { items: [], error: message };
84
+ }
85
+ }
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'fs';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import updateNotifier from 'update-notifier';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+ const PACKAGE_JSON_PATH = join(__dirname, '../../package.json');
8
+ let cachedPackageJson = null;
9
+ export function getPackageJson() {
10
+ if (cachedPackageJson) {
11
+ return cachedPackageJson;
12
+ }
13
+ try {
14
+ const content = readFileSync(PACKAGE_JSON_PATH, 'utf-8');
15
+ cachedPackageJson = JSON.parse(content);
16
+ return cachedPackageJson;
17
+ }
18
+ catch {
19
+ return { name: 'ebay-mcp', version: '0.0.0' };
20
+ }
21
+ }
22
+ export function getVersion() {
23
+ return getPackageJson().version;
24
+ }
25
+ export function getPackageName() {
26
+ return getPackageJson().name;
27
+ }
28
+ const ONE_DAY_MS = 1000 * 60 * 60 * 24;
29
+ export function checkForUpdates(options = {}) {
30
+ const pkg = getPackageJson();
31
+ const notifier = updateNotifier({
32
+ pkg,
33
+ updateCheckInterval: ONE_DAY_MS,
34
+ });
35
+ notifier.notify({
36
+ isGlobal: true,
37
+ defer: options.defer ?? false,
38
+ message: 'Update available {currentVersion} → {latestVersion}\n' + 'Run {updateCommand} to update',
39
+ });
40
+ }
41
+ export async function getUpdateInfo() {
42
+ const pkg = getPackageJson();
43
+ const notifier = updateNotifier({
44
+ pkg,
45
+ updateCheckInterval: 0,
46
+ });
47
+ await notifier.fetchInfo();
48
+ if (notifier.update && notifier.update.latest !== pkg.version) {
49
+ return {
50
+ current: pkg.version,
51
+ latest: notifier.update.latest,
52
+ name: pkg.name,
53
+ };
54
+ }
55
+ return undefined;
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ebay-mcp",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Local MCP server for eBay APIs - provides access to eBay developer functionality through MCP (Model Context Protocol)",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -42,6 +42,7 @@
42
42
  "build/**/*.js",
43
43
  "build/index.d.ts",
44
44
  "build/server-http.d.ts",
45
+ "public/icons/*.png",
45
46
  "README.md"
46
47
  ],
47
48
  "repository": {
@@ -59,10 +60,12 @@
59
60
  "cors": "^2.8.5",
60
61
  "dotenv": "^17.2.3",
61
62
  "express": "^5.1.0",
63
+ "fast-xml-parser": "^5.3.4",
62
64
  "helmet": "^8.1.0",
63
65
  "jose": "^6.1.2",
64
66
  "jsonwebtoken": "^9.0.2",
65
67
  "prompts": "^2.4.2",
68
+ "update-notifier": "^7.3.1",
66
69
  "winston": "^3.19.0",
67
70
  "zod": "^3.23.8",
68
71
  "zod-to-json-schema": "^3.24.1"
@@ -75,6 +78,7 @@
75
78
  "@types/node": "^24.10.1",
76
79
  "@types/prompts": "^2.4.9",
77
80
  "@types/supertest": "^6.0.3",
81
+ "@types/update-notifier": "^6.0.8",
78
82
  "@vitest/coverage-v8": "^4.0.13",
79
83
  "@vitest/ui": "^4.0.8",
80
84
  "eslint": "^9.39.1",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file