UNPKG

@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

172 lines (171 loc) 8.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const logger_util_js_1 = require("../utils/logger.util.js"); const error_handler_util_js_1 = require("../utils/error-handler.util.js"); const error_types_util_js_1 = require("../utils/error-types.util.js"); const vendor_aws_sso_auth_service_js_1 = require("../services/vendor.aws.sso.auth.service.js"); const aws_sso_auth_formatter_js_1 = require("./aws.sso.auth.formatter.js"); const vendor_aws_sso_exec_service_js_1 = require("../services/vendor.aws.sso.exec.service.js"); const vendor_aws_sso_accounts_service_js_1 = require("../services/vendor.aws.sso.accounts.service.js"); const aws_sso_exec_formatter_js_1 = require("./aws.sso.exec.formatter.js"); /** * AWS SSO Execution Controller Module * * Provides functionality for executing AWS CLI commands with temporary credentials * obtained via AWS SSO. Handles credential retrieval, environment setup, and * command execution with proper output formatting. */ // Create a module logger const controllerLogger = logger_util_js_1.Logger.forContext('controllers/aws.sso.exec.controller.ts'); // Log module initialization controllerLogger.debug('AWS SSO execution controller initialized'); /** * Execute an AWS CLI command with temporary credentials from SSO * * Gets temporary AWS credentials for the specified account and role via SSO, * then executes the AWS CLI command with those credentials in the environment. * Handles authentication verification, command execution, and result formatting. * * @async * @param {ExecCommandToolArgsType} options - Command execution options * @param {string} options.accountId - AWS account ID to get credentials for * @param {string} options.roleName - AWS role name to assume via SSO * @param {string} [options.region] - AWS region to use for the command (optional) * @param {string} options.command - AWS CLI command to execute as string * @returns {Promise<ControllerResponse>} - Formatted command execution result * @throws {Error} If authentication fails, command execution fails, or parameters are invalid * @example * // Execute an S3 list command * const result = await executeCommand({ * accountId: "123456789012", * roleName: "AdminAccess", * region: "us-east-1", * command: "aws s3 ls" * }); */ async function executeCommand(options) { const execCommandLogger = logger_util_js_1.Logger.forContext('controllers/aws.sso.exec.controller.ts', 'executeCommand'); execCommandLogger.debug('Executing AWS CLI command', options); try { // Check if user is authenticated const authStatus = await (0, vendor_aws_sso_auth_service_js_1.checkSsoAuthStatus)(); if (!authStatus.isAuthenticated) { execCommandLogger.debug('User is not authenticated', { errorMessage: authStatus.errorMessage, }); // Return formatted auth required message return { content: (0, aws_sso_auth_formatter_js_1.formatAuthRequired)(), }; } // Validate command options if (!options.accountId || !options.roleName || !options.command) { throw new Error('Missing required parameters: accountId, roleName, and command are required'); } // Log region usage if (options.region) { execCommandLogger.debug('Using explicitly provided region', { region: options.region, }); } // Execute the command execCommandLogger.debug('Executing command with environment', { command: options.command, env: { AWS_REGION: options.region, AWS_DEFAULT_REGION: options.region, }, }); let result; let suggestedRoles; try { // Execute the command via the service result = await (0, vendor_aws_sso_exec_service_js_1.executeCommand)(options.accountId, options.roleName, options.command, options.region); execCommandLogger.debug('Command execution completed by service', { exitCode: result.exitCode, stdoutLength: result.stdout.length, stderrLength: result.stderr.length, }); // Explicitly check for non-zero exit code even if service doesn't throw if (result.exitCode !== 0) { // Check for permission error indicators in stderr or stdout const errorOutput = (result.stderr || '') + (result.stdout || ''); // Combine outputs as errors can be in stdout const isPermissionError = /AccessDenied|UnauthorizedOperation|permission|denied/i.test(errorOutput); if (isPermissionError) { execCommandLogger.debug('Potential permission error detected based on output/exit code.', { accountId: options.accountId, exitCode: result.exitCode, errorOutputSnippet: errorOutput.substring(0, 100), }); try { // Attempt to fetch roles for this account const rolesResponse = await (0, vendor_aws_sso_accounts_service_js_1.listAccountRoles)({ accountId: options.accountId, }); suggestedRoles = rolesResponse.roleList; execCommandLogger.debug(`Found ${suggestedRoles?.length ?? 0} alternative roles for account ${options.accountId}.`); } catch (roleError) { execCommandLogger.warn('Failed to fetch alternative roles after permission error', roleError); // Continue without suggested roles suggestedRoles = []; // Indicate that we tried but failed } } // Note: We don't throw here; the formatter will handle displaying the error } } catch (error) { // Handle errors thrown by executeServiceCommand itself execCommandLogger.error('Error during command execution service call', error); // Check if this underlying error is a permission error const errorMessage = error instanceof Error ? error.message : String(error); const isPermissionError = /AccessDenied|UnauthorizedOperation|permission|denied/i.test(errorMessage); if (isPermissionError) { execCommandLogger.debug('Potential permission error detected from service error.', { accountId: options.accountId }); try { const rolesResponse = await (0, vendor_aws_sso_accounts_service_js_1.listAccountRoles)({ accountId: options.accountId, }); suggestedRoles = rolesResponse.roleList; execCommandLogger.debug(`Found ${suggestedRoles?.length ?? 0} alternative roles for account ${options.accountId}.`); } catch (roleError) { execCommandLogger.warn('Failed to fetch alternative roles after permission service error', roleError); suggestedRoles = []; } // Construct a result object similar to what executeServiceCommand would return on failure result = { stdout: '', stderr: errorMessage, exitCode: 1, // Assume exit code 1 for service errors }; } else { // If it's not a permission error caught here, re-throw for general handling throw error; } } // Format the result, passing suggestedRoles if available const formattedContent = (0, aws_sso_exec_formatter_js_1.formatCommandResult)(options.command, result, { accountId: options.accountId, roleName: options.roleName, region: options.region, suggestedRoles: suggestedRoles, }); return { content: formattedContent, }; } catch (error) { // Use throw instead of return throw (0, error_handler_util_js_1.handleControllerError)(error, (0, error_types_util_js_1.buildErrorContext)('AWS Command', 'executing', 'controllers/aws.sso.exec.controller.ts@executeCommand', `${options.accountId}/${options.roleName}`, { accountId: options.accountId, roleName: options.roleName, command: options.command, region: options.region, })); } } exports.default = { executeCommand, };