@aashari/mcp-server-aws-sso
Version:
Node.js/TypeScript MCP server for AWS Single Sign-On (SSO). Enables AI systems (LLMs) with tools to initiate SSO login (device auth flow), list accounts/roles, and securely execute AWS CLI commands using temporary credentials. Streamlines AI interaction w
222 lines (221 loc) • 9.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createUserFriendlyErrorMessage = createUserFriendlyErrorMessage;
exports.formatCliError = formatCliError;
const logger_util_js_1 = require("./logger.util.js");
const error_types_util_js_1 = require("./error-types.util.js");
const formatter_util_js_1 = require("./formatter.util.js");
const aws_sso_util_js_1 = require("./aws.sso.util.js");
const error_util_js_1 = require("./error.util.js");
const error_detection_util_js_1 = require("./error-detection.util.js");
/**
* Create user-friendly error messages based on error type and context
* @param code The error code
* @param context Context information for better error messages
* @param originalMessage The original error message
* @returns User-friendly error message
*/
function createUserFriendlyErrorMessage(code, context = {}, originalMessage) {
const methodLogger = logger_util_js_1.Logger.forContext('utils/error-formatting.util.ts', 'createUserFriendlyErrorMessage');
const { entityType, entityId, operation } = context;
// Format entity ID for display
let entityIdStr = '';
if (entityId) {
if (typeof entityId === 'string') {
entityIdStr = entityId;
}
else {
// Handle object entityId
entityIdStr = Object.values(entityId).join('/');
}
}
// Determine entity display name
const entity = entityType
? `${entityType}${entityIdStr ? ` ${entityIdStr}` : ''}`
: 'Resource';
let message = '';
switch (code) {
case error_types_util_js_1.ErrorCode.NOT_FOUND:
case error_types_util_js_1.ErrorCode.AWS_SDK_RESOURCE_NOT_FOUND:
message = `${entity} not found${entityIdStr ? `: ${entityIdStr}` : ''}. Verify the ID is correct and that you have access to this ${entityType?.toLowerCase() || 'resource'}.`;
break;
case error_types_util_js_1.ErrorCode.ACCESS_DENIED:
case error_types_util_js_1.ErrorCode.AWS_SDK_PERMISSION_DENIED: {
const entityForDisplay = entityType
? `${entityType.toLowerCase()}${entityIdStr ? ` ${entityIdStr}` : ''}`
: 'resource';
message = `Access denied for ${entityForDisplay}. Verify your credentials and permissions.`;
break;
}
case error_types_util_js_1.ErrorCode.INVALID_CURSOR:
message = `Invalid pagination cursor. Use the exact cursor string returned from previous results.`;
break;
case error_types_util_js_1.ErrorCode.VALIDATION_ERROR:
case error_types_util_js_1.ErrorCode.AWS_SDK_INVALID_REQUEST:
message =
originalMessage ||
`Invalid data provided for ${operation || 'operation'} ${entity.toLowerCase()}.`;
break;
case error_types_util_js_1.ErrorCode.NETWORK_ERROR:
message = `Network error while ${operation || 'connecting to'} the service. Please check your internet connection and try again.`;
break;
case error_types_util_js_1.ErrorCode.RATE_LIMIT_ERROR:
case error_types_util_js_1.ErrorCode.AWS_SDK_THROTTLING:
message = `Rate limit exceeded. Please wait a moment and try again, or reduce the frequency of requests.`;
break;
case error_types_util_js_1.ErrorCode.AWS_SSO_DEVICE_AUTH_TIMEOUT:
message = `AWS SSO authentication timed out. Please run 'login' again and complete the authorization in your browser.`;
break;
case error_types_util_js_1.ErrorCode.AWS_SSO_TOKEN_EXPIRED:
message = `AWS SSO token has expired. Please run 'login' to get a new token.`;
break;
case error_types_util_js_1.ErrorCode.AWS_SSO_AUTH_PENDING:
message = `AWS SSO authorization is pending. Please complete the authorization in your browser.`;
break;
case error_types_util_js_1.ErrorCode.AWS_SSO_AUTH_DENIED:
message = `AWS SSO authorization was denied. Please run 'login' and approve the access request.`;
break;
default:
message = `An unexpected error occurred while ${operation || 'processing'} ${entity.toLowerCase()}.`;
}
// Include original message details if appropriate
if (originalMessage &&
code !== error_types_util_js_1.ErrorCode.NOT_FOUND &&
code !== error_types_util_js_1.ErrorCode.ACCESS_DENIED &&
code !== error_types_util_js_1.ErrorCode.AWS_SDK_RESOURCE_NOT_FOUND &&
code !== error_types_util_js_1.ErrorCode.AWS_SDK_PERMISSION_DENIED) {
message += ` Error details: ${originalMessage}`;
}
methodLogger.debug(`Created user-friendly message: ${message}`, {
code,
context,
});
return message;
}
/**
* Format an error for CLI output in the same Markdown style as successful responses
* @param error The error to format
* @param context Additional context for formatting
* @returns Markdown formatted error message
*/
function formatCliError(error, context) {
const methodLogger = logger_util_js_1.Logger.forContext('utils/error-formatting.util.ts', 'formatCliError');
// Ensure we have an McpError to work with
const mcpError = (0, error_util_js_1.ensureMcpError)(error);
methodLogger.debug(`Formatting CLI error: ${mcpError.message}`, {
error,
context,
});
// Extract useful information for display
const { code } = (0, error_detection_util_js_1.detectErrorType)(mcpError);
const errorMessage = createUserFriendlyErrorMessage(code, {}, mcpError.message);
// Build context properties for the formatter
const contextProps = {};
if (context?.accountId)
contextProps['Account'] = context.accountId;
if (context?.roleName)
contextProps['Role'] = context.roleName;
if (context?.region)
contextProps['Region'] = context.region;
if (context?.instanceId)
contextProps['Instance ID'] = context.instanceId;
// Create output sections
const outputSections = [];
// Main error section
outputSections.push({
heading: 'Error',
content: errorMessage,
});
// Add command if available
if (context?.command) {
outputSections.push({
heading: 'Failed Command',
content: context.command,
isCodeBlock: true,
language: 'bash',
});
}
// Add error details if available
const errorDetails = [];
if (mcpError.errorType) {
errorDetails.push(`**Error Type**: ${mcpError.errorType}`);
errorDetails.push('');
}
if (mcpError.statusCode) {
errorDetails.push(`**Status Code**: ${mcpError.statusCode}`);
errorDetails.push('');
}
// Add technical details for debugging
if (errorDetails.length > 0) {
outputSections.push({
heading: 'Error Details',
level: 3,
content: errorDetails,
});
}
// Add troubleshooting section for common errors
if (code === error_types_util_js_1.ErrorCode.AWS_SSO_TOKEN_EXPIRED) {
outputSections.push({
heading: 'Troubleshooting',
level: 3,
content: [
'Your AWS SSO token has expired. Run the following command to re-authenticate:',
'```bash',
'mcp-aws-sso login',
'```',
],
});
}
else if (code === error_types_util_js_1.ErrorCode.AWS_SDK_PERMISSION_DENIED) {
outputSections.push({
heading: 'Troubleshooting',
level: 3,
content: [
'The role you are using does not have the required permissions. Try:',
'- Using a different role with appropriate permissions',
'- Verifying the account ID and role name are correct',
'',
'You can list available roles with:',
'```bash',
'mcp-aws-sso ls-accounts',
'```',
],
});
}
else if (code === error_types_util_js_1.ErrorCode.AWS_SDK_INVALID_REQUEST) {
outputSections.push({
heading: 'Troubleshooting',
level: 3,
content: [
'The request to AWS is invalid. Check:',
'- Instance ID exists and is in a running state',
'- The AWS region is correct',
'- The EC2 instance has SSM Agent installed and running',
'- The role has permission to use SSM',
],
});
}
// Get default region from utility if available
let defaultRegion;
try {
defaultRegion = (0, aws_sso_util_js_1.getDefaultAwsRegion)();
}
catch {
// Fallback to environment variables
defaultRegion =
process.env.AWS_REGION ||
process.env.AWS_DEFAULT_REGION ||
'ap-southeast-1';
}
// Add identity and region info
const identityInfo = {
defaultRegion,
selectedRegion: context?.region,
identity: {
accountId: context?.accountId,
roleName: context?.roleName,
},
};
// Use baseCommandFormatter to maintain consistent structure
return (0, formatter_util_js_1.baseCommandFormatter)(context?.title || 'AWS SSO: Error', contextProps, outputSections, undefined, identityInfo);
}