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.
- package/README.de.md +3 -3
- package/README.es.md +3 -3
- package/README.fr.md +3 -3
- package/README.ja.md +3 -3
- package/README.ko.md +3 -3
- package/README.md +163 -41
- package/README.pt-BR.md +3 -3
- package/README.ru.md +3 -3
- package/README.zh-CN.md +3 -3
- package/build/api/account-management/account.js +633 -297
- package/build/api/analytics-and-report/analytics.js +111 -65
- package/build/api/client.js +199 -140
- package/build/api/clientRequestError.js +23 -0
- package/build/api/clientTrading.js +213 -0
- package/build/api/clientTradingError.js +4 -0
- package/build/api/communication/feedback.js +147 -59
- package/build/api/communication/message.js +134 -81
- package/build/api/communication/negotiation.js +65 -53
- package/build/api/communication/notification.js +458 -164
- package/build/api/developer/developer.js +99 -45
- package/build/api/index.js +23 -8
- package/build/api/listing-management/inventory.js +884 -338
- package/build/api/listing-metadata/metadata.js +416 -170
- package/build/api/listing-metadata/taxonomy.js +187 -42
- package/build/api/marketing-and-promotions/marketing.js +1392 -598
- package/build/api/marketing-and-promotions/recommendation.js +49 -24
- package/build/api/order-management/dispute.js +226 -45
- package/build/api/order-management/fulfillment.js +144 -71
- package/build/api/order-management/orderIssueHelpers.js +61 -0
- package/build/api/other/compliance.js +50 -38
- package/build/api/other/edelivery.js +603 -159
- package/build/api/other/finding.js +280 -0
- package/build/api/other/identity.js +30 -15
- package/build/api/other/translation.js +29 -14
- package/build/api/other/vero.js +113 -27
- package/build/api/rateLimitTracker.js +55 -0
- package/build/api/shared/request.js +189 -98
- package/build/api/trading/trading.js +154 -87
- package/build/auth/credentialSession.js +250 -0
- package/build/auth/oauth.js +298 -171
- package/build/auth/{oauth-metadata.js → oauthMetadata.js} +22 -6
- package/build/auth/oauthMiddleware.js +104 -0
- package/build/auth/{scope-utils.js → scopeUtils.js} +101 -28
- package/build/auth/tokenVerifier.js +243 -0
- package/build/auth/tokenVerifierError.js +21 -0
- package/build/config/environment.js +148 -113
- package/build/config/{tool-families.js → toolFamilies.js} +30 -14
- package/build/index.js +25 -19
- package/build/mcp/{http-transport.js → httpTransport.js} +102 -52
- package/build/mcp/readOnlyFilter.js +67 -0
- package/build/mcp/runtime.js +31 -13
- package/build/mcp/{tool-gating.js → toolGating.js} +77 -49
- package/build/mcp/{ui-bridge.js → uiBridge.js} +80 -23
- package/build/schemas/account-management/account.js +144 -6
- package/build/schemas/analytics/analytics.js +33 -19
- package/build/schemas/communication/messages.js +12 -42
- package/build/schemas/developer/developer.js +135 -0
- package/build/schemas/fulfillment/orders.js +183 -21
- package/build/schemas/index.js +27 -19
- package/build/schemas/inventory-management/inventory.js +17 -9
- package/build/schemas/marketing/marketing.js +480 -25
- package/build/schemas/metadata/metadata.js +11 -6
- package/build/schemas/other/finding.js +27 -0
- package/build/schemas/other/otherApis.js +424 -0
- package/build/schemas/taxonomy/taxonomy.js +11 -5
- package/build/scripts/{auto-setup.js → autoSetup.js} +87 -74
- package/build/scripts/{build-ui.js → buildUi.js} +6 -1
- package/build/scripts/{dev-sync.js → devSync.js} +169 -110
- package/build/scripts/diagnostics.js +52 -44
- package/build/scripts/{download-specs.js → downloadSpecs.js} +29 -21
- package/build/scripts/{interactive-setup.js → interactiveSetup.js} +84 -78
- package/build/scripts/setup.js +241 -178
- package/build/scripts/{setup-shared.js → setupShared.js} +29 -7
- package/build/scripts/skills.js +35 -27
- package/build/scripts/updateApiStatusDoc.js +71 -0
- package/build/serverHttp.js +90 -0
- package/build/skills/content.js +124 -118
- package/build/skills/index.js +22 -10
- package/build/skills/install.js +21 -10
- package/build/skills/markers.js +50 -15
- package/build/skills/{registry-snapshot.js → registrySnapshot.js} +8 -2
- package/build/skills/render.js +41 -14
- package/build/skills/targets.js +49 -17
- package/build/tools/categories/account.js +166 -264
- package/build/tools/categories/analytics.js +11 -22
- package/build/tools/categories/browse.js +17 -0
- package/build/tools/categories/communication.js +124 -399
- package/build/tools/categories/connector.js +23 -20
- package/build/tools/categories/developer.js +46 -159
- package/build/tools/categories/fulfillment.js +84 -215
- package/build/tools/categories/index.js +6 -14
- package/build/tools/categories/inventory.js +238 -293
- package/build/tools/categories/marketing.js +677 -39
- package/build/tools/categories/metadata.js +190 -117
- package/build/tools/categories/other.js +81 -208
- package/build/tools/categories/taxonomy.js +29 -20
- package/build/tools/categories/{token-management.js → tokenManagement.js} +187 -192
- package/build/tools/categories/trading.js +16 -47
- package/build/tools/contracts.js +34 -24
- package/build/tools/{define-tool.js → defineTool.js} +44 -29
- package/build/tools/registry.js +87 -29
- package/build/tools/schemas.js +3 -57
- package/build/tools/ui/archetypes.js +1 -0
- package/build/tools/ui/{map-helpers.js → mapHelpers.js} +69 -19
- package/build/tools/ui/maps.js +294 -80
- package/build/types/index.js +1 -1
- package/build/ui/card.html +14 -14
- package/build/ui/chart.html +14 -14
- package/build/ui/stat.html +87 -0
- package/build/ui/table.html +14 -14
- package/build/utils/apiStatusFeed.js +95 -0
- package/build/utils/cliOutput.js +15 -0
- package/build/utils/cliUi.js +108 -0
- package/build/utils/communication/feedback.js +57 -77
- package/build/utils/communication/message.js +69 -98
- package/build/utils/communication/negotiation.js +30 -57
- package/build/utils/communication/notification.js +112 -252
- package/build/utils/{date-converter.js → dateConverter.js} +62 -30
- package/build/utils/effectSchema.js +203 -0
- package/build/utils/effectSchemaAdapter.js +121 -0
- package/build/utils/effectSchemaChainMethods.js +146 -0
- package/build/utils/effectSchemaRefinements.js +104 -0
- package/build/utils/effectSchemaStringMethods.js +63 -0
- package/build/utils/effectSchemaTypes.js +47 -0
- package/build/utils/{env-parser.js → envParser.js} +10 -2
- package/build/utils/errors.js +7 -3
- package/build/utils/http.js +148 -89
- package/build/utils/{llm-client-detector.js → llmClientDetector.js} +295 -274
- package/build/utils/logger.js +127 -51
- package/build/utils/{oauth-helper.js → oauthHelper.js} +127 -90
- package/build/utils/other/compliance.js +18 -38
- package/build/utils/other/identity.js +2 -2
- package/build/utils/other/translation.js +4 -4
- package/build/utils/other/vero.js +36 -20
- package/build/utils/schemaHelpers.js +22 -0
- package/build/utils/scopeHelper.js +270 -0
- package/build/utils/securityChecker.js +316 -0
- package/build/utils/{setup-validator.js → setupValidator.js} +113 -94
- package/build/utils/{setup-wizard.js → setupWizard.js} +57 -46
- package/build/utils/{token-utils.js → tokenUtils.js} +2 -2
- package/build/utils/trading/trading.js +35 -0
- package/build/utils/typeGuards.js +12 -0
- package/build/utils/version.js +65 -29
- package/package.json +10 -8
- package/build/api/client-trading.js +0 -124
- package/build/api/communication/shared.js +0 -59
- package/build/api/shared/query-params.js +0 -16
- package/build/auth/credential-session.js +0 -166
- package/build/auth/oauth-middleware.js +0 -100
- package/build/auth/token-verifier.js +0 -186
- package/build/schemas/other/other-apis.js +0 -390
- package/build/scripts/update-api-status-doc.js +0 -44
- package/build/server-http.js +0 -85
- package/build/tools/definitions/marketing.js +0 -1381
- package/build/tools/tool-handlers/marketing.js +0 -312
- package/build/utils/account-management/account.js +0 -818
- package/build/utils/api-status-feed.js +0 -84
- package/build/utils/cli-ui.js +0 -47
- package/build/utils/order-management/dispute.js +0 -369
- package/build/utils/order-management/fulfillment.js +0 -205
- package/build/utils/other/edelivery.js +0 -235
- package/build/utils/schema-helpers.js +0 -13
- package/build/utils/scope-helper.js +0 -207
- package/build/utils/security-checker.js +0 -248
- package/build/utils/type-guards.js +0 -6
- /package/build/auth/{oauth-types.js → oauthTypes.js} +0 -0
- /package/build/{server-http.d.ts → serverHttp.d.ts} +0 -0
- /package/build/tools/ui/{view-models.js → viewModels.js} +0 -0
- /package/build/types/{ebay-enums.js → ebayEnums.js} +0 -0
|
@@ -1,13 +1,86 @@
|
|
|
1
|
-
import { z } from '
|
|
2
|
-
import { buildCredentialDisplay, maskToken } from '../../auth/
|
|
1
|
+
import { z } from '../../utils/effectSchema.js';
|
|
2
|
+
import { buildCredentialDisplay, maskToken } from '../../auth/credentialSession.js';
|
|
3
3
|
import { getOAuthAuthorizationUrl, validateScopes } from '../../config/environment.js';
|
|
4
|
-
import { defineTool } from '../../tools/
|
|
5
|
-
import { convertToTimestamp, validateTokenExpiry } from '../../utils/
|
|
4
|
+
import { defineTool } from '../../tools/defineTool.js';
|
|
5
|
+
import { convertToTimestamp, validateTokenExpiry } from '../../utils/dateConverter.js';
|
|
6
6
|
import { getErrorMessage } from '../../utils/errors.js';
|
|
7
|
+
import { Data, Effect, Either } from 'effect';
|
|
8
|
+
/** Tagged failure raised inside token-management tool handlers. */
|
|
9
|
+
class TokenManagementToolError extends Data.TaggedError('TokenManagementToolError') {
|
|
10
|
+
}
|
|
11
|
+
const tokenToolError = (operation, message, error) => new TokenManagementToolError({
|
|
12
|
+
operation,
|
|
13
|
+
message: error === undefined ? message : `${message}: ${getErrorMessage(error, String(error))}`,
|
|
14
|
+
...(error === undefined ? {} : { cause: error }),
|
|
15
|
+
});
|
|
16
|
+
/** Convert an optional token expiry value into a timestamp inside the token tool Effect. */
|
|
17
|
+
const optionalTokenExpiryTimestamp = (value, operation, message) => {
|
|
18
|
+
if (value === undefined) {
|
|
19
|
+
return Effect.succeed(undefined);
|
|
20
|
+
}
|
|
21
|
+
return convertToTimestamp(value).pipe(Effect.mapError((error) => tokenToolError(operation, message, error)));
|
|
22
|
+
};
|
|
23
|
+
const missingClientIdError = tokenToolError('getOAuthUrl', 'EBAY_CLIENT_ID environment variable is required to generate OAuth URL');
|
|
24
|
+
const missingRedirectUriError = tokenToolError('getOAuthUrl', 'Redirect URI is required. Either provide it as a parameter or set EBAY_REDIRECT_URI in your .env file.');
|
|
25
|
+
const missingUserTokensError = tokenToolError('refreshAccessToken', 'No user tokens available. Please set user tokens first using ebay_set_user_tokens_with_expiry or add EBAY_USER_REFRESH_TOKEN to your .env file.');
|
|
26
|
+
const oauthUrlInputSchema = z.object({
|
|
27
|
+
redirectUri: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Optional redirect URI registered with your eBay application (RuName). If not provided, will use EBAY_REDIRECT_URI from .env file.'),
|
|
31
|
+
scopes: z
|
|
32
|
+
.array(z.string())
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Optional array of OAuth scopes. If not provided, uses environment-specific default scopes (production or sandbox based on EBAY_ENVIRONMENT). Custom scopes will be validated against the environment.'),
|
|
35
|
+
state: z.string().optional().describe('Optional state parameter for CSRF protection'),
|
|
36
|
+
});
|
|
37
|
+
const setUserTokensInputSchema = z.object({
|
|
38
|
+
accessToken: z.string().min(1).describe('The user access token obtained from OAuth flow'),
|
|
39
|
+
refreshToken: z.string().min(1).describe('The refresh token obtained from OAuth flow'),
|
|
40
|
+
});
|
|
41
|
+
const setUserTokensWithExpiryInputSchema = z.object({
|
|
42
|
+
accessToken: z.string().min(1).describe('eBay user access token'),
|
|
43
|
+
refreshToken: z.string().min(1).describe('eBay user refresh token'),
|
|
44
|
+
accessTokenExpiry: z
|
|
45
|
+
.union([z.string(), z.number()])
|
|
46
|
+
.optional()
|
|
47
|
+
.describe('Optional access-token expiry. Supports ISO date strings, Unix timestamps, and relative time.'),
|
|
48
|
+
refreshTokenExpiry: z
|
|
49
|
+
.union([z.string(), z.number()])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Optional refresh-token expiry. Supports ISO date strings, Unix timestamps, and relative time.'),
|
|
52
|
+
autoRefresh: z
|
|
53
|
+
.boolean()
|
|
54
|
+
.optional()
|
|
55
|
+
.default(true)
|
|
56
|
+
.describe('Whether to validate and refresh the access token immediately'),
|
|
57
|
+
});
|
|
58
|
+
const getTokenStatusInputSchema = z.object({});
|
|
59
|
+
const clearTokensInputSchema = z.object({});
|
|
60
|
+
const validateTokenExpiryInputSchema = z.object({
|
|
61
|
+
accessTokenExpiry: z
|
|
62
|
+
.union([z.string(), z.number()])
|
|
63
|
+
.describe('Access token expiry as an ISO date string, Unix timestamp, or relative time'),
|
|
64
|
+
refreshTokenExpiry: z
|
|
65
|
+
.union([z.string(), z.number()])
|
|
66
|
+
.describe('Refresh token expiry as an ISO date string, Unix timestamp, or relative time'),
|
|
67
|
+
});
|
|
68
|
+
const convertDateToTimestampInputSchema = z.object({
|
|
69
|
+
dateInput: z
|
|
70
|
+
.union([z.string(), z.number()])
|
|
71
|
+
.describe('Date to convert as an ISO date string, Unix timestamp, or relative time'),
|
|
72
|
+
});
|
|
73
|
+
const displayCredentialsInputSchema = z.object({});
|
|
74
|
+
const refreshAccessTokenInputSchema = z.object({});
|
|
75
|
+
const exchangeAuthorizationCodeInputSchema = z.object({
|
|
76
|
+
code: z
|
|
77
|
+
.string()
|
|
78
|
+
.min(1)
|
|
79
|
+
.describe('The authorization code received from eBay after user authorization'),
|
|
80
|
+
});
|
|
7
81
|
/**
|
|
8
82
|
* OAuth token management tools for consent URLs, token storage, refresh, status,
|
|
9
|
-
* and date/expiry helpers.
|
|
10
|
-
* (e.g. `EBAY_CLIENT_ID`, `EBAY_REDIRECT_URI`) rather than the eBay API client.
|
|
83
|
+
* and date/expiry helpers. Handlers receive validated runtime config from the API facade.
|
|
11
84
|
*/
|
|
12
85
|
export const tokenManagementEntries = [
|
|
13
86
|
defineTool({
|
|
@@ -33,111 +106,59 @@ export const tokenManagementEntries = [
|
|
|
33
106
|
'- "Insufficient permissions" errors mean you need to re-authorize with additional scopes\n' +
|
|
34
107
|
'- OAuth URL format: Use + to separate scopes (e.g., scope=scope1+scope2), not %2B\n' +
|
|
35
108
|
'- Refresh tokens last 18 months and are saved to .env file for persistence',
|
|
36
|
-
inputSchema:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.array(z.string())
|
|
43
|
-
.optional()
|
|
44
|
-
.describe('Optional array of OAuth scopes. If not provided, uses environment-specific default scopes (production or sandbox based on EBAY_ENVIRONMENT). Custom scopes will be validated against the environment.'),
|
|
45
|
-
state: z.string().optional().describe('Optional state parameter for CSRF protection'),
|
|
46
|
-
},
|
|
47
|
-
handler: (_api, args) => {
|
|
48
|
-
const clientId = process.env.EBAY_CLIENT_ID ?? '';
|
|
49
|
-
const environment = (process.env.EBAY_ENVIRONMENT ?? 'sandbox');
|
|
50
|
-
const envRedirectUri = process.env.EBAY_REDIRECT_URI;
|
|
51
|
-
const redirectUri = args.redirectUri ?? envRedirectUri;
|
|
52
|
-
const scopes = args.scopes;
|
|
53
|
-
const state = args.state;
|
|
54
|
-
if (!clientId) {
|
|
55
|
-
throw new Error('EBAY_CLIENT_ID environment variable is required to generate OAuth URL');
|
|
109
|
+
inputSchema: oauthUrlInputSchema.shape,
|
|
110
|
+
handler: (api, args) => Effect.runPromise(Effect.gen(function* () {
|
|
111
|
+
const config = api.getConfig();
|
|
112
|
+
const redirectUri = args.redirectUri ?? config.redirectUri;
|
|
113
|
+
if (!config.clientId) {
|
|
114
|
+
return yield* Effect.fail(missingClientIdError);
|
|
56
115
|
}
|
|
57
116
|
if (!redirectUri) {
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
let scopeWarnings = [];
|
|
61
|
-
let validatedScopes = scopes;
|
|
62
|
-
if (scopes && scopes.length > 0) {
|
|
63
|
-
const validation = validateScopes(scopes, environment);
|
|
64
|
-
scopeWarnings = validation.warnings;
|
|
65
|
-
validatedScopes = validation.validScopes;
|
|
117
|
+
return yield* Effect.fail(missingRedirectUriError);
|
|
66
118
|
}
|
|
67
|
-
const
|
|
119
|
+
const scopes = args.scopes;
|
|
120
|
+
const scopeValidation = scopes && scopes.length > 0
|
|
121
|
+
? validateScopes(scopes, config.environment)
|
|
122
|
+
: { warnings: [], validScopes: scopes };
|
|
123
|
+
const authUrl = getOAuthAuthorizationUrl(config.clientId, redirectUri, config.environment, scopeValidation.validScopes, args.state);
|
|
68
124
|
const result = {
|
|
69
125
|
authorizationUrl: authUrl,
|
|
70
126
|
redirectUri,
|
|
71
127
|
instructions: 'Open this URL in a browser to authorize the application. After authorization, you will be redirected to your redirect URI with an authorization code that can be exchanged for an access token.',
|
|
72
|
-
environment,
|
|
128
|
+
environment: config.environment,
|
|
73
129
|
scopes: scopes ?? 'default (all Sell API scopes)',
|
|
74
130
|
};
|
|
75
|
-
if (
|
|
76
|
-
result.warnings =
|
|
131
|
+
if (scopeValidation.warnings.length > 0) {
|
|
132
|
+
result.warnings = scopeValidation.warnings;
|
|
77
133
|
}
|
|
78
134
|
return result;
|
|
79
|
-
},
|
|
135
|
+
})),
|
|
80
136
|
}),
|
|
81
137
|
defineTool({
|
|
82
138
|
name: 'ebay_set_user_tokens',
|
|
83
139
|
description: 'Set the user access token and refresh token for authenticated API requests. These tokens should be obtained through the OAuth authorization code flow. Tokens will be persisted to disk and automatically refreshed when needed. User tokens provide higher rate limits (10,000-50,000 requests/day) compared to client credentials (1,000 requests/day).',
|
|
84
|
-
inputSchema:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (!accessToken || !refreshToken) {
|
|
91
|
-
throw new Error('Both accessToken and refreshToken are required');
|
|
92
|
-
}
|
|
93
|
-
await api.setUserTokens(accessToken, refreshToken);
|
|
94
|
-
return {
|
|
95
|
-
success: true,
|
|
96
|
-
message: 'User tokens successfully stored. These tokens will be used for all subsequent API requests and will be automatically refreshed when needed.',
|
|
97
|
-
tokenInfo: api.getTokenInfo(),
|
|
98
|
-
};
|
|
99
|
-
},
|
|
140
|
+
inputSchema: setUserTokensInputSchema.shape,
|
|
141
|
+
handler: (api, args) => Effect.runPromise(api.setUserTokens(args.accessToken, args.refreshToken).pipe(Effect.mapError((error) => tokenToolError('setUserTokens', 'Failed to set user tokens', error)), Effect.map(() => ({
|
|
142
|
+
success: true,
|
|
143
|
+
message: 'User tokens successfully stored. These tokens will be used for all subsequent API requests and will be automatically refreshed when needed.',
|
|
144
|
+
tokenInfo: api.getTokenInfo(),
|
|
145
|
+
})))),
|
|
100
146
|
}),
|
|
101
147
|
defineTool({
|
|
102
148
|
name: 'ebay_set_user_tokens_with_expiry',
|
|
103
149
|
description: "Set user access and refresh tokens with custom expiry times. This is an enhanced version of ebay_set_user_tokens that accepts expiry times and can automatically refresh the access token if it's expired but the refresh token is valid. Useful when user provides tokens that may already be partially expired.",
|
|
104
|
-
inputSchema:
|
|
105
|
-
|
|
106
|
-
refreshToken: z.string().min(1).describe('eBay user refresh token'),
|
|
107
|
-
accessTokenExpiry: z
|
|
108
|
-
.union([z.string(), z.number()])
|
|
109
|
-
.optional()
|
|
110
|
-
.describe('Optional: Access token expiry time. If not provided, defaults to 2 hours from now. Can be ISO date string, Unix timestamp, or relative time (e.g., "in 7200 seconds")'),
|
|
111
|
-
refreshTokenExpiry: z
|
|
112
|
-
.union([z.string(), z.number()])
|
|
113
|
-
.optional()
|
|
114
|
-
.describe('Optional: Refresh token expiry time. If not provided, defaults to 18 months from now. Can be ISO date string, Unix timestamp, or relative time'),
|
|
115
|
-
autoRefresh: z
|
|
116
|
-
.boolean()
|
|
117
|
-
.optional()
|
|
118
|
-
.default(true)
|
|
119
|
-
.describe('If true and access token is expired but refresh token is valid, automatically refresh the access token. Default: true'),
|
|
120
|
-
},
|
|
121
|
-
handler: async (api, args) => {
|
|
150
|
+
inputSchema: setUserTokensWithExpiryInputSchema.shape,
|
|
151
|
+
handler: (api, args) => {
|
|
122
152
|
const { accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry, autoRefresh } = args;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (accessTokenExpiry !== undefined) {
|
|
130
|
-
accessExpiry = convertToTimestamp(accessTokenExpiry);
|
|
131
|
-
}
|
|
132
|
-
if (refreshTokenExpiry !== undefined) {
|
|
133
|
-
refreshExpiry = convertToTimestamp(refreshTokenExpiry);
|
|
134
|
-
}
|
|
135
|
-
await api.setUserTokens(accessToken, refreshToken, accessExpiry, refreshExpiry);
|
|
136
|
-
// The OAuth client refreshes internally if the access token is expired.
|
|
153
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
154
|
+
const accessExpiry = yield* optionalTokenExpiryTimestamp(accessTokenExpiry, 'setUserTokens', 'Failed to set user tokens');
|
|
155
|
+
const refreshExpiry = yield* optionalTokenExpiryTimestamp(refreshTokenExpiry, 'setUserTokens', 'Failed to set user tokens');
|
|
156
|
+
yield* api
|
|
157
|
+
.setUserTokens(accessToken, refreshToken, accessExpiry, refreshExpiry)
|
|
158
|
+
.pipe(Effect.mapError((error) => tokenToolError('setUserTokens', 'Failed to set user tokens', error)));
|
|
137
159
|
if (autoRefresh) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
await authClient.getAccessToken();
|
|
160
|
+
const refreshResult = yield* Effect.either(api.getAuthClient().getOAuthClient().getAccessToken());
|
|
161
|
+
if (Either.isRight(refreshResult)) {
|
|
141
162
|
return {
|
|
142
163
|
success: true,
|
|
143
164
|
message: 'User tokens stored successfully in memory. Access token validated and refreshed if needed. To persist tokens, update EBAY_USER_REFRESH_TOKEN in .env file.',
|
|
@@ -145,15 +166,13 @@ export const tokenManagementEntries = [
|
|
|
145
166
|
refreshed: true,
|
|
146
167
|
};
|
|
147
168
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
};
|
|
156
|
-
}
|
|
169
|
+
return {
|
|
170
|
+
success: true,
|
|
171
|
+
message: 'User tokens stored, but failed to validate/refresh access token. You may need to re-authorize.',
|
|
172
|
+
tokenInfo: api.getTokenInfo(),
|
|
173
|
+
refreshed: false,
|
|
174
|
+
refreshError: getErrorMessage(refreshResult.left),
|
|
175
|
+
};
|
|
157
176
|
}
|
|
158
177
|
return {
|
|
159
178
|
success: true,
|
|
@@ -161,28 +180,28 @@ export const tokenManagementEntries = [
|
|
|
161
180
|
tokenInfo: api.getTokenInfo(),
|
|
162
181
|
refreshed: false,
|
|
163
182
|
};
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
throw new Error(`Failed to set user tokens: ${getErrorMessage(error, String(error))}`);
|
|
167
|
-
}
|
|
183
|
+
}));
|
|
168
184
|
},
|
|
169
185
|
}),
|
|
170
186
|
defineTool({
|
|
171
187
|
name: 'ebay_get_token_status',
|
|
172
188
|
description: 'Check the current OAuth token status. Returns information about whether user tokens or client credentials are being used, and whether tokens are valid.',
|
|
173
|
-
inputSchema:
|
|
189
|
+
inputSchema: getTokenStatusInputSchema.shape,
|
|
174
190
|
handler: (api) => {
|
|
175
191
|
const tokenInfo = api.getTokenInfo();
|
|
176
192
|
const hasUserTokens = api.hasUserTokens();
|
|
193
|
+
let currentTokenType = 'none';
|
|
194
|
+
if (tokenInfo.hasUserToken) {
|
|
195
|
+
currentTokenType = 'user_token (10,000-50,000 req/day)';
|
|
196
|
+
}
|
|
197
|
+
else if (tokenInfo.hasAppAccessToken) {
|
|
198
|
+
currentTokenType = 'app_access_token (1,000 req/day)';
|
|
199
|
+
}
|
|
177
200
|
return {
|
|
178
201
|
hasUserToken: tokenInfo.hasUserToken,
|
|
179
202
|
hasAppAccessToken: tokenInfo.hasAppAccessToken,
|
|
180
203
|
authenticated: api.isAuthenticated(),
|
|
181
|
-
currentTokenType
|
|
182
|
-
? 'user_token (10,000-50,000 req/day)'
|
|
183
|
-
: tokenInfo.hasAppAccessToken
|
|
184
|
-
? 'app_access_token (1,000 req/day)'
|
|
185
|
-
: 'none',
|
|
204
|
+
currentTokenType,
|
|
186
205
|
message: hasUserTokens
|
|
187
206
|
? 'Using user access token with automatic refresh'
|
|
188
207
|
: 'Using app access token from client credentials flow (lower rate limits). Consider setting user tokens for higher rate limits.',
|
|
@@ -192,7 +211,7 @@ export const tokenManagementEntries = [
|
|
|
192
211
|
defineTool({
|
|
193
212
|
name: 'ebay_clear_tokens',
|
|
194
213
|
description: 'Clear all stored OAuth tokens (both user tokens and client credentials). This will require re-authentication for subsequent API calls.',
|
|
195
|
-
inputSchema:
|
|
214
|
+
inputSchema: clearTokensInputSchema.shape,
|
|
196
215
|
handler: (api) => {
|
|
197
216
|
const authClient = api.getAuthClient().getOAuthClient();
|
|
198
217
|
authClient.clearAllTokens();
|
|
@@ -205,44 +224,28 @@ export const tokenManagementEntries = [
|
|
|
205
224
|
defineTool({
|
|
206
225
|
name: 'ebay_validate_token_expiry',
|
|
207
226
|
description: 'Validate token expiry times and get recommendations. Checks if access/refresh tokens are expired or expiring soon, and provides actionable recommendations (e.g., refresh access token, re-authorize user).',
|
|
208
|
-
inputSchema:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
...validation,
|
|
223
|
-
accessTokenExpiryTimestamp: accessExpiry,
|
|
224
|
-
refreshTokenExpiryTimestamp: refreshExpiry,
|
|
225
|
-
accessTokenExpiryDate: new Date(accessExpiry).toISOString(),
|
|
226
|
-
refreshTokenExpiryDate: new Date(refreshExpiry).toISOString(),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
throw new Error(`Failed to validate token expiry: ${getErrorMessage(error, String(error))}`);
|
|
231
|
-
}
|
|
232
|
-
},
|
|
227
|
+
inputSchema: validateTokenExpiryInputSchema.shape,
|
|
228
|
+
handler: (_api, args) => Effect.runPromise(Effect.gen(function* () {
|
|
229
|
+
const accessExpiry = yield* convertToTimestamp(args.accessTokenExpiry).pipe(Effect.mapError((error) => tokenToolError('validateTokenExpiry', 'Failed to validate token expiry', error)));
|
|
230
|
+
const refreshExpiry = yield* convertToTimestamp(args.refreshTokenExpiry).pipe(Effect.mapError((error) => tokenToolError('validateTokenExpiry', 'Failed to validate token expiry', error)));
|
|
231
|
+
const validation = validateTokenExpiry(accessExpiry, refreshExpiry);
|
|
232
|
+
return {
|
|
233
|
+
...validation,
|
|
234
|
+
accessTokenExpiryTimestamp: accessExpiry,
|
|
235
|
+
refreshTokenExpiryTimestamp: refreshExpiry,
|
|
236
|
+
accessTokenExpiryDate: new Date(accessExpiry).toISOString(),
|
|
237
|
+
refreshTokenExpiryDate: new Date(refreshExpiry).toISOString(),
|
|
238
|
+
};
|
|
239
|
+
})),
|
|
233
240
|
}),
|
|
234
241
|
defineTool({
|
|
235
242
|
name: 'ebay_convert_date_to_timestamp',
|
|
236
243
|
description: 'Convert a date string or number to Unix timestamp (milliseconds). Supports ISO 8601 dates, Unix timestamps (seconds or milliseconds), and relative time (e.g., "in 2 hours", "in 7200 seconds"). Useful when setting token expiry times from user input.',
|
|
237
|
-
inputSchema:
|
|
238
|
-
dateInput: z
|
|
239
|
-
.union([z.string(), z.number()])
|
|
240
|
-
.describe('Date to convert. Supports ISO 8601 strings (e.g., "2025-01-15T10:30:00Z"), Unix timestamps (seconds or milliseconds), or relative time (e.g., "in 2 hours")'),
|
|
241
|
-
},
|
|
244
|
+
inputSchema: convertDateToTimestampInputSchema.shape,
|
|
242
245
|
handler: (_api, args) => {
|
|
243
246
|
const { dateInput } = args;
|
|
244
|
-
|
|
245
|
-
const timestamp = convertToTimestamp(dateInput);
|
|
247
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
248
|
+
const timestamp = yield* convertToTimestamp(dateInput).pipe(Effect.mapError((error) => tokenToolError('convertDateToTimestamp', 'Failed to convert date', error)));
|
|
246
249
|
return {
|
|
247
250
|
success: true,
|
|
248
251
|
timestamp,
|
|
@@ -250,16 +253,13 @@ export const tokenManagementEntries = [
|
|
|
250
253
|
formattedDate: new Date(timestamp).toISOString(),
|
|
251
254
|
message: `Successfully converted to timestamp: ${timestamp}ms (${new Date(timestamp).toISOString()})`,
|
|
252
255
|
};
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
throw new Error(`Failed to convert date: ${getErrorMessage(error, String(error))}`);
|
|
256
|
-
}
|
|
256
|
+
}));
|
|
257
257
|
},
|
|
258
258
|
}),
|
|
259
259
|
defineTool({
|
|
260
260
|
name: 'ebay_display_credentials',
|
|
261
261
|
description: 'Display all eBay API credentials and current token information. Shows client ID, client secret (masked), environment (production/sandbox), redirect URI, and current token status including access token (masked), refresh token (masked), app token (masked), and their expiry times. Useful for debugging authentication issues and verifying configuration.',
|
|
262
|
-
inputSchema:
|
|
262
|
+
inputSchema: displayCredentialsInputSchema.shape,
|
|
263
263
|
handler: (api) => {
|
|
264
264
|
const tokenInfo = api.getTokenInfo();
|
|
265
265
|
const authClient = api.getAuthClient().getOAuthClient();
|
|
@@ -267,7 +267,7 @@ export const tokenManagementEntries = [
|
|
|
267
267
|
appAccessToken: authClient.getCachedAppAccessToken(),
|
|
268
268
|
appAccessTokenExpiry: authClient.getCachedAppAccessTokenExpiry(),
|
|
269
269
|
authenticated: api.isAuthenticated(),
|
|
270
|
-
|
|
270
|
+
config: api.getConfig(),
|
|
271
271
|
tokenInfo,
|
|
272
272
|
userTokens: authClient.getUserTokens(),
|
|
273
273
|
});
|
|
@@ -276,39 +276,40 @@ export const tokenManagementEntries = [
|
|
|
276
276
|
defineTool({
|
|
277
277
|
name: 'ebay_refresh_access_token',
|
|
278
278
|
description: 'Manually refresh the user access token using the stored refresh token. This is useful when you want to proactively refresh an access token before it expires, or when recovering from authentication errors. Requires that user tokens are already set (either via EBAY_USER_REFRESH_TOKEN in .env or via ebay_set_user_tokens_with_expiry). Returns the new access token and expiry time.',
|
|
279
|
-
inputSchema:
|
|
279
|
+
inputSchema: refreshAccessTokenInputSchema.shape,
|
|
280
280
|
outputSchema: {
|
|
281
281
|
type: 'object',
|
|
282
282
|
properties: {},
|
|
283
283
|
description: 'Success response',
|
|
284
284
|
},
|
|
285
|
-
handler:
|
|
285
|
+
handler: (api) => {
|
|
286
286
|
const authClient = api.getAuthClient().getOAuthClient();
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
: '
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
287
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
288
|
+
if (!api.hasUserTokens()) {
|
|
289
|
+
return yield* Effect.fail(missingUserTokensError);
|
|
290
|
+
}
|
|
291
|
+
yield* authClient
|
|
292
|
+
.refreshUserToken()
|
|
293
|
+
.pipe(Effect.mapError((error) => tokenToolError('refreshAccessToken', 'Failed to refresh access token', error)));
|
|
294
|
+
return yield* Effect.sync(() => {
|
|
295
|
+
const internalTokens = authClient.getUserTokens();
|
|
296
|
+
return {
|
|
297
|
+
success: true,
|
|
298
|
+
message: 'Access token refreshed successfully',
|
|
299
|
+
accessToken: internalTokens?.userAccessToken
|
|
300
|
+
? maskToken(internalTokens.userAccessToken)
|
|
301
|
+
: null,
|
|
302
|
+
accessTokenExpiry: internalTokens?.userAccessTokenExpiry
|
|
303
|
+
? {
|
|
304
|
+
timestamp: internalTokens.userAccessTokenExpiry,
|
|
305
|
+
date: new Date(internalTokens.userAccessTokenExpiry).toISOString(),
|
|
306
|
+
expiresInSeconds: Math.floor((internalTokens.userAccessTokenExpiry - Date.now()) / 1000),
|
|
307
|
+
}
|
|
308
|
+
: null,
|
|
309
|
+
tokenInfo: api.getTokenInfo(),
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
}));
|
|
312
313
|
},
|
|
313
314
|
}),
|
|
314
315
|
defineTool({
|
|
@@ -324,27 +325,24 @@ export const tokenManagementEntries = [
|
|
|
324
325
|
'- "invalid or was issued to another client": Code expired, get fresh code\n' +
|
|
325
326
|
'- "Insufficient permissions": Re-run OAuth flow with additional scopes in ebay_get_oauth_url\n\n' +
|
|
326
327
|
'For complete OAuth guide with scopes, troubleshooting, and examples, see: docs/auth/OAUTH_QUICK_REFERENCE.md',
|
|
327
|
-
inputSchema:
|
|
328
|
-
code: z
|
|
329
|
-
.string()
|
|
330
|
-
.min(1)
|
|
331
|
-
.describe('The authorization code received from eBay after user authorization. This is the "code" parameter in the redirect URL.'),
|
|
332
|
-
},
|
|
328
|
+
inputSchema: exchangeAuthorizationCodeInputSchema.shape,
|
|
333
329
|
outputSchema: {
|
|
334
330
|
type: 'object',
|
|
335
331
|
properties: {},
|
|
336
332
|
description: 'Token exchange response including access token, refresh token, and expiry times',
|
|
337
333
|
},
|
|
338
|
-
handler:
|
|
334
|
+
handler: (api, args) => {
|
|
339
335
|
const { code } = args;
|
|
340
|
-
|
|
341
|
-
throw new Error('Authorization code is required');
|
|
342
|
-
}
|
|
343
|
-
try {
|
|
336
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
344
337
|
// Codes arriving from a redirect URL may be percent-encoded.
|
|
345
|
-
const decodedCode =
|
|
338
|
+
const decodedCode = yield* Effect.try({
|
|
339
|
+
try: () => (code.includes('%') ? decodeURIComponent(code) : code),
|
|
340
|
+
catch: (error) => tokenToolError('exchangeAuthorizationCode', 'Failed to exchange authorization code', error),
|
|
341
|
+
});
|
|
346
342
|
const authClient = api.getAuthClient().getOAuthClient();
|
|
347
|
-
const tokenData =
|
|
343
|
+
const tokenData = yield* authClient
|
|
344
|
+
.exchangeCodeForToken(decodedCode)
|
|
345
|
+
.pipe(Effect.mapError((error) => tokenToolError('exchangeAuthorizationCode', 'Failed to exchange authorization code', error)));
|
|
348
346
|
return {
|
|
349
347
|
success: true,
|
|
350
348
|
message: 'Authorization code successfully exchanged for tokens. Tokens have been stored and will be used for subsequent API requests.',
|
|
@@ -358,10 +356,7 @@ export const tokenManagementEntries = [
|
|
|
358
356
|
},
|
|
359
357
|
note: 'The refresh token has been saved to your .env file for future use.',
|
|
360
358
|
};
|
|
361
|
-
}
|
|
362
|
-
catch (error) {
|
|
363
|
-
throw new Error(`Failed to exchange authorization code: ${getErrorMessage(error, String(error))}`);
|
|
364
|
-
}
|
|
359
|
+
}));
|
|
365
360
|
},
|
|
366
361
|
}),
|
|
367
362
|
];
|
|
@@ -1,79 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { defineTool } from '../../tools/defineTool.js';
|
|
2
|
+
import { createListingSchema, endListingSchema, getActiveListingsSchema, getListingSchema, relistItemSchema, reviseListingSchema, } from '../../utils/trading/trading.js';
|
|
3
|
+
import { Effect } from 'effect';
|
|
4
|
+
/** Trading API tools for fixed-price listing operations. */
|
|
4
5
|
export const tradingEntries = [
|
|
5
6
|
defineTool({
|
|
6
7
|
name: 'ebay_get_active_listings',
|
|
7
8
|
description: 'Get all active fixed-price listings with SKU, quantity, price, and watch count.\n\nUses the Trading API (GetMyeBaySelling). Returns listings created via any method (UI, Trading API, or REST API).\n\nRequired: User OAuth token.',
|
|
8
|
-
inputSchema:
|
|
9
|
-
page: z.number().optional().describe('Page number (default 1)'),
|
|
10
|
-
entriesPerPage: z.number().optional().describe('Items per page, max 200 (default 50)'),
|
|
11
|
-
},
|
|
9
|
+
inputSchema: getActiveListingsSchema.shape,
|
|
12
10
|
annotations: { readOnlyHint: true },
|
|
13
|
-
handler: (api, args) => api.trading.getActiveListings(args
|
|
11
|
+
handler: (api, args) => Effect.runPromise(api.trading.getActiveListings(args)),
|
|
14
12
|
}),
|
|
15
13
|
defineTool({
|
|
16
14
|
name: 'ebay_get_listing',
|
|
17
15
|
description: 'Get full details for a single listing by item ID.\n\nUses the Trading API (GetItem). Returns all listing fields including description, specifics, shipping, and images.\n\nRequired: User OAuth token.',
|
|
18
|
-
inputSchema:
|
|
19
|
-
itemId: z.string().describe('The eBay item ID (e.g., "167382780779")'),
|
|
20
|
-
},
|
|
16
|
+
inputSchema: getListingSchema.shape,
|
|
21
17
|
annotations: { readOnlyHint: true },
|
|
22
|
-
handler: (api, args) => api.trading.getListing(args
|
|
18
|
+
handler: (api, args) => Effect.runPromise(api.trading.getListing(args)),
|
|
23
19
|
}),
|
|
24
20
|
defineTool({
|
|
25
21
|
name: 'ebay_create_listing',
|
|
26
22
|
description: 'Create a new fixed-price listing.\n\nUses the Trading API (AddFixedPriceItem). Requires complete item details.\n\nRequired: User OAuth token.',
|
|
27
|
-
inputSchema:
|
|
28
|
-
item: z
|
|
29
|
-
.record(z.unknown())
|
|
30
|
-
.describe('Item details object. Required fields: Title, PrimaryCategory.CategoryID, StartPrice, ConditionID, Country, Currency, DispatchTimeMax, ListingDuration, ListingType ("FixedPriceItem"), Quantity, SKU.'),
|
|
31
|
-
},
|
|
23
|
+
inputSchema: createListingSchema.shape,
|
|
32
24
|
annotations: { readOnlyHint: false },
|
|
33
|
-
handler: (api, args) => api.trading.createListing(args
|
|
25
|
+
handler: (api, args) => Effect.runPromise(api.trading.createListing(args)),
|
|
34
26
|
}),
|
|
35
27
|
defineTool({
|
|
36
28
|
name: 'ebay_revise_listing',
|
|
37
29
|
description: 'Revise an existing fixed-price listing. Update quantity, price, title, description, or any other field.\n\nUses the Trading API (ReviseFixedPriceItem). Only send the fields you want to change.\n\nExamples:\n- Update quantity: { "Quantity": 10 }\n- Update price: { "StartPrice": 14.99 }\n- Update title: { "Title": "New Title" }\n- Multiple fields: { "Quantity": 10, "StartPrice": 14.99 }\n\nRequired: User OAuth token.',
|
|
38
|
-
inputSchema:
|
|
39
|
-
itemId: z.string().describe('The eBay item ID to revise'),
|
|
40
|
-
fields: z
|
|
41
|
-
.record(z.unknown())
|
|
42
|
-
.describe('Fields to update (e.g., { "Quantity": 10, "StartPrice": 14.99 })'),
|
|
43
|
-
},
|
|
30
|
+
inputSchema: reviseListingSchema.shape,
|
|
44
31
|
annotations: { readOnlyHint: false },
|
|
45
|
-
handler: (api, args) => api.trading.reviseListing(args
|
|
32
|
+
handler: (api, args) => Effect.runPromise(api.trading.reviseListing(args)),
|
|
46
33
|
}),
|
|
47
34
|
defineTool({
|
|
48
35
|
name: 'ebay_end_listing',
|
|
49
36
|
description: 'End/remove an active fixed-price listing.\n\nUses the Trading API (EndFixedPriceItem).\n\nRequired: User OAuth token.',
|
|
50
|
-
inputSchema:
|
|
51
|
-
itemId: z.string().describe('The eBay item ID to end'),
|
|
52
|
-
reason: z
|
|
53
|
-
.enum([
|
|
54
|
-
'NotAvailable',
|
|
55
|
-
'Incorrect',
|
|
56
|
-
'LostOrBroken',
|
|
57
|
-
'OtherListingError',
|
|
58
|
-
'SellToHighBidder',
|
|
59
|
-
])
|
|
60
|
-
.optional()
|
|
61
|
-
.describe('Reason for ending (default: NotAvailable)'),
|
|
62
|
-
},
|
|
37
|
+
inputSchema: endListingSchema.shape,
|
|
63
38
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
64
|
-
handler: (api, args) => api.trading.endListing(args
|
|
39
|
+
handler: (api, args) => Effect.runPromise(api.trading.endListing(args)),
|
|
65
40
|
}),
|
|
66
41
|
defineTool({
|
|
67
42
|
name: 'ebay_relist_item',
|
|
68
43
|
description: 'Relist an ended fixed-price listing, optionally with modifications.\n\nUses the Trading API (RelistFixedPriceItem).\n\nRequired: User OAuth token.',
|
|
69
|
-
inputSchema:
|
|
70
|
-
itemId: z.string().describe('The eBay item ID to relist'),
|
|
71
|
-
modifications: z
|
|
72
|
-
.record(z.unknown())
|
|
73
|
-
.optional()
|
|
74
|
-
.describe('Optional fields to change when relisting (e.g., { "Quantity": 20 })'),
|
|
75
|
-
},
|
|
44
|
+
inputSchema: relistItemSchema.shape,
|
|
76
45
|
annotations: { readOnlyHint: false },
|
|
77
|
-
handler: (api, args) => api.trading.relistItem(args
|
|
46
|
+
handler: (api, args) => Effect.runPromise(api.trading.relistItem(args)),
|
|
78
47
|
}),
|
|
79
48
|
];
|