amazon-seller-mcp
Version:
Model Context Protocol (MCP) client for Amazon Selling Partner API
531 lines • 31 kB
JavaScript
/**
* Reports resources for Amazon Selling Partner API
*/
import { ReportsClient } from '../../api/reports-client.js';
import { error, info } from '../../utils/logger.js';
/**
* Register reports resources with the resource manager
*
* @param resourceManager Resource registration manager
* @param authConfig Authentication configuration
*/
export function registerReportsResources(resourceManager, authConfig) {
const reportsClient = new ReportsClient(authConfig);
// Register reports collection resource
resourceManager.registerResource('amazon-reports', resourceManager.createResourceTemplate('amazon-reports://{reportId}', 'amazon-reports://', {
// Completion function for reportId parameter
reportId: async (value) => {
if (!value || value.length < 3) {
return [];
}
try {
// Get all reports and filter by the partial report ID
const result = await reportsClient.getReports();
// Filter and return matching report IDs
return result.reports
.filter((report) => report.reportId.toLowerCase().includes(value.toLowerCase()))
.map((report) => report.reportId)
.slice(0, 10); // Limit to 10 results
}
catch (err) {
error('Error completing Report ID:', { error: err });
return [];
}
},
}), {
title: 'Amazon Reports',
description: 'View and manage your Amazon reports',
}, async (uri, params) => {
try {
const { reportId } = params;
// If reportId is provided, get a specific report
if (reportId) {
const report = await reportsClient.getReport({ reportId });
// Format the response as markdown
let markdown = `# Amazon Report: ${reportId}\n\n`;
// Add basic information
markdown += `## Report Information\n\n`;
markdown += `**Report ID:** ${report.reportId}\n\n`;
markdown += `**Report Type:** ${report.reportType}\n\n`;
markdown += `**Status:** ${report.processingStatus}\n\n`;
markdown += `**Created:** ${new Date(report.createdTime).toLocaleString()}\n\n`;
if (report.processingStartTime) {
markdown += `**Processing Started:** ${new Date(report.processingStartTime).toLocaleString()}\n\n`;
}
if (report.processingEndTime) {
markdown += `**Processing Completed:** ${new Date(report.processingEndTime).toLocaleString()}\n\n`;
}
if (report.dataStartTime) {
markdown += `**Data Start Time:** ${new Date(report.dataStartTime).toLocaleString()}\n\n`;
}
if (report.dataEndTime) {
markdown += `**Data End Time:** ${new Date(report.dataEndTime).toLocaleString()}\n\n`;
}
if (report.marketplaceIds && report.marketplaceIds.length > 0) {
markdown += `**Marketplace IDs:** ${report.marketplaceIds.join(', ')}\n\n`;
}
// Add report content if available
if (report.reportDocumentId && report.processingStatus === 'DONE') {
markdown += `## Report Content\n\n`;
try {
const reportContent = await reportsClient.downloadReportDocument(report.reportDocumentId);
// Determine if the content is likely to be CSV
const isCSV = reportContent.includes(',') && reportContent.split('\n').length > 1;
if (isCSV) {
// Format as a markdown table
const lines = reportContent.trim().split('\n');
const headers = lines[0].split(',').map((header) => header.trim());
// Create table header
markdown += '| ' + headers.join(' | ') + ' |\n';
markdown += '| ' + headers.map(() => '---').join(' | ') + ' |\n';
// Add table rows (limit to 100 rows to avoid excessive content)
const dataRows = lines.slice(1, Math.min(lines.length, 101));
dataRows.forEach((row) => {
const cells = row.split(',').map((cell) => cell.trim());
markdown += '| ' + cells.join(' | ') + ' |\n';
});
if (lines.length > 101) {
markdown +=
'\n*Report truncated. Download the full report for complete data.*\n\n';
}
}
else {
// Format as a code block
markdown += '```\n';
// Limit content to avoid excessive output
markdown +=
reportContent.length > 5000
? reportContent.substring(0, 5000) + '\n\n... (content truncated) ...'
: reportContent;
markdown += '\n```\n\n';
}
// Add download link
markdown += `## Actions\n\n`;
markdown += `- [Download Full Report](amazon-report-action://${reportId}/download)\n\n`;
}
catch (error) {
markdown += `Failed to retrieve report content: ${error.message}\n\n`;
markdown += `You can try downloading the report directly:\n\n`;
markdown += `- [Download Report](amazon-report-action://${reportId}/download)\n\n`;
}
}
else if (report.processingStatus === 'IN_QUEUE' ||
report.processingStatus === 'IN_PROGRESS') {
markdown += `## Status\n\n`;
markdown += `This report is still being processed. Please check back later.\n\n`;
markdown += `- [Refresh Report Status](amazon-reports://${reportId})\n\n`;
}
else if (report.processingStatus === 'CANCELLED' ||
report.processingStatus === 'FATAL') {
markdown += `## Status\n\n`;
markdown += `This report was not completed successfully.\n\n`;
markdown += `- [Request New Report](amazon-report-action://create/${report.reportType})\n\n`;
}
// Add actions
markdown += `## Actions\n\n`;
markdown += `- [View All Reports](amazon-reports://)\n`;
markdown += `- [Request Similar Report](amazon-report-action://create/${report.reportType})\n`;
if (report.processingStatus !== 'CANCELLED') {
markdown += `- [Cancel Report](amazon-report-action://${reportId}/cancel)\n`;
}
return {
contents: [
{
uri: `amazon-reports://${reportId}`,
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
// If no reportId is provided, list all reports
else {
// Get query parameters from the URI
const url = new URL(uri.toString());
const reportTypes = url.searchParams.get('reportTypes')?.split(',');
const processingStatuses = url.searchParams.get('processingStatuses')?.split(',');
const createdSince = url.searchParams.get('createdSince') || undefined;
const createdUntil = url.searchParams.get('createdUntil') || undefined;
const nextToken = url.searchParams.get('nextToken') || undefined;
// Get reports with optional filters
const result = await reportsClient.getReports({
reportTypes,
processingStatuses,
createdSince,
createdUntil,
nextToken,
});
// Format the response as markdown
let markdown = `# Amazon Reports\n\n`;
if (result.reports.length === 0) {
markdown += `No reports found.\n`;
}
else {
markdown += `Found ${result.reports.length} reports\n\n`;
// Add filtering options
markdown += `## Filter Options\n\n`;
markdown += `- [All Reports](amazon-reports://)\n`;
markdown += `- [Completed Reports](amazon-reports://?processingStatuses=DONE)\n`;
markdown += `- [In Progress Reports](amazon-reports://?processingStatuses=IN_PROGRESS,IN_QUEUE)\n`;
markdown += `- [Failed Reports](amazon-reports://?processingStatuses=CANCELLED,FATAL)\n\n`;
// Add common report type filters
markdown += `## Report Types\n\n`;
markdown += `- [Inventory Reports](amazon-reports://?reportTypes=GET_AFN_INVENTORY_DATA,GET_FBA_FULFILLMENT_INVENTORY_SUMMARY_DATA)\n`;
markdown += `- [Order Reports](amazon-reports://?reportTypes=GET_FLAT_FILE_ORDERS_DATA,GET_ORDERS_DATA)\n`;
markdown += `- [Listing Reports](amazon-reports://?reportTypes=GET_MERCHANT_LISTINGS_ALL_DATA,GET_FLAT_FILE_OPEN_LISTINGS_DATA)\n`;
markdown += `- [Performance Reports](amazon-reports://?reportTypes=GET_V1_SELLER_PERFORMANCE_REPORT)\n`;
markdown += `- [Settlement Reports](amazon-reports://?reportTypes=GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE)\n\n`;
// Add date range filters
markdown += `## Date Range Filters\n\n`;
markdown += `- [Last 24 Hours](amazon-reports://?createdSince=${encodeURIComponent(new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString())})\n`;
markdown += `- [Last 7 Days](amazon-reports://?createdSince=${encodeURIComponent(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString())})\n`;
markdown += `- [Last 30 Days](amazon-reports://?createdSince=${encodeURIComponent(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString())})\n\n`;
// Add pagination info if available
if (result.nextToken) {
const nextPageUrl = new URL(uri.toString());
nextPageUrl.searchParams.set('nextToken', result.nextToken);
markdown += `[Next Page](${nextPageUrl.toString()})\n\n`;
}
// Add reports
markdown += `## Reports\n\n`;
result.reports.forEach((report, index) => {
markdown += `### ${index + 1}. [${report.reportType}](amazon-reports://${report.reportId})\n\n`;
markdown += `**Report ID:** ${report.reportId}\n\n`;
markdown += `**Status:** ${report.processingStatus}\n\n`;
markdown += `**Created:** ${new Date(report.createdTime).toLocaleString()}\n\n`;
if (report.processingEndTime && report.processingStatus === 'DONE') {
markdown += `**Completed:** ${new Date(report.processingEndTime).toLocaleString()}\n\n`;
}
// Add action links based on report status
if (report.processingStatus === 'DONE' && report.reportDocumentId) {
markdown += `[View Report](amazon-reports://${report.reportId}) | [Download Report](amazon-report-action://${report.reportId}/download)\n\n`;
}
else if (report.processingStatus === 'IN_QUEUE' ||
report.processingStatus === 'IN_PROGRESS') {
markdown += `[Check Status](amazon-reports://${report.reportId}) | [Cancel Report](amazon-report-action://${report.reportId}/cancel)\n\n`;
}
else {
markdown += `[View Details](amazon-reports://${report.reportId}) | [Request Again](amazon-report-action://create/${report.reportType})\n\n`;
}
markdown += `---\n\n`;
});
}
// Add action to create a new report
markdown += `## Create New Report\n\n`;
markdown += `- [Create New Report](amazon-report-action://create)\n\n`;
return {
contents: [
{
uri: 'amazon-reports://',
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
}
catch (err) {
error('Error retrieving reports:', { error: err });
return {
contents: [
{
uri: uri.toString(),
text: `# Error\n\nFailed to retrieve reports: ${err.message}`,
mimeType: 'text/markdown',
},
],
};
}
});
// Register report action resource
resourceManager.registerResource('amazon-report-action', resourceManager.createResourceTemplate('amazon-report-action://{reportId}/{action}'), {
title: 'Amazon Report Actions',
description: 'Perform actions on Amazon reports',
}, async (uri, params) => {
try {
const { reportId, action } = params;
// Handle create action (special case)
if (reportId === 'create') {
// Format the response as markdown
let markdown = `# Create New Report\n\n`;
markdown += `Use this form to request a new report.\n\n`;
// If a specific report type is provided in the action parameter
if (action) {
markdown += `## Create ${action} Report\n\n`;
markdown += `To create a new ${action} report, use the \`create-report\` tool with the following parameters:\n\n`;
markdown += '```json\n';
markdown += `{\n`;
markdown += ` "reportType": "${action}",\n`;
markdown += ` "marketplaceIds": ["${authConfig.marketplaceId}"],\n`;
markdown += ` "dataStartTime": "YYYY-MM-DDT00:00:00Z",\n`;
markdown += ` "dataEndTime": "YYYY-MM-DDT23:59:59Z"\n`;
markdown += `}`;
markdown += '\n```\n\n';
}
else {
// Show available report types
markdown += `## Available Report Types\n\n`;
// Group report types by category
markdown += `### Inventory Reports\n\n`;
markdown += `- [Create Inventory Report](amazon-report-action://create/GET_AFN_INVENTORY_DATA)\n`;
markdown += `- [Create FBA Inventory Summary Report](amazon-report-action://create/GET_FBA_FULFILLMENT_INVENTORY_SUMMARY_DATA)\n`;
markdown += `- [Create FBA Inventory Health Report](amazon-report-action://create/GET_FBA_FULFILLMENT_INVENTORY_HEALTH_DATA)\n\n`;
markdown += `### Order Reports\n\n`;
markdown += `- [Create Flat File Orders Report](amazon-report-action://create/GET_FLAT_FILE_ORDERS_DATA)\n`;
markdown += `- [Create XML Orders Report](amazon-report-action://create/GET_ORDERS_DATA)\n`;
markdown += `- [Create All Orders Report by Last Update](amazon-report-action://create/GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE)\n\n`;
markdown += `### Listing Reports\n\n`;
markdown += `- [Create All Listings Report](amazon-report-action://create/GET_MERCHANT_LISTINGS_ALL_DATA)\n`;
markdown += `- [Create Open Listings Report](amazon-report-action://create/GET_FLAT_FILE_OPEN_LISTINGS_DATA)\n`;
markdown += `- [Create Inactive Listings Report](amazon-report-action://create/GET_MERCHANT_LISTINGS_INACTIVE_DATA)\n\n`;
markdown += `### Performance Reports\n\n`;
markdown += `- [Create Seller Performance Report](amazon-report-action://create/GET_V1_SELLER_PERFORMANCE_REPORT)\n`;
markdown += `- [Create Seller Feedback Report](amazon-report-action://create/GET_SELLER_FEEDBACK_DATA)\n\n`;
markdown += `### Financial Reports\n\n`;
markdown += `- [Create Settlement Report (Flat File)](amazon-report-action://create/GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE)\n`;
markdown += `- [Create Settlement Report (XML)](amazon-report-action://create/GET_V2_SETTLEMENT_REPORT_DATA_XML)\n\n`;
markdown += `### Other Reports\n\n`;
markdown += `- [Create Sales and Traffic Report](amazon-report-action://create/GET_SALES_AND_TRAFFIC_REPORT)\n`;
markdown += `- [Create Restock Inventory Recommendations Report](amazon-report-action://create/GET_RESTOCK_INVENTORY_RECOMMENDATIONS_REPORT)\n\n`;
markdown += `## Custom Report Request\n\n`;
markdown += `To create a custom report, use the \`create-report\` tool with the following parameters:\n\n`;
markdown += '```json\n';
markdown += `{\n`;
markdown += ` "reportType": "REPORT_TYPE",\n`;
markdown += ` "marketplaceIds": ["${authConfig.marketplaceId}"],\n`;
markdown += ` "dataStartTime": "YYYY-MM-DDT00:00:00Z",\n`;
markdown += ` "dataEndTime": "YYYY-MM-DDT23:59:59Z",\n`;
markdown += ` "reportOptions": {\n`;
markdown += ` "option1": "value1",\n`;
markdown += ` "option2": "value2"\n`;
markdown += ` }\n`;
markdown += `}`;
markdown += '\n```\n\n';
}
markdown += `[Back to Reports](amazon-reports://)\n\n`;
return {
contents: [
{
uri: uri.toString(),
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
// Handle other actions for specific reports
if (!reportId || !action) {
throw new Error('Report ID and action are required');
}
// Format the response as markdown based on the action
let markdown = '';
switch (action.toLowerCase()) {
case 'download':
markdown = `# Download Report: ${reportId}\n\n`;
try {
// Get the report details
const report = await reportsClient.getReport({ reportId });
if (report.processingStatus !== 'DONE' || !report.reportDocumentId) {
markdown += `This report is not ready for download. Current status: ${report.processingStatus}\n\n`;
markdown += `[View Report Details](amazon-reports://${reportId})\n\n`;
}
else {
markdown += `Use this form to download the report.\n\n`;
markdown += `**Report ID:** ${reportId}\n\n`;
markdown += `**Report Type:** ${report.reportType}\n\n`;
markdown += `**Created:** ${new Date(report.createdTime).toLocaleString()}\n\n`;
markdown += `To download this report, use the \`download-report\` tool with the following parameters:\n\n`;
markdown += '```json\n';
markdown += `{\n "reportId": "${reportId}"\n}`;
markdown += '\n```\n\n';
markdown += `This will download the report content and return it in a suitable format.\n\n`;
}
}
catch (error) {
markdown += `Error retrieving report details: ${error.message}\n\n`;
}
break;
case 'cancel':
markdown = `# Cancel Report: ${reportId}\n\n`;
markdown += `Use this form to cancel the report processing.\n\n`;
markdown += `**Report ID:** ${reportId}\n\n`;
markdown += `To cancel this report, use the \`cancel-report\` tool with the following parameters:\n\n`;
markdown += '```json\n';
markdown += `{\n "reportId": "${reportId}"\n}`;
markdown += '\n```\n\n';
markdown += `**Note:** Only reports that are in the IN_QUEUE or IN_PROGRESS state can be canceled.\n\n`;
break;
default:
throw new Error(`Unsupported action: ${action}`);
}
markdown += `[Back to Report](amazon-reports://${reportId})\n\n`;
markdown += `[View All Reports](amazon-reports://)\n\n`;
return {
contents: [
{
uri: uri.toString(),
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
catch (err) {
error('Error processing report action:', { error: err });
return {
contents: [
{
uri: uri.toString(),
text: `# Error\n\nFailed to process report action: ${err.message}`,
mimeType: 'text/markdown',
},
],
};
}
});
// Register report filter resource
resourceManager.registerResource('amazon-report-filter', resourceManager.createResourceTemplate('amazon-report-filter://{filter}', 'amazon-report-filter://'), {
title: 'Amazon Report Filter',
description: 'Filter and view your Amazon reports by various criteria',
}, async (uri, params) => {
try {
const { filter } = params;
// Parse the filter string to determine the filter type and value
let filterType;
let filterValue;
if (filter && filter.includes(':')) {
[filterType, filterValue] = filter.split(':', 2);
}
else {
// Default to showing filter options
filterType = '';
filterValue = '';
}
// If a specific filter is provided
if (filterType && filterValue) {
// Get query parameters from the URI
const url = new URL(uri.toString());
const nextToken = url.searchParams.get('nextToken') || undefined;
// Prepare filter parameters
const filterParams = { nextToken };
// Apply the appropriate filter
switch (filterType.toLowerCase()) {
case 'type':
filterParams.reportTypes = [filterValue];
break;
case 'status':
if (['CANCELLED', 'DONE', 'FATAL', 'IN_PROGRESS', 'IN_QUEUE'].includes(filterValue.toUpperCase())) {
filterParams.processingStatuses = [filterValue.toUpperCase()];
}
break;
case 'date':
// Format should be YYYY-MM-DD
try {
const date = new Date(filterValue);
filterParams.createdSince = new Date(date.setHours(0, 0, 0, 0)).toISOString();
filterParams.createdUntil = new Date(date.setHours(23, 59, 59, 999)).toISOString();
}
catch {
throw new Error(`Invalid date format. Use YYYY-MM-DD.`);
}
break;
default:
throw new Error(`Unknown filter type: ${filterType}`);
}
// Get filtered reports
const result = await reportsClient.getReports(filterParams);
// Format the response as markdown
let markdown = `# Amazon Reports: ${filterType.charAt(0).toUpperCase() + filterType.slice(1)} Filter - ${filterValue}\n\n`;
if (result.reports.length === 0) {
markdown += `No reports found matching the filter.\n`;
}
else {
markdown += `Found ${result.reports.length} reports\n\n`;
// Add pagination info if available
if (result.nextToken) {
const nextPageUrl = new URL(uri.toString());
nextPageUrl.searchParams.set('nextToken', result.nextToken);
markdown += `[Next Page](${nextPageUrl.toString()})\n\n`;
}
// Add reports
markdown += `## Reports\n\n`;
result.reports.forEach((report, index) => {
markdown += `### ${index + 1}. [${report.reportType}](amazon-reports://${report.reportId})\n\n`;
markdown += `**Report ID:** ${report.reportId}\n\n`;
markdown += `**Status:** ${report.processingStatus}\n\n`;
markdown += `**Created:** ${new Date(report.createdTime).toLocaleString()}\n\n`;
if (report.processingEndTime && report.processingStatus === 'DONE') {
markdown += `**Completed:** ${new Date(report.processingEndTime).toLocaleString()}\n\n`;
}
// Add action links based on report status
if (report.processingStatus === 'DONE' && report.reportDocumentId) {
markdown += `[View Report](amazon-reports://${report.reportId}) | [Download Report](amazon-report-action://${report.reportId}/download)\n\n`;
}
else if (report.processingStatus === 'IN_QUEUE' ||
report.processingStatus === 'IN_PROGRESS') {
markdown += `[Check Status](amazon-reports://${report.reportId}) | [Cancel Report](amazon-report-action://${report.reportId}/cancel)\n\n`;
}
else {
markdown += `[View Details](amazon-reports://${report.reportId}) | [Request Again](amazon-report-action://create/${report.reportType})\n\n`;
}
markdown += `---\n\n`;
});
}
return {
contents: [
{
uri: uri.toString(),
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
// If no specific filter is provided, show filter options
else {
// Format the response as markdown
let markdown = `# Amazon Report Filters\n\n`;
markdown += `Use these filters to narrow down your reports view:\n\n`;
markdown += `## Filter by Report Type\n\n`;
markdown += `- [Inventory Reports](amazon-report-filter://type:GET_AFN_INVENTORY_DATA)\n`;
markdown += `- [Order Reports](amazon-report-filter://type:GET_FLAT_FILE_ORDERS_DATA)\n`;
markdown += `- [Listing Reports](amazon-report-filter://type:GET_MERCHANT_LISTINGS_ALL_DATA)\n`;
markdown += `- [Performance Reports](amazon-report-filter://type:GET_V1_SELLER_PERFORMANCE_REPORT)\n`;
markdown += `- [Settlement Reports](amazon-report-filter://type:GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE)\n\n`;
markdown += `## Filter by Status\n\n`;
markdown += `- [Completed Reports](amazon-report-filter://status:DONE)\n`;
markdown += `- [In Progress Reports](amazon-report-filter://status:IN_PROGRESS)\n`;
markdown += `- [Queued Reports](amazon-report-filter://status:IN_QUEUE)\n`;
markdown += `- [Cancelled Reports](amazon-report-filter://status:CANCELLED)\n`;
markdown += `- [Failed Reports](amazon-report-filter://status:FATAL)\n\n`;
markdown += `## Filter by Date\n\n`;
markdown += `Enter a date (YYYY-MM-DD) to filter by: [amazon-report-filter://date:YYYY-MM-DD]\n\n`;
markdown += `## View All Reports\n\n`;
markdown += `- [View All Reports](amazon-reports://)\n`;
return {
contents: [
{
uri: 'amazon-report-filter://',
text: markdown,
mimeType: 'text/markdown',
},
],
};
}
}
catch (err) {
error('Error filtering reports:', { error: err });
return {
contents: [
{
uri: uri.toString(),
text: `# Error\n\nFailed to filter reports: ${err.message}`,
mimeType: 'text/markdown',
},
],
};
}
});
info('Registered reports resources');
}
//# sourceMappingURL=reports-resources.js.map