ebay-mcp 1.6.2 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/build/api/client.js +26 -13
- package/build/api/index.js +2 -2
- package/build/api/listing-management/inventory.js +7 -35
- package/build/api/other/identity.js +1 -1
- package/build/auth/oauth.js +4 -4
- package/build/config/environment.js +54 -36
- package/build/index.js +14 -0
- package/build/scripts/auto-setup.js +6 -0
- package/build/scripts/diagnostics.js +2 -0
- package/build/scripts/interactive-setup.js +6 -0
- package/build/scripts/setup.js +254 -25
- package/build/scripts/update-api-status-doc.js +44 -0
- package/build/server-http.js +32 -13
- package/build/tools/definitions/developer.js +43 -0
- package/build/tools/definitions/token-management.js +1 -1
- package/build/tools/index.js +46 -5
- package/build/utils/api-status-feed.js +85 -0
- package/build/utils/version.js +56 -0
- package/package.json +5 -1
- package/public/icons/1024x1024.png +0 -0
- package/public/icons/128x128.png +0 -0
- package/public/icons/16x16.png +0 -0
- package/public/icons/256x256.png +0 -0
- package/public/icons/32x32.png +0 -0
- package/public/icons/48x48.png +0 -0
- package/public/icons/512x512.png +0 -0
|
@@ -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.',
|
|
@@ -126,7 +126,7 @@ export const tokenManagementTools = [
|
|
|
126
126
|
'IMPORTANT NOTES:\n' +
|
|
127
127
|
'- Authorization codes expire in ~5 minutes - if you get "invalid grant" error, get a fresh code\n' +
|
|
128
128
|
'- Codes can be URL-encoded (e.g., v%5E1.1%23...) - this tool automatically decodes them\n' +
|
|
129
|
-
'- Extract the code parameter from the redirect URL: https://
|
|
129
|
+
'- Extract the code parameter from the redirect URL (your RuName Accept URL): https://your-redirect-uri?code=YOUR_CODE&expires_in=299\n' +
|
|
130
130
|
'- Tokens are saved to .env file and will auto-refresh every 2 hours\n' +
|
|
131
131
|
'- Refresh tokens last 18 months before requiring re-authorization\n\n' +
|
|
132
132
|
'COMMON ERRORS:\n' +
|
package/build/tools/index.js
CHANGED
|
@@ -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
|
|
37
|
-
const
|
|
38
|
-
|
|
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.
|
|
3
|
+
"version": "1.7.2",
|
|
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
|