amazon-seller-mcp
Version:
Model Context Protocol (MCP) client for Amazon Selling Partner API
593 lines • 25.4 kB
JavaScript
/**
* Orders tools for Amazon Selling Partner API
*/
// Third-party dependencies
import { z } from 'zod';
import { OrdersClient } from '../api/orders-client.js';
/**
* Register orders tools with the tool manager
*
* @param toolManager Tool registration manager
* @param authConfig Authentication configuration
* @param ordersClient Optional orders client instance
*/
export function registerOrdersTools(toolManager, authConfig, ordersClient) {
// Create a new client if one wasn't provided
const client = ordersClient || new OrdersClient(authConfig);
// Register get orders tool
toolManager.registerTool('get-orders', {
title: 'Get Amazon Orders',
description: 'Retrieve Amazon orders with optional filtering',
inputSchema: z.object({
createdAfter: z.string().optional().describe('Created after date (ISO 8601)'),
createdBefore: z.string().optional().describe('Created before date (ISO 8601)'),
lastUpdatedAfter: z.string().optional().describe('Last updated after date (ISO 8601)'),
lastUpdatedBefore: z.string().optional().describe('Last updated before date (ISO 8601)'),
orderStatuses: z
.array(z.enum([
'PENDING',
'UNSHIPPED',
'PARTIALLY_SHIPPED',
'SHIPPED',
'CANCELED',
'UNFULFILLABLE',
'INVOICE_UNCONFIRMED',
'PENDING_AVAILABILITY',
]))
.optional()
.describe('Order statuses to filter by'),
fulfillmentChannels: z
.array(z.enum(['AFN', 'MFN']))
.optional()
.describe('Fulfillment channels to filter by'),
paymentMethods: z
.array(z.enum(['COD', 'CVS', 'Other']))
.optional()
.describe('Payment methods to filter by'),
buyerEmail: z.string().optional().describe('Buyer email to filter by'),
sellerOrderId: z.string().optional().describe('Seller order ID to filter by'),
maxResultsPerPage: z
.number()
.int()
.min(1)
.max(100)
.optional()
.describe('Maximum results per page (1-100)'),
nextToken: z.string().optional().describe('Next token for pagination'),
amazonOrderIds: z
.array(z.string())
.optional()
.describe('Specific Amazon order IDs to retrieve'),
}),
}, async (input) => {
try {
// Validate input
const validatedInput = z
.object({
createdAfter: z.string().optional(),
createdBefore: z.string().optional(),
lastUpdatedAfter: z.string().optional(),
lastUpdatedBefore: z.string().optional(),
orderStatuses: z.array(z.string()).optional(),
fulfillmentChannels: z.array(z.string()).optional(),
paymentMethods: z.array(z.string()).optional(),
buyerEmail: z.string().optional(),
sellerOrderId: z.string().optional(),
maxResultsPerPage: z.number().optional(),
nextToken: z.string().optional(),
amazonOrderIds: z.array(z.string()).optional(),
itemCategories: z.array(z.string()).optional(),
easyShipShipmentStatuses: z.array(z.string()).optional(),
})
.parse(input);
const result = await client.getOrders(validatedInput);
let responseText = `# Amazon Orders\n\n`;
responseText += `**Total Orders:** ${result.orders.length}\n\n`;
if (result.nextToken) {
responseText += `**Next Token:** ${result.nextToken}\n\n`;
}
if (result.orders.length === 0) {
responseText += `No orders found matching the specified criteria.\n\n`;
}
else {
responseText += `## Orders\n\n`;
result.orders.forEach((order, index) => {
responseText += `### ${index + 1}. Order ${order.amazonOrderId}\n\n`;
responseText += `- **Status:** ${order.orderStatus}\n`;
responseText += `- **Purchase Date:** ${order.purchaseDate}\n`;
responseText += `- **Last Updated:** ${order.lastUpdateDate}\n`;
if (order.orderTotal) {
responseText += `- **Total:** ${order.orderTotal.amount} ${order.orderTotal.currencyCode}\n`;
}
if (order.fulfillmentChannel) {
responseText += `- **Fulfillment:** ${order.fulfillmentChannel}\n`;
}
if (order.numberOfItemsShipped !== undefined) {
responseText += `- **Items Shipped:** ${order.numberOfItemsShipped}\n`;
}
if (order.numberOfItemsUnshipped !== undefined) {
responseText += `- **Items Unshipped:** ${order.numberOfItemsUnshipped}\n`;
}
responseText += `- **Resource URI:** amazon-orders://${order.amazonOrderId}\n\n`;
});
}
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error retrieving orders: ${error.message}`,
},
],
isError: true,
};
}
});
// Register get order tool
toolManager.registerTool('get-order', {
title: 'Get Amazon Order',
description: 'Retrieve a specific Amazon order by ID',
inputSchema: z.object({
orderId: z.string().describe('Amazon Order ID'),
}),
}, async (input) => {
try {
// Validate input
const validatedInput = z
.object({
orderId: z.string(),
})
.parse(input);
const order = await client.getOrder({ amazonOrderId: validatedInput.orderId });
let responseText = `# Amazon Order ${order.amazonOrderId}\n\n`;
responseText += `**Status:** ${order.orderStatus}\n\n`;
responseText += `**Purchase Date:** ${order.purchaseDate}\n\n`;
responseText += `**Last Updated:** ${order.lastUpdateDate}\n\n`;
if (order.orderTotal) {
responseText += `**Total:** ${order.orderTotal.amount} ${order.orderTotal.currencyCode}\n\n`;
}
if (order.fulfillmentChannel) {
responseText += `**Fulfillment Channel:** ${order.fulfillmentChannel}\n\n`;
}
if (order.salesChannel) {
responseText += `**Sales Channel:** ${order.salesChannel}\n\n`;
}
if (order.shipServiceLevel) {
responseText += `**Ship Service Level:** ${order.shipServiceLevel}\n\n`;
}
if (order.numberOfItemsShipped !== undefined) {
responseText += `**Items Shipped:** ${order.numberOfItemsShipped}\n\n`;
}
if (order.numberOfItemsUnshipped !== undefined) {
responseText += `**Items Unshipped:** ${order.numberOfItemsUnshipped}\n\n`;
}
if (order.shippingAddress) {
responseText += `## Shipping Address\n\n`;
responseText += `**Name:** ${order.shippingAddress.name}\n\n`;
responseText += `**Address:** ${order.shippingAddress.addressLine1}\n`;
if (order.shippingAddress.addressLine2) {
responseText += `${order.shippingAddress.addressLine2}\n`;
}
if (order.shippingAddress.addressLine3) {
responseText += `${order.shippingAddress.addressLine3}\n`;
}
responseText += `${order.shippingAddress.city}, ${order.shippingAddress.stateOrRegion} ${order.shippingAddress.postalCode}\n`;
responseText += `${order.shippingAddress.countryCode}\n\n`;
if (order.shippingAddress.phone) {
responseText += `**Phone:** ${order.shippingAddress.phone}\n\n`;
}
}
if (order.buyerInfo) {
responseText += `## Buyer Information\n\n`;
if (order.buyerInfo.buyerName) {
responseText += `**Name:** ${order.buyerInfo.buyerName}\n\n`;
}
if (order.buyerInfo.buyerEmail) {
responseText += `**Email:** ${order.buyerInfo.buyerEmail}\n\n`;
}
if (order.buyerInfo.purchaseOrderNumber) {
responseText += `**PO Number:** ${order.buyerInfo.purchaseOrderNumber}\n\n`;
}
}
if (order.paymentMethod) {
responseText += `**Payment Method:** ${order.paymentMethod}\n\n`;
}
if (order.isPrime) {
responseText += `**Prime Order:** Yes\n\n`;
}
if (order.isBusinessOrder) {
responseText += `**Business Order:** Yes\n\n`;
}
responseText += `**Resource URI:** amazon-orders://${order.amazonOrderId}\n\n`;
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error retrieving order: ${error.message}`,
},
],
isError: true,
};
}
});
// Register order processing tool
toolManager.registerTool('process-order', {
title: 'Process Amazon Order',
description: 'Process an Amazon order (confirm, ship, or cancel)',
inputSchema: z.object({
amazonOrderId: z.string().describe('Amazon Order ID'),
action: z.enum(['CONFIRM', 'SHIP', 'CANCEL']).describe('Action to perform'),
details: z
.object({
// For CANCEL action
cancellationReason: z
.string()
.optional()
.describe('Reason for cancellation (required for CANCEL action)'),
// For SHIP action
shippingDetails: z
.object({
carrierCode: z.string().describe('Carrier code'),
trackingNumber: z.string().describe('Tracking number'),
shipDate: z.string().describe('Ship date (YYYY-MM-DD format)'),
items: z
.array(z.object({
orderItemId: z.string().describe('Order item ID'),
quantity: z.number().int().positive().describe('Quantity to ship'),
}))
.describe('Items to ship'),
})
.optional()
.describe('Shipping details (required for SHIP action)'),
})
.optional()
.describe('Additional details for the action'),
}),
}, async (input) => {
try {
// Validate input
const validatedInput = z
.object({
amazonOrderId: z.string(),
action: z.enum(['CONFIRM', 'CANCEL', 'SHIP']),
details: z
.object({
cancellationReason: z.string().optional(),
shippingDetails: z
.object({
carrierCode: z.string(),
trackingNumber: z.string(),
shipDate: z.string(),
items: z.array(z.object({
orderItemId: z.string(),
quantity: z.number().int().positive(),
})),
})
.optional(),
})
.optional(),
})
.parse(input);
// Validate input based on action
if (validatedInput.action === 'CANCEL' &&
(!validatedInput.details || !validatedInput.details.cancellationReason)) {
return {
content: [
{
type: 'text',
text: 'Error: Cancellation reason is required for CANCEL action',
},
],
isError: true,
};
}
if (validatedInput.action === 'SHIP' &&
(!validatedInput.details || !validatedInput.details.shippingDetails)) {
return {
content: [
{
type: 'text',
text: 'Error: Shipping details are required for SHIP action',
},
],
isError: true,
};
}
// Prepare parameters for the API call
const params = {
amazonOrderId: validatedInput.amazonOrderId,
action: validatedInput.action,
details: validatedInput.details,
};
// Call the API
const result = await client.updateOrderStatus(params);
// Format the response
let responseText = `# Order Processing Result\n\n`;
responseText += `**Order ID:** ${result.amazonOrderId}\n\n`;
responseText += `**Action:** ${validatedInput.action}\n\n`;
responseText += `**Status:** ${result.success ? 'Success' : 'Failed'}\n\n`;
if (!result.success && result.errorMessage) {
responseText += `**Error:** ${result.errorMessage}\n\n`;
}
else {
switch (validatedInput.action) {
case 'CONFIRM':
responseText += `Order ${result.amazonOrderId} has been confirmed successfully.\n\n`;
break;
case 'SHIP':
responseText += `Order ${result.amazonOrderId} has been marked as shipped successfully.\n\n`;
responseText += `**Carrier:** ${validatedInput.details?.shippingDetails?.carrierCode}\n\n`;
responseText += `**Tracking Number:** ${validatedInput.details?.shippingDetails?.trackingNumber}\n\n`;
responseText += `**Ship Date:** ${validatedInput.details?.shippingDetails?.shipDate}\n\n`;
break;
case 'CANCEL':
responseText += `Order ${result.amazonOrderId} has been canceled successfully.\n\n`;
responseText += `**Cancellation Reason:** ${validatedInput.details?.cancellationReason}\n\n`;
break;
}
responseText += `Resource URI: amazon-orders://${result.amazonOrderId}\n\n`;
}
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error processing order: ${error.message}`,
},
],
isError: true,
};
}
});
// Register order status update tool
toolManager.registerTool('update-order-status', {
title: 'Update Amazon Order Status',
description: 'Update the status of an Amazon order',
inputSchema: z.object({
amazonOrderId: z.string().describe('Amazon Order ID'),
status: z
.enum([
'PENDING',
'UNSHIPPED',
'PARTIALLY_SHIPPED',
'SHIPPED',
'CANCELED',
'UNFULFILLABLE',
'INVOICE_UNCONFIRMED',
'PENDING_AVAILABILITY',
])
.describe('New order status'),
reason: z.string().optional().describe('Reason for status change'),
}),
}, async (input) => {
try {
// Validate input
const validatedInput = z
.object({
amazonOrderId: z.string(),
status: z.enum([
'PENDING',
'UNSHIPPED',
'PARTIALLY_SHIPPED',
'SHIPPED',
'CANCELED',
'UNFULFILLABLE',
'INVOICE_UNCONFIRMED',
'PENDING_AVAILABILITY',
]),
reason: z.string().optional(),
})
.parse(input);
// Determine the appropriate action based on the requested status
let action;
let details = {};
switch (validatedInput.status) {
case 'SHIPPED':
// For simplicity, we'll require the user to use the process-order tool for shipping
return {
content: [
{
type: 'text',
text: 'To mark an order as shipped, please use the process-order tool with action=SHIP and provide shipping details.',
},
],
isError: true,
};
case 'CANCELED':
action = 'CANCEL';
details = {
cancellationReason: validatedInput.reason || 'Canceled by seller',
};
break;
case 'PENDING':
case 'UNSHIPPED':
action = 'CONFIRM';
break;
default:
return {
content: [
{
type: 'text',
text: `Status ${validatedInput.status} cannot be set directly. Please use the process-order tool with the appropriate action.`,
},
],
isError: true,
};
}
// Call the API
const result = await client.updateOrderStatus({
amazonOrderId: validatedInput.amazonOrderId,
action,
details,
});
// Format the response
let responseText = `# Order Status Update Result\n\n`;
responseText += `**Order ID:** ${result.amazonOrderId}\n\n`;
responseText += `**Requested Status:** ${validatedInput.status}\n\n`;
responseText += `**Status:** ${result.success ? 'Success' : 'Failed'}\n\n`;
if (!result.success && result.errorMessage) {
responseText += `**Error:** ${result.errorMessage}\n\n`;
}
else {
responseText += `Order ${result.amazonOrderId} status has been updated successfully.\n\n`;
if (validatedInput.reason) {
responseText += `**Reason:** ${validatedInput.reason}\n\n`;
}
responseText += `Resource URI: amazon-orders://${result.amazonOrderId}\n\n`;
}
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error updating order status: ${error.message}`,
},
],
isError: true,
};
}
});
// Register order fulfillment tool
toolManager.registerTool('fulfill-order', {
title: 'Fulfill Amazon Order',
description: 'Fulfill an Amazon order with shipping information',
inputSchema: z.object({
amazonOrderId: z.string().describe('Amazon Order ID'),
carrierCode: z.string().describe('Carrier code (e.g., UPS, FEDEX, USPS)'),
trackingNumber: z.string().describe('Tracking number'),
shipDate: z.string().describe('Ship date (YYYY-MM-DD format)'),
items: z
.array(z.object({
orderItemId: z.string().describe('Order item ID'),
quantity: z.number().int().positive().describe('Quantity to ship'),
}))
.describe('Items to ship'),
notifyCustomer: z
.boolean()
.optional()
.describe('Whether to notify the customer about shipment'),
}),
}, async (input) => {
try {
// Validate input
const validatedInput = z
.object({
amazonOrderId: z.string(),
carrierCode: z.string(),
trackingNumber: z.string(),
shipDate: z.string(),
items: z.array(z.object({
orderItemId: z.string(),
quantity: z.number().int().positive(),
})),
notifyCustomer: z.boolean().optional(),
})
.parse(input);
// Validate ship date format
const shipDateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!shipDateRegex.test(validatedInput.shipDate)) {
return {
content: [
{
type: 'text',
text: 'Error: Ship date must be in YYYY-MM-DD format',
},
],
isError: true,
};
}
// Call the API
const result = await client.updateOrderStatus({
amazonOrderId: validatedInput.amazonOrderId,
action: 'SHIP',
details: {
shippingDetails: {
carrierCode: validatedInput.carrierCode,
trackingNumber: validatedInput.trackingNumber,
shipDate: validatedInput.shipDate,
items: validatedInput.items,
},
},
});
// Format the response
let responseText = `# Order Fulfillment Result\n\n`;
responseText += `**Order ID:** ${result.amazonOrderId}\n\n`;
responseText += `**Status:** ${result.success ? 'Success' : 'Failed'}\n\n`;
if (!result.success && result.errorMessage) {
responseText += `**Error:** ${result.errorMessage}\n\n`;
}
else {
responseText += `Order ${result.amazonOrderId} has been fulfilled successfully.\n\n`;
responseText += `**Carrier:** ${validatedInput.carrierCode}\n\n`;
responseText += `**Tracking Number:** ${validatedInput.trackingNumber}\n\n`;
responseText += `**Ship Date:** ${validatedInput.shipDate}\n\n`;
responseText += `**Shipped Items:**\n\n`;
validatedInput.items.forEach((item, index) => {
responseText += `${index + 1}. Item ID: ${item.orderItemId}, Quantity: ${item.quantity}\n`;
});
responseText += `\n`;
if (validatedInput.notifyCustomer) {
responseText += `Customer has been notified about the shipment.\n\n`;
}
responseText += `Resource URI: amazon-orders://${result.amazonOrderId}\n\n`;
}
return {
content: [
{
type: 'text',
text: responseText,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error fulfilling order: ${error.message}`,
},
],
isError: true,
};
}
});
}
//# sourceMappingURL=orders-tools.js.map