amazon-seller-mcp
Version:
Model Context Protocol (MCP) client for Amazon Selling Partner API
240 lines • 8.79 kB
JavaScript
/**
* Inventory API client for Amazon Selling Partner API
*/
// Third-party dependencies
import { z } from 'zod';
// Internal imports
import { BaseApiClient } from './base-client.js';
/**
* Inventory API client for Amazon Selling Partner API
*/
export class InventoryClient extends BaseApiClient {
/**
* API version
*/
apiVersion = 'fba/inventory/v1';
/**
* Original authentication configuration
*/
authConfig;
/**
* Create a new InventoryClient instance
*
* @param authConfig Authentication configuration
*/
constructor(authConfig) {
super(authConfig);
this.authConfig = authConfig;
}
/**
* Gets the client configuration
*
* @returns Authentication configuration
*/
getConfig() {
return { ...this.authConfig };
}
/**
* Get inventory
*
* @param params Parameters for retrieving inventory
* @returns Promise resolving to the inventory details
*/
async getInventory(params = {}) {
const { sellerSkus, marketplaceId = this.config.marketplaceId, asins, fulfillmentChannels, startDateTime, endDateTime, pageSize, nextToken, } = params;
// Build query parameters
const query = {
marketplaceId,
};
if (sellerSkus && sellerSkus.length > 0) {
query.sellerSkus = sellerSkus;
}
if (asins && asins.length > 0) {
query.asins = asins;
}
if (fulfillmentChannels && fulfillmentChannels.length > 0) {
query.fulfillmentChannels = fulfillmentChannels;
}
if (startDateTime) {
query.startDateTime = startDateTime.toISOString();
}
if (endDateTime) {
query.endDateTime = endDateTime.toISOString();
}
if (pageSize) {
query.pageSize = Math.min(100, Math.max(1, pageSize)); // Ensure pageSize is between 1 and 100
}
if (nextToken) {
query.nextToken = nextToken;
}
// Make API request
const requestOptions = {
method: 'GET',
path: `/${this.apiVersion}/inventories`,
query: query,
};
// Use cache for inventory (30 seconds TTL - shorter than listings because inventory changes frequently)
const cacheKey = `inventory:${marketplaceId}:${sellerSkus?.join(',') || 'all'}:${asins?.join(',') || 'all'}:${fulfillmentChannels?.join(',') || 'all'}:${nextToken || 'first'}`;
return this.withCache(cacheKey, async () => {
const response = await this.request(requestOptions);
return response.data.payload;
}, 30 // 30 seconds TTL
);
}
/**
* Get inventory for a specific SKU
*
* @param sku Seller SKU
* @returns Promise resolving to the inventory item
*/
async getInventoryBySku(sku) {
const result = await this.getInventory({ sellerSkus: [sku] });
if (!result.items || result.items.length === 0) {
throw new Error(`Inventory for SKU ${sku} not found`);
}
return result.items[0];
}
/**
* Update inventory quantity
*
* @param params Parameters for updating inventory
* @param emitNotification Whether to emit a notification event (default: true)
* Note: This parameter is used by the notification system when it overrides this method
* @returns Promise resolving to the update result
*/
async updateInventory(params, _emitNotification = true) {
const { sku, quantity, fulfillmentChannel, restockDate } = params;
// Validate inventory data
this.validateInventoryUpdateData(params);
// Build request body
const requestBody = {
inventory: {
sku,
fulfillmentChannel,
quantity,
...(restockDate && { restockDate: restockDate.toISOString() }),
},
};
// Make API request
const requestOptions = {
method: 'PUT',
path: `/${this.apiVersion}/inventories/${sku}`,
query: {
marketplaceId: this.config.marketplaceId,
},
data: requestBody,
};
const response = await this.request(requestOptions);
// Clear cache for this SKU
this.clearCache(`inventory:*:${sku}:*`);
return response.data.payload;
}
/**
* Set inventory replenishment settings
*
* @param params Parameters for setting inventory replenishment settings
* @returns Promise resolving to the update result
*/
async setInventoryReplenishment(params) {
const { sku, restockLevel, targetLevel, maximumLevel, leadTimeDays } = params;
// Validate replenishment data
this.validateReplenishmentData(params);
// Build request body
const requestBody = {
replenishmentSettings: {
restockLevel,
targetLevel,
...(maximumLevel !== undefined && { maximumLevel }),
...(leadTimeDays !== undefined && { leadTimeDays }),
},
};
// Make API request
const requestOptions = {
method: 'PUT',
path: `/${this.apiVersion}/inventories/${sku}/replenishment`,
query: {
marketplaceId: this.config.marketplaceId,
},
data: requestBody,
};
const response = await this.request(requestOptions);
// Clear cache for this SKU
this.clearCache(`inventory:*:${sku}:*`);
return response.data.payload;
}
/**
* Validate inventory update data
*
* @param params Inventory update parameters to validate
* @throws Error if validation fails
*/
validateInventoryUpdateData(params) {
// Define validation schema using zod
const inventoryUpdateSchema = z.object({
sku: z.string().min(1, 'SKU is required'),
quantity: z.number().int().min(0, 'Quantity must be a non-negative integer'),
fulfillmentChannel: z.enum(['AMAZON', 'SELLER'], {
errorMap: () => ({ message: "Fulfillment channel must be either 'AMAZON' or 'SELLER'" }),
}),
restockDate: z.date().optional(),
});
try {
// Validate against schema
inventoryUpdateSchema.parse(params);
}
catch (error) {
if (error instanceof z.ZodError) {
// Format validation errors
const formattedErrors = error.errors
.map((err) => `${err.path.join('.')}: ${err.message}`)
.join(', ');
throw new Error(`Inventory update validation failed: ${formattedErrors}`);
}
throw error;
}
}
/**
* Validate replenishment data
*
* @param params Replenishment parameters to validate
* @throws Error if validation fails
*/
validateReplenishmentData(params) {
// Define validation schema using zod
const replenishmentSchema = z
.object({
sku: z.string().min(1, 'SKU is required'),
restockLevel: z.number().int().min(0, 'Restock level must be a non-negative integer'),
targetLevel: z.number().int().min(0, 'Target level must be a non-negative integer'),
maximumLevel: z
.number()
.int()
.min(0, 'Maximum level must be a non-negative integer')
.optional(),
leadTimeDays: z.number().int().min(1, 'Lead time must be a positive integer').optional(),
})
.refine((data) => data.targetLevel >= data.restockLevel, {
message: 'Target level must be greater than or equal to restock level',
path: ['targetLevel'],
})
.refine((data) => !data.maximumLevel || data.maximumLevel >= data.targetLevel, {
message: 'Maximum level must be greater than or equal to target level',
path: ['maximumLevel'],
});
try {
// Validate against schema
replenishmentSchema.parse(params);
}
catch (error) {
if (error instanceof z.ZodError) {
// Format validation errors
const formattedErrors = error.errors
.map((err) => `${err.path.join('.')}: ${err.message}`)
.join(', ');
throw new Error(`Replenishment settings validation failed: ${formattedErrors}`);
}
throw error;
}
}
}
//# sourceMappingURL=inventory-client.js.map