amazon-seller-mcp
Version:
Model Context Protocol (MCP) client for Amazon Selling Partner API
285 lines • 10.7 kB
JavaScript
/**
* Credential management for Amazon Selling Partner API
*/
// Node.js built-ins
import fs from 'fs';
import path from 'path';
// Third-party dependencies
import dotenv from 'dotenv';
// Internal imports
import { AmazonRegion, AuthError, AuthErrorType, } from '../types/auth.js';
/**
* Default configuration file name
*/
const DEFAULT_CONFIG_FILE = '.amazon-seller-mcp.json';
/**
* Environment variable prefixes
*/
const ENV_PREFIX = 'AMAZON_SELLER_MCP';
/**
* Environment variable names
*/
const ENV_VARS = {
CLIENT_ID: `${ENV_PREFIX}_CLIENT_ID`,
CLIENT_SECRET: `${ENV_PREFIX}_CLIENT_SECRET`,
REFRESH_TOKEN: `${ENV_PREFIX}_REFRESH_TOKEN`,
ACCESS_KEY_ID: `${ENV_PREFIX}_ACCESS_KEY_ID`,
SECRET_ACCESS_KEY: `${ENV_PREFIX}_SECRET_ACCESS_KEY`,
ROLE_ARN: `${ENV_PREFIX}_ROLE_ARN`,
REGION: `${ENV_PREFIX}_REGION`,
MARKETPLACE_ID: `${ENV_PREFIX}_MARKETPLACE_ID`,
};
/**
* Common marketplaces
*/
export const MARKETPLACES = {
US: {
marketplaceId: 'ATVPDKIKX0DER',
region: AmazonRegion.NA,
countryCode: 'US',
currencyCode: 'USD',
languageCode: 'en_US',
},
CA: {
marketplaceId: 'A2EUQ1WTGCTBG2',
region: AmazonRegion.NA,
countryCode: 'CA',
currencyCode: 'CAD',
languageCode: 'en_CA',
},
MX: {
marketplaceId: 'A1AM78C64UM0Y8',
region: AmazonRegion.NA,
countryCode: 'MX',
currencyCode: 'MXN',
languageCode: 'es_MX',
},
UK: {
marketplaceId: 'A1F83G8C2ARO7P',
region: AmazonRegion.EU,
countryCode: 'UK',
currencyCode: 'GBP',
languageCode: 'en_GB',
},
DE: {
marketplaceId: 'A1PA6795UKMFR9',
region: AmazonRegion.EU,
countryCode: 'DE',
currencyCode: 'EUR',
languageCode: 'de_DE',
},
FR: {
marketplaceId: 'A13V1IB3VIYZZH',
region: AmazonRegion.EU,
countryCode: 'FR',
currencyCode: 'EUR',
languageCode: 'fr_FR',
},
IT: {
marketplaceId: 'APJ6JRA9NG5V4',
region: AmazonRegion.EU,
countryCode: 'IT',
currencyCode: 'EUR',
languageCode: 'it_IT',
},
ES: {
marketplaceId: 'A1RKKUPIHCS9HS',
region: AmazonRegion.EU,
countryCode: 'ES',
currencyCode: 'EUR',
languageCode: 'es_ES',
},
JP: {
marketplaceId: 'A1VC38T7YXB528',
region: AmazonRegion.FE,
countryCode: 'JP',
currencyCode: 'JPY',
languageCode: 'ja_JP',
},
AU: {
marketplaceId: 'A39IBJ37TRP1C6',
region: AmazonRegion.FE,
countryCode: 'AU',
currencyCode: 'AUD',
languageCode: 'en_AU',
},
};
/**
* Credential manager for Amazon Selling Partner API
*
* Handles loading credentials from environment variables and config files
*/
export class CredentialManager {
configFilePath;
loadEnv;
envPath;
/**
* Create a new CredentialManager instance
*
* @param options Credential manager options
*/
constructor(options = {}) {
this.configFilePath = options.configFilePath;
this.loadEnv = options.loadEnv !== false;
this.envPath = options.envPath;
// Load environment variables if specified
if (this.loadEnv && this.envPath) {
dotenv.config({ path: this.envPath });
}
else if (this.loadEnv) {
dotenv.config();
}
}
/**
* Load credentials from environment variables and config file
*
* @returns Authentication configuration
*/
loadCredentials() {
// Start with empty credentials
let credentials = {};
let region;
let marketplaceId;
let marketplace;
// Load from config file if specified
if (this.configFilePath) {
const fileConfig = this.loadConfigFile(this.configFilePath);
credentials = {
...credentials,
clientId: fileConfig.clientId,
clientSecret: fileConfig.clientSecret,
refreshToken: fileConfig.refreshToken,
accessKeyId: fileConfig.accessKeyId,
secretAccessKey: fileConfig.secretAccessKey,
roleArn: fileConfig.roleArn,
};
region = fileConfig.region;
marketplaceId = fileConfig.marketplaceId;
marketplace = fileConfig.marketplace;
}
// Try default config file locations if no config file specified
if (!this.configFilePath) {
// Try current directory
try {
const fileConfig = this.loadConfigFile(DEFAULT_CONFIG_FILE);
credentials = {
...credentials,
clientId: credentials.clientId || fileConfig.clientId,
clientSecret: credentials.clientSecret || fileConfig.clientSecret,
refreshToken: credentials.refreshToken || fileConfig.refreshToken,
accessKeyId: credentials.accessKeyId || fileConfig.accessKeyId,
secretAccessKey: credentials.secretAccessKey || fileConfig.secretAccessKey,
roleArn: credentials.roleArn || fileConfig.roleArn,
};
region = region || fileConfig.region;
marketplaceId = marketplaceId || fileConfig.marketplaceId;
marketplace = marketplace || fileConfig.marketplace;
}
catch {
// Ignore errors, try home directory next
}
// Try home directory
try {
const homeDir = process.env.HOME || process.env.USERPROFILE;
if (homeDir) {
const homeConfig = this.loadConfigFile(path.join(homeDir, DEFAULT_CONFIG_FILE));
credentials = {
...credentials,
clientId: credentials.clientId || homeConfig.clientId,
clientSecret: credentials.clientSecret || homeConfig.clientSecret,
refreshToken: credentials.refreshToken || homeConfig.refreshToken,
accessKeyId: credentials.accessKeyId || homeConfig.accessKeyId,
secretAccessKey: credentials.secretAccessKey || homeConfig.secretAccessKey,
roleArn: credentials.roleArn || homeConfig.roleArn,
};
region = region || homeConfig.region;
marketplaceId = marketplaceId || homeConfig.marketplaceId;
marketplace = marketplace || homeConfig.marketplace;
}
}
catch {
// Ignore errors
}
}
// Load from environment variables
if (this.loadEnv) {
credentials = {
...credentials,
clientId: process.env[ENV_VARS.CLIENT_ID] || credentials.clientId,
clientSecret: process.env[ENV_VARS.CLIENT_SECRET] || credentials.clientSecret,
refreshToken: process.env[ENV_VARS.REFRESH_TOKEN] || credentials.refreshToken,
accessKeyId: process.env[ENV_VARS.ACCESS_KEY_ID] || credentials.accessKeyId,
secretAccessKey: process.env[ENV_VARS.SECRET_ACCESS_KEY] || credentials.secretAccessKey,
roleArn: process.env[ENV_VARS.ROLE_ARN] || credentials.roleArn,
};
region = process.env[ENV_VARS.REGION] || region;
marketplaceId = process.env[ENV_VARS.MARKETPLACE_ID] || marketplaceId;
}
// If marketplace is specified but not marketplaceId or region, use marketplace config
if (marketplace && (!marketplaceId || !region)) {
const marketplaceConfig = MARKETPLACES[marketplace.toUpperCase()];
if (marketplaceConfig) {
marketplaceId = marketplaceId || marketplaceConfig.marketplaceId;
region = region || marketplaceConfig.region;
}
}
// Validate credentials
if (!credentials.clientId || !credentials.clientSecret || !credentials.refreshToken) {
throw new AuthError('Missing required credentials: clientId, clientSecret, and refreshToken are required', AuthErrorType.INVALID_CREDENTIALS);
}
// Validate region and marketplaceId
if (!region || !Object.values(AmazonRegion).includes(region)) {
throw new AuthError(`Invalid region: ${region}. Must be one of: ${Object.values(AmazonRegion).join(', ')}`, AuthErrorType.INVALID_CREDENTIALS);
}
if (!marketplaceId) {
throw new AuthError('Missing marketplaceId. Specify a marketplace or marketplaceId in config or environment variables', AuthErrorType.INVALID_CREDENTIALS);
}
return {
credentials: credentials,
region,
marketplaceId,
};
}
/**
* Load configuration from a JSON file
*
* @param filePath Path to the configuration file
* @returns Configuration from the file
*/
loadConfigFile(filePath) {
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
return JSON.parse(fileContent);
}
catch (error) {
throw new AuthError(`Failed to load config file ${filePath}: ${error instanceof Error ? error.message : String(error)}`, AuthErrorType.INVALID_CREDENTIALS, error instanceof Error ? error : undefined);
}
}
/**
* Get marketplace configuration by country code
*
* @param countryCode ISO country code (e.g., 'US', 'UK')
* @returns Marketplace configuration
*/
static getMarketplaceByCountry(countryCode) {
const marketplace = MARKETPLACES[countryCode.toUpperCase()];
if (!marketplace) {
throw new AuthError(`Invalid country code: ${countryCode}. Must be one of: ${Object.keys(MARKETPLACES).join(', ')}`, AuthErrorType.INVALID_CREDENTIALS);
}
return marketplace;
}
/**
* Get marketplace configuration by marketplace ID
*
* @param marketplaceId Amazon marketplace ID
* @returns Marketplace configuration
*/
static getMarketplaceById(marketplaceId) {
const marketplace = Object.values(MARKETPLACES).find((m) => m.marketplaceId === marketplaceId);
if (!marketplace) {
throw new AuthError(`Invalid marketplace ID: ${marketplaceId}`, AuthErrorType.INVALID_CREDENTIALS);
}
return marketplace;
}
}
//# sourceMappingURL=credential-manager.js.map