ebay-mcp-remote-edition 4.4.0 → 4.5.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.
@@ -1,6 +1,6 @@
1
+ import { processImageForUpload, } from '../../utils/image-processor.js';
1
2
  import axios from 'axios';
2
3
  import * as fs from 'fs';
3
- import * as path from 'path';
4
4
  /**
5
5
  * Commerce Media API (v1_beta) - Upload and manage images via eBay Picture Services
6
6
  * Based on: https://developer.ebay.com/api-docs/commerce/media/resources/image/from_url/methods
@@ -21,15 +21,13 @@ export class MediaApi {
21
21
  /**
22
22
  * Upload an image from a public URL to eBay Picture Services.
23
23
  *
24
- * Flow:
25
- * 1. Download image from URL
26
- * 2. Validate dimensions (min 500px, max 4800px)
27
- * 3. Resize if needed (maintain aspect ratio)
28
- * 4. Optimize and convert to JPEG
29
- * 5. Upload to eBay via multipart/form-data
30
- * 6. Return hosted URL
24
+ * Primary flow: Pass URL directly to eBay's createImageFromUrl endpoint
25
+ * (eBay downloads and processes server-side — no local processing needed).
31
26
  *
32
- * Supported formats: JPG, GIF, PNG, BMP, TIFF, AVIF, HEIC, WEBP
27
+ * Fallback: If eBay rejects the image (e.g., too small for 500px minimum),
28
+ * download → enlarge with Sharp → upload via createImageFromFile.
29
+ *
30
+ * Supported source formats: JPG, GIF, PNG, BMP, TIFF, AVIF, HEIC, WEBP
33
31
  * Max file size: 10MB per image
34
32
  *
35
33
  * @param imageUrl - Public URL of the image to upload
@@ -43,42 +41,69 @@ export class MediaApi {
43
41
  const token = await this.getAccessToken();
44
42
  const baseUrl = this.getMediaBaseUrl();
45
43
  try {
46
- // Try direct createImageFromUrl endpoint first (eBay downloads server-side)
47
- // This avoids the need to download+process+upload ourselves
48
- const requestBody = { imageUrl };
44
+ // Primary: Pass URL directly to eBay they handle server-side download
45
+ const body = { imageUrl };
49
46
  if (description) {
50
- requestBody.description = description;
47
+ body.description = description;
51
48
  }
52
- const response = await axios.post(`${baseUrl}${this.basePath}/image/create_image_from_url`, requestBody, {
49
+ const createResponse = await axios.post(`${baseUrl}${this.basePath}/image/create_image_from_url`, body, {
53
50
  headers: {
54
51
  Authorization: `Bearer ${token}`,
55
52
  'Content-Type': 'application/json',
56
53
  Accept: 'application/json',
54
+ Prefer: 'return=representation',
57
55
  },
58
56
  timeout: 30000,
59
57
  });
60
- const data = response.data;
61
- const imageId = data.id;
62
- if (imageId) {
63
- return await this.getImage(imageId);
58
+ const responseData = createResponse.data;
59
+ const imageId = typeof responseData.id === 'string'
60
+ ? responseData.id
61
+ : createResponse.headers.location?.split('/').pop();
62
+ if (!imageId) {
63
+ throw new Error('No image ID returned from create endpoint');
64
64
  }
65
- // Fallback: if no image ID returned, try download+upload method
66
- throw new Error('No image ID returned from createImageFromUrl endpoint');
65
+ return await this.getImage(imageId);
67
66
  }
68
- catch (error) {
69
- if (axios.isAxiosError(error)) {
70
- const status = error.response?.status;
71
- const data = error.response?.data;
67
+ catch (primaryError) {
68
+ // Fallback: if eBay rejects (e.g., image too small), download → Sharp enlarge → upload via file
69
+ if (axios.isAxiosError(primaryError)) {
70
+ const status = primaryError.response?.status;
71
+ // Only fallback on errors that suggest image quality/size issues
72
+ if (status && (status === 400 || status === 500)) {
73
+ try {
74
+ console.log(`[MediaApi] Direct URL upload failed (status ${status}), falling back to Sharp processing for: ${imageUrl}`);
75
+ // Download the image
76
+ const downloadResponse = await axios.get(imageUrl, {
77
+ responseType: 'arraybuffer',
78
+ timeout: 30000,
79
+ maxContentLength: 10 * 1024 * 1024, // 10MB max
80
+ });
81
+ const imageBuffer = Buffer.from(downloadResponse.data);
82
+ // Process with Sharp (enlarge if too small, convert to JPEG)
83
+ const processed = await processImageForUpload(imageBuffer);
84
+ // Upload via file endpoint
85
+ return await this.uploadProcessedImage(processed.buffer, processed.metadata, token, baseUrl, description);
86
+ }
87
+ catch {
88
+ // If fallback also fails, throw the original error (more actionable)
89
+ throw primaryError;
90
+ }
91
+ }
92
+ }
93
+ // Re-throw non-retryable errors as-is
94
+ if (axios.isAxiosError(primaryError)) {
95
+ const status = primaryError.response?.status;
96
+ const data = primaryError.response?.data;
72
97
  const message = typeof data === 'object' && data !== null && 'errors' in data
73
98
  ? data.errors?.[0]?.longMessage ||
74
99
  data.errors?.[0]?.message ||
75
- error.message
76
- : error.message;
100
+ primaryError.message
101
+ : primaryError.message;
77
102
  throw new Error(`Failed to upload image from URL (status ${status}): ${message}`, {
78
- cause: error,
103
+ cause: primaryError,
79
104
  });
80
105
  }
81
- throw new Error(`Failed to upload image from URL: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
106
+ throw new Error(`Failed to upload image from URL: ${primaryError instanceof Error ? primaryError.message : 'Unknown error'}`, { cause: primaryError });
82
107
  }
83
108
  }
84
109
  /**
@@ -105,41 +130,10 @@ export class MediaApi {
105
130
  const baseUrl = this.getMediaBaseUrl();
106
131
  try {
107
132
  const fileBuffer = fs.readFileSync(filePath);
108
- // Build multipart/form-data body manually
109
- const boundary = `----FormBoundary${Date.now()}`;
110
- const fileName = path.basename(filePath);
111
- // Build the multipart body
112
- let body = '';
113
- // File part
114
- body += `--${boundary}\r\n`;
115
- body += `Content-Disposition: form-data; name="imageFile"; filename="${fileName}"\r\n`;
116
- body += `Content-Type: application/octet-stream\r\n\r\n`;
117
- // Description part (optional)
118
- if (description) {
119
- body += `--${boundary}\r\n`;
120
- body += `Content-Disposition: form-data; name="description"\r\n\r\n`;
121
- body += `${description}\r\n`;
122
- }
123
- body += `--${boundary}--\r\n`;
124
- const multipartBody = Buffer.concat([Buffer.from(body, 'utf-8'), Buffer.from(fileBuffer)]);
125
- const createResponse = await axios.post(`${baseUrl}${this.basePath}/image/create_image_from_file`, multipartBody, {
126
- headers: {
127
- Authorization: `Bearer ${token}`,
128
- 'Content-Type': `multipart/form-data; boundary=${boundary}`,
129
- Prefer: 'return=representation',
130
- },
131
- timeout: 30000,
132
- });
133
- // Extract image ID from response body or Location header
134
- const responseData = createResponse.data;
135
- const imageId = typeof responseData.id === 'string'
136
- ? responseData.id
137
- : createResponse.headers.location?.split('/').pop();
138
- if (!imageId) {
139
- throw new Error('No image ID returned from create endpoint');
140
- }
141
- // Fetch image details to get the eBay-hosted URL
142
- return await this.getImage(imageId);
133
+ // Process image validate dimensions, enlarge to min 500px if too small,
134
+ // convert to JPEG, and optimize. Uses sharp library.
135
+ const processed = await processImageForUpload(fileBuffer);
136
+ return await this.uploadProcessedImage(processed.buffer, processed.metadata, token, baseUrl, description);
143
137
  }
144
138
  catch (error) {
145
139
  if (axios.isAxiosError(error)) {
@@ -168,19 +162,34 @@ export class MediaApi {
168
162
  * @returns Object with image ID and eBay-hosted image URL
169
163
  */
170
164
  async uploadProcessedImage(buffer, metadata, token, baseUrl, description) {
171
- // Build multipart/form-data body
165
+ // Build multipart/form-data body correctly:
166
+ // --boundary\r\n
167
+ // Content-Disposition: imageFile\r\n
168
+ // Content-Type: image/jpeg\r\n\r\n
169
+ // [IMAGE BINARY DATA]
170
+ // --boundary\r\n
171
+ // Content-Disposition: description\r\n\r\n
172
+ // [description text]
173
+ // --boundary--\r\n
172
174
  const boundary = `----FormBoundary${Date.now()}`;
173
175
  const fileName = `image_${Date.now()}.jpg`;
174
- let body = `--${boundary}\r\n`;
175
- body += `Content-Disposition: form-data; name="imageFile"; filename="${fileName}"\r\n`;
176
- body += `Content-Type: image/jpeg\r\n\r\n`;
176
+ const parts = [];
177
+ // Image file part headers + binary data
178
+ const imageHeaders = `--${boundary}\r\n` +
179
+ `Content-Disposition: form-data; name="imageFile"; filename="${fileName}"\r\n` +
180
+ `Content-Type: image/jpeg\r\n\r\n`;
181
+ parts.push(Buffer.from(imageHeaders, 'utf-8'));
182
+ parts.push(buffer);
183
+ // Description part (optional)
177
184
  if (description) {
178
- body += `--${boundary}\r\n`;
179
- body += `Content-Disposition: form-data; name="description"\r\n\r\n`;
180
- body += `${description}\r\n`;
185
+ const descPart = `--${boundary}\r\n` +
186
+ `Content-Disposition: form-data; name="description"\r\n\r\n` +
187
+ `${description}\r\n`;
188
+ parts.push(Buffer.from(descPart, 'utf-8'));
181
189
  }
182
- body += `--${boundary}--\r\n`;
183
- const multipartBody = Buffer.concat([Buffer.from(body, 'utf-8'), buffer]);
190
+ // Closing boundary
191
+ parts.push(Buffer.from(`--${boundary}--\r\n`, 'utf-8'));
192
+ const multipartBody = Buffer.concat(parts);
184
193
  const createResponse = await axios.post(`${baseUrl}${this.basePath}/image/create_image_from_file`, multipartBody, {
185
194
  headers: {
186
195
  Authorization: `Bearer ${token}`,
@@ -219,9 +228,15 @@ export class MediaApi {
219
228
  timeout: 30000,
220
229
  });
221
230
  const data = response.data;
231
+ let imageUrl = data.imageUrl;
232
+ // eBay Media API returns $_1.JPG thumbnail URL. Convert to full-size (s-l1600.jpg)
233
+ // which is required for listing images (500px minimum).
234
+ if (imageUrl?.includes('$_1.JPG')) {
235
+ imageUrl = imageUrl.replace('$_1.JPG', 's-l1600.jpg');
236
+ }
222
237
  return {
223
238
  id: data.id,
224
- imageUrl: data.imageUrl,
239
+ imageUrl: imageUrl || '',
225
240
  description: typeof data.description === 'string' ? data.description : undefined,
226
241
  };
227
242
  }
package/build/index.js CHANGED
File without changes
@@ -32,7 +32,25 @@ function buildMarkdown(items) {
32
32
  async function main() {
33
33
  const { items, error } = await getApiStatusFeed({ limit: DEFAULT_LIMIT });
34
34
  if (error && items.length === 0) {
35
- throw new Error(`Failed to fetch API status feed: ${error}`);
35
+ // Feed is temporarily unavailable write a fallback doc instead of failing
36
+ const fallback = [
37
+ '# eBay API Status (cached)',
38
+ '',
39
+ 'The eBay API Status RSS feed is currently unavailable. Last attempt:',
40
+ `*${new Date().toISOString()}*`,
41
+ '',
42
+ `**Error:** ${error}`,
43
+ '',
44
+ 'Full list: [developer.ebay.com/support/api-status](https://developer.ebay.com/support/api-status).',
45
+ '',
46
+ '---',
47
+ '',
48
+ '*Generated by [ebay-mcp-remote-edition](https://github.com/mrnajiboy/ebay-mcp-remote-edition) API status sync.*',
49
+ ].join('\n');
50
+ writeFileSync(OUT_PATH, fallback, 'utf8');
51
+ console.warn(`Feed unavailable (${error}), wrote fallback to ${OUT_PATH}`);
52
+ process.exitCode = 0;
53
+ return;
36
54
  }
37
55
  const markdown = buildMarkdown(items);
38
56
  writeFileSync(OUT_PATH, markdown, 'utf8');
@@ -966,17 +966,42 @@ function mountEnvRouter(hardcodedEnv, serverUrl, iconBaseUrl) {
966
966
  { src: `${iconBaseUrl}/48x48.png`, mimeType: 'image/png', sizes: ['48x48'] },
967
967
  ],
968
968
  });
969
+ // Configurable tool execution timeout (60s default — most tools complete in <10s)
970
+ // Longer than individual API timeouts (30s) to allow for token refresh + API chaining
971
+ const TOOL_TIMEOUT_MS = Number(process.env.MCP_TOOL_TIMEOUT_MS ?? 60_000);
969
972
  const tools = getToolDefinitions();
970
973
  for (const toolDef of tools) {
971
974
  try {
972
975
  server.registerTool(toolDef.name, { description: toolDef.description, inputSchema: toolDef.inputSchema }, async (args) => {
976
+ const startTime = Date.now();
973
977
  try {
974
- const result = await executeTool(api, toolDef.name, args);
978
+ // Wrap tool execution with timeout to prevent indefinite hangs
979
+ const result = await Promise.race([
980
+ executeTool(api, toolDef.name, args),
981
+ new Promise((_, reject) => {
982
+ setTimeout(() => reject(new Error(`Tool ${toolDef.name} timed out after ${TOOL_TIMEOUT_MS}ms`)), TOOL_TIMEOUT_MS);
983
+ }),
984
+ ]);
985
+ const duration = Date.now() - startTime;
986
+ serverLogger.info(`[tool-exec] ${toolDef.name}`, {
987
+ userId,
988
+ environment,
989
+ durationMs: duration,
990
+ status: 'success',
991
+ });
975
992
  return {
976
993
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
977
994
  };
978
995
  }
979
996
  catch (error) {
997
+ const duration = Date.now() - startTime;
998
+ serverLogger.warn(`[tool-exec] ${toolDef.name}`, {
999
+ userId,
1000
+ environment,
1001
+ durationMs: duration,
1002
+ status: 'error',
1003
+ error: error instanceof Error ? error.message : String(error),
1004
+ });
980
1005
  return {
981
1006
  content: [
982
1007
  {
@@ -3,6 +3,31 @@ import { accountTools, analyticsTools, browseTools, communicationTools, develope
3
3
  import { chatGptTools } from '../tools/chat-tools.js';
4
4
  import { getApiStatusFeed } from '../utils/api-status-feed.js';
5
5
  import { convertToTimestamp, validateTokenExpiry } from '../utils/date-converter.js';
6
+ /**
7
+ * Tools disabled at config level because the eBay seller account doesn't support them.
8
+ * These tools return errors when called, so we exclude them from the tool list entirely.
9
+ *
10
+ * Categories:
11
+ * - Commerce Shipping: account not enrolled in commerce shipping services
12
+ * - VERO (Vendor Enforcement of Rights Online): account not enrolled in VERO program
13
+ * - Signing Keys: account doesn't have signing key permissions
14
+ *
15
+ * @see TASK-MCP.6 — Disable unsupported tools (config-level tools.exclude)
16
+ */
17
+ const EXCLUDED_TOOLS = new Set([
18
+ // Commerce Shipping (account not enrolled)
19
+ 'ebay_get_shipping_services',
20
+ 'ebay_get_dropoff_sites',
21
+ 'ebay_get_consign_preferences',
22
+ 'ebay_get_battery_qualifications',
23
+ // VERO (account not enrolled)
24
+ 'ebay_get_vero_reason_codes',
25
+ 'ebay_create_vero_report',
26
+ // Signing Keys (account doesn't have permissions)
27
+ 'ebay_suppress_violation',
28
+ 'ebay_get_signing_keys',
29
+ 'ebay_create_signing_key',
30
+ ]);
6
31
  // Import Zod schemas for input validation
7
32
  import { getAwaitingFeedbackSchema, getFeedbackRatingSummarySchema, getFeedbackSchema, leaveFeedbackForBuyerSchema, respondToFeedbackSchema, } from '../utils/communication/feedback.js';
8
33
  import { bulkUpdateConversationSchema, getConversationSchema, getConversationsSchema, sendMessageSchema, updateConversationSchema, } from '../utils/communication/message.js';
@@ -187,10 +212,11 @@ function normalizeTimeUnit(value) {
187
212
  }
188
213
  /**
189
214
  * Get all tool definitions for the MCP server
215
+ * Filters out tools the eBay seller account doesn't support (see EXCLUDED_TOOLS)
190
216
  */
191
217
  export function getToolDefinitions() {
192
218
  const chatConnectorTools = chatGptTools.filter((tool) => tool.name === 'search' || tool.name === 'fetch');
193
- return [
219
+ const allTools = [
194
220
  ...chatConnectorTools,
195
221
  ...tokenManagementTools,
196
222
  ...accountTools,
@@ -206,6 +232,8 @@ export function getToolDefinitions() {
206
232
  ...developerTools,
207
233
  ...tradingTools,
208
234
  ];
235
+ // Filter out tools the account doesn't support
236
+ return allTools.filter((tool) => !EXCLUDED_TOOLS.has(tool.name));
209
237
  }
210
238
  /**
211
239
  * Execute a tool based on its name
@@ -803,13 +831,19 @@ export async function executeTool(api, toolName, args) {
803
831
  return await api.inventory.deleteOffer(args.offerId);
804
832
  case 'ebay_publish_offer': {
805
833
  const offerId = args.offerId;
806
- // Pre-publish: ensure offer has categoryId + merchantLocationKey,
807
- // and inventory item has brand/mpn pair + location mapping.
834
+ // Pre-publish: ensure offer has categoryId + merchantLocationKey + listingPolicies,
835
+ // and inventory item has brand/mpn pair + location mapping + itemSpecifics.
808
836
  try {
809
837
  const offer = await api.inventory.getOffer(offerId);
810
838
  const offerData = offer;
811
839
  const sku = offerData?.sku;
812
840
  if (sku) {
841
+ // Default policy IDs (Hankuk Expo)
842
+ const defaultPolicies = {
843
+ paymentPolicyId: '259198675013',
844
+ returnPolicyId: '259198703013',
845
+ fulfillmentPolicyId: '259198453013',
846
+ };
813
847
  // Fix offer-level fields first
814
848
  let needsOfferUpdate = false;
815
849
  const offerUpdate = {
@@ -829,10 +863,30 @@ export async function executeTool(api, toolName, args) {
829
863
  offerUpdate.merchantLocationKey = 'seoul-warehouse';
830
864
  needsOfferUpdate = true;
831
865
  }
866
+ // Inject listingPolicies if missing
867
+ const existingPolicies = offerData.listingPolicies;
868
+ if (!existingPolicies?.paymentPolicyId ||
869
+ !existingPolicies.returnPolicyId ||
870
+ !existingPolicies.fulfillmentPolicyId) {
871
+ offerUpdate.listingPolicies = {
872
+ ...defaultPolicies,
873
+ ...(existingPolicies || {}),
874
+ };
875
+ needsOfferUpdate = true;
876
+ }
877
+ // Ensure pricingSummary has currency
878
+ const pricingSummary = offerData.pricingSummary;
879
+ if (pricingSummary?.price && typeof pricingSummary.price === 'object') {
880
+ const priceObj = pricingSummary.price;
881
+ if (!priceObj.currency) {
882
+ priceObj.currency = 'USD';
883
+ needsOfferUpdate = true;
884
+ }
885
+ }
832
886
  if (needsOfferUpdate) {
833
887
  await api.inventory.updateOffer(offerId, offerUpdate);
834
888
  }
835
- // Fix inventory item fields (brand/mpn pair)
889
+ // Fix inventory item fields (brand/mpn pair + itemSpecifics)
836
890
  try {
837
891
  const items = await api.inventory.getInventoryItem(sku);
838
892
  const itemData = Array.isArray(items)
@@ -868,6 +922,83 @@ export async function executeTool(api, toolName, args) {
868
922
  catch (e) {
869
923
  console.warn(`publish_offer pre-transform warning for ${offerId}: ${e}`);
870
924
  }
925
+ // Use Trading API to publish with proper XML transform (Currency, Country, itemSpecifics)
926
+ try {
927
+ const offer = await api.inventory.getOffer(offerId);
928
+ const offerData = offer;
929
+ const sku = offerData?.sku;
930
+ if (sku) {
931
+ const items = await api.inventory.getInventoryItem(sku);
932
+ const itemData = Array.isArray(items) ? items?.[0] : items;
933
+ const inventoryItem = itemData;
934
+ const product = inventoryItem?.product || {};
935
+ // Get location for Country
936
+ const locationKey = offerData.merchantLocationKey || 'seoul-warehouse';
937
+ let country = 'KR';
938
+ try {
939
+ const location = await api.inventory.getInventoryLocation(locationKey);
940
+ const locData = location;
941
+ const address = locData?.address;
942
+ if (address?.country) {
943
+ country = address.country;
944
+ }
945
+ }
946
+ catch {
947
+ // Default to KR
948
+ }
949
+ // Build Trading API item object
950
+ const pricingSummary = offerData.pricingSummary;
951
+ const priceObj = pricingSummary?.price;
952
+ const tradingItem = {
953
+ SKU: sku,
954
+ Title: product.title || offerData.title || sku,
955
+ Description: product.description || offerData.listingDescription || '',
956
+ PrimaryCategory: { CategoryID: offerData.categoryId || '176984' },
957
+ StartPrice: priceObj?.value || '0',
958
+ Currency: priceObj?.currency || 'USD',
959
+ Quantity: offerData.availableQuantity || 1,
960
+ ListingDuration: 'GTC',
961
+ ListingType: 'FixedPriceItem',
962
+ ConditionID: offerData.condition === 'NEW' ? 1000 : 3000,
963
+ Country: country,
964
+ PaymentMethods: ['PayPal'],
965
+ DispatchTimeMax: 3,
966
+ ShippingServiceOptions: {
967
+ ShippingService: 'USPSGround',
968
+ ShippingServiceCost: { Value: '0', CurrencyId: 'USD' },
969
+ },
970
+ ReturnPolicy: {
971
+ ReturnsAcceptedOption: 'ReturnsAccepted',
972
+ ReturnsWithinOption: 'Days_30',
973
+ Description: 'Returns accepted within 30 days.',
974
+ RefundOption: 'MoneyBack',
975
+ },
976
+ };
977
+ // Add itemSpecifics from inventory item product aspects
978
+ const aspects = product.aspects;
979
+ if (aspects && Object.keys(aspects).length > 0) {
980
+ tradingItem.ItemSpecifics = {
981
+ NameValueList: Object.entries(aspects).map(([name, value]) => ({
982
+ Name: name,
983
+ Value: Array.isArray(value) ? value : [value],
984
+ })),
985
+ };
986
+ }
987
+ // Add pictures
988
+ const imageUrls = product.imageUrls;
989
+ if (imageUrls && imageUrls.length > 0) {
990
+ tradingItem.PictureDetails = {
991
+ PictureURL: imageUrls.length === 1 ? imageUrls[0] : imageUrls,
992
+ };
993
+ }
994
+ // Publish via Trading API with transform
995
+ return await api.trading.createListing(tradingItem);
996
+ }
997
+ }
998
+ catch (e) {
999
+ console.warn(`publish_offer Trading API warning for ${offerId}: ${e}`);
1000
+ // Fallback to Inventory API if Trading API fails
1001
+ }
871
1002
  return await api.inventory.publishOffer(offerId);
872
1003
  }
873
1004
  case 'ebay_withdraw_offer':
@@ -47,7 +47,10 @@ export async function getApiStatusFeed(options = {}) {
47
47
  const response = await axios.get(RSS_URL, {
48
48
  timeout: 15_000,
49
49
  responseType: 'text',
50
- headers: { Accept: 'application/rss+xml, application/xml, text/xml' },
50
+ headers: {
51
+ Accept: 'application/rss+xml, application/xml, text/xml',
52
+ 'User-Agent': 'ebay-mcp-remote-edition/1.0 (GitHub Actions CI; +https://github.com/mrnajiboy/ebay-mcp-remote-edition)',
53
+ },
51
54
  });
52
55
  const xml = response.data;
53
56
  const parser = new XMLParser({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ebay-mcp-remote-edition",
3
- "version": "4.4.0",
3
+ "version": "4.5.1",
4
4
  "description": "Remote + 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",
@@ -8,29 +8,6 @@
8
8
  "bin": {
9
9
  "ebay-mcp": "build/index.js"
10
10
  },
11
- "scripts": {
12
- "build": "tsc && tsc-alias",
13
- "check-for-updates": "npx -y npm-check-updates",
14
- "update": "npx -y npm-check-updates -u && npm install",
15
- "playwright:install": "npx -y playwright@1.59.1 install chromium",
16
- "start": "node build/scripts/env-check.js node build/index.js",
17
- "start:http": "node build/scripts/env-check.js node build/server-http.js",
18
- "dev": "tsx src/scripts/env-check.ts tsx src/index.ts",
19
- "dev:http": "tsx src/scripts/env-check.ts tsx src/server-http.ts",
20
- "research:bootstrap": "node build/scripts/env-check.js node build/scripts/bootstrap-ebay-research-session.js",
21
- "research:inspect-session": "node build/scripts/env-check.js node build/scripts/inspect-ebay-research-session.js",
22
- "research:check-browser": "node build/scripts/env-check.js node build/scripts/check-playwright.js",
23
- "test": "vitest run",
24
- "test:coverage": "vitest run --coverage",
25
- "setup": "tsx src/scripts/env-check.ts tsx src/scripts/setup.ts",
26
- "sync": "tsx src/scripts/dev-sync.ts",
27
- "diagnose": "tsx src/scripts/env-check.ts tsx src/scripts/diagnostics.ts",
28
- "typecheck": "tsc --noEmit",
29
- "check": "tsc --noEmit && eslint . && prettier --check \"src/**/*.{ts,js,json,md}\"",
30
- "fix": "eslint . --fix && prettier --write \"src/**/*.{ts,js,json,md}\"",
31
- "env:encrypt": "npx -y @dotenvx/dotenvx encrypt",
32
- "env:decrypt": "npx -y @dotenvx/dotenvx decrypt"
33
- },
34
11
  "keywords": [
35
12
  "mcp",
36
13
  "ebay",
@@ -111,17 +88,30 @@
111
88
  "typescript-eslint": "^8.59.2",
112
89
  "vitest": "^4.1.5"
113
90
  },
114
- "packageManager": "pnpm@10.33.2",
115
91
  "engines": {
116
92
  "node": ">=18.0.0"
117
93
  },
118
- "pnpm": {
119
- "onlyBuiltDependencies": [
120
- "esbuild"
121
- ],
122
- "overrides": {
123
- "hono@<4.12.7": ">=4.12.7",
124
- "flatted@<=3.4.1": ">=3.4.2"
125
- }
94
+ "scripts": {
95
+ "build": "tsc && tsc-alias",
96
+ "check-for-updates": "npx -y npm-check-updates",
97
+ "update": "npx -y npm-check-updates -u && npm install",
98
+ "playwright:install": "npx -y playwright@1.59.1 install chromium",
99
+ "start": "node build/scripts/env-check.js node build/index.js",
100
+ "start:http": "node build/scripts/env-check.js node build/server-http.js",
101
+ "dev": "tsx src/scripts/env-check.ts tsx src/index.ts",
102
+ "dev:http": "tsx src/scripts/env-check.ts tsx src/server-http.ts",
103
+ "research:bootstrap": "node build/scripts/env-check.js node build/scripts/bootstrap-ebay-research-session.js",
104
+ "research:inspect-session": "node build/scripts/env-check.js node build/scripts/inspect-ebay-research-session.js",
105
+ "research:check-browser": "node build/scripts/env-check.js node build/scripts/check-playwright.js",
106
+ "test": "vitest run",
107
+ "test:coverage": "vitest run --coverage",
108
+ "setup": "tsx src/scripts/env-check.ts tsx src/scripts/setup.ts",
109
+ "sync": "tsx src/scripts/dev-sync.ts",
110
+ "diagnose": "tsx src/scripts/env-check.ts tsx src/scripts/diagnostics.ts",
111
+ "typecheck": "tsc --noEmit",
112
+ "check": "tsc --noEmit && eslint . && prettier --check \"src/**/*.{ts,js,json,md}\"",
113
+ "fix": "eslint . --fix && prettier --write \"src/**/*.{ts,js,json,md}\"",
114
+ "env:encrypt": "npx -y @dotenvx/dotenvx encrypt",
115
+ "env:decrypt": "npx -y @dotenvx/dotenvx decrypt"
126
116
  }
127
- }
117
+ }