AWS MCP: How to Use AWS MCP Servers
In the rapidly evolving landscape of AI-assisted cloud operations, the integration of Amazon Web Services (AWS) with the Model Context Protocol (MCP) represents a significant advancement in how AI systems can securely interact with cloud infrastructure. AWS MCP servers establish a standardized bridge between conversational AI models and AWS's extensive suite of services, enabling AI assistants to provision resources, manage configurations, analyze metrics, and execute complex cloud workflows through natural language interfaces. This technical implementation transforms language models from simple text generators into powerful cloud operations agents capable of programmatic interaction with the entire AWS ecosystem while maintaining appropriate authentication, authorization, and audit controls.
Introduction
The Model Context Protocol provides a standardized framework for connecting AI models with external tools and data sources. AWS MCP servers implement this protocol to create a secure, authenticated interface between AI assistants and Amazon Web Services. By leveraging AWS's comprehensive SDKs and APIs, MCP servers enable language models to programmatically access and manipulate cloud resources, analyze operational data, manage services, and execute infrastructure-as-code—all while maintaining robust security controls and proper access management.
https://github.com/modelcontextprotocol/aws-mcp-server (opens in a new tab)
Technical Architecture of AWS MCP
MCP Protocol Foundation
AWS MCP operates within the Model Context Protocol architecture, which defines several key components:
-
Transport Layer:
- STDIO (Standard Input/Output): Direct process communication
- SSE (Server-Sent Events): HTTP-based asynchronous communication
- WebSocket: Bidirectional real-time communication
-
Resource Types:
- Prompts: Templated cloud operation patterns
- Tools: Executable AWS service operations
- Resources: Cloud resource metadata and configurations
-
Serialization Format: JSON for structured data exchange between client and server
AWS MCP Architecture
The AWS MCP server implements a multi-layered architecture optimized for secure cloud operations:
aws-mcp/
├── src/
│ ├── auth/
│ │ ├── credentials.ts # AWS credentials management
│ │ ├── role-assumption.ts # IAM role management
│ │ └── policy-validator.ts # Permission validation
│ ├── services/
│ │ ├── ec2-service.ts # EC2 operations
│ │ ├── s3-service.ts # S3 operations
│ │ ├── lambda-service.ts # Lambda operations
│ │ └── cloudwatch-service.ts # CloudWatch operations
│ ├── tools/
│ │ ├── resource-tools.ts # Resource management tools
│ │ ├── analytics-tools.ts # Data analysis tools
│ │ ├── monitoring-tools.ts # CloudWatch tools
│ │ └── iac-tools.ts # Infrastructure-as-code tools
│ ├── resources/
│ │ ├── account-info.ts # Account metadata resources
│ │ └── service-quotas.ts # Service limits resources
│ └── server.ts # MCP server implementation
├── config/
│ └── aws-config.ts # Configuration schema
└── package.json # Dependencies and scripts
The core technical implementation consists of:
- Credential Manager: Secure handling of AWS authentication credentials
- Service Clients: Type-safe wrappers around AWS SDK clients
- Permission Boundary Enforcer: Ensures operations comply with IAM policies
- Tool Implementations: Translates MCP commands to AWS API calls
- Response Formatters: Transforms complex AWS responses into MCP-friendly formats
Setup and Installation
Prerequisites
To implement AWS MCP, ensure you have:
- Node.js 16+ environment
- AWS account with appropriate IAM permissions
- AWS CLI configured with credentials (optional but recommended)
- An MCP-compatible client (e.g., Claude Desktop, Cursor, VS Code)
Authentication Configuration
AWS MCP supports multiple authentication methods:
1. Environment Variables
# Set AWS credentials directly
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_REGION=us-west-2
# Optional session token for temporary credentials
export AWS_SESSION_TOKEN=your_session_token
2. Shared Credentials File
The MCP server can use the standard AWS credentials file:
# ~/.aws/credentials
[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key
region = us-west-2
[project-prod]
aws_access_key_id = production_access_key
aws_secret_access_key = production_secret_key
region = us-east-1
3. IAM Role Assumption (Advanced)
For enhanced security, the server can assume IAM roles:
interface AssumeRoleOptions {
roleArn: string;
sessionName: string;
durationSeconds?: number;
externalId?: string;
}
// Implementation
async function assumeRole(options: AssumeRoleOptions): Promise<Credentials> {
const sts = new STSClient({ region: process.env.AWS_REGION });
const command = new AssumeRoleCommand({
RoleArn: options.roleArn,
RoleSessionName: options.sessionName,
DurationSeconds: options.durationSeconds || 3600,
ExternalId: options.externalId
});
const response = await sts.send(command);
return {
accessKeyId: response.Credentials.AccessKeyId,
secretAccessKey: response.Credentials.SecretAccessKey,
sessionToken: response.Credentials.SessionToken,
expiration: response.Credentials.Expiration
};
}
Installation Methods
Option 1: Using npm
npm install -g aws-mcp
Option 2: Using Smithery
npx -y @smithery/cli install aws-mcp --client claude
Option 3: Manual Installation from Source
git clone https://github.com/modelcontextprotocol/aws-mcp-server
cd aws-mcp-server
npm install
npm run build
Configuration
The AWS MCP server accepts various configuration parameters:
-
Authentication Configuration:
AWS_PROFILE
: Named profile from credentials fileAWS_REGION
: Default AWS regionAWS_ROLE_ARN
: Optional IAM role to assume
-
Permission Controls:
AWS_ALLOWED_SERVICES
: Comma-separated list of allowed AWS servicesAWS_ALLOWED_REGIONS
: Comma-separated list of allowed regionsAWS_PERMISSION_BOUNDARY
: ARN of IAM permission boundary policy
-
Operational Settings:
AWS_MAX_CONCURRENCY
: Maximum concurrent AWS API callsAWS_REQUEST_TIMEOUT
: Default timeout for API requests in millisecondsAWS_RETRY_COUNT
: Number of retries for failed API calls
Example configuration in aws-config.json
:
{
"authentication": {
"profile": "default",
"region": "us-east-1",
"assumeRole": {
"roleArn": "arn:aws:iam::123456789012:role/MCP-ExecutionRole",
"sessionName": "MCP-Session",
"durationSeconds": 3600
}
},
"permissions": {
"allowedServices": ["s3", "ec2", "lambda", "cloudwatch"],
"allowedRegions": ["us-east-1", "us-west-2"],
"readOnlyMode": false,
"permissionBoundary": "arn:aws:iam::123456789012:policy/MCP-Boundary"
},
"operations": {
"maxConcurrency": 5,
"timeout": 30000,
"retryCount": 3,
"logLevel": "info"
}
}
Integration with MCP Clients
Claude Desktop Integration
To integrate with Claude Desktop, edit the configuration file:
- macOS:
~/Library/Application\ Support/Claude/claude_desktop_config.json
- Windows:
%APPDATA%/Claude/claude_desktop_config.json
Add the following configuration:
{
"mcpServers": {
"aws": {
"command": "npx",
"args": ["-y", "aws-mcp"],
"env": {
"AWS_PROFILE": "default",
"AWS_REGION": "us-east-1",
"AWS_ALLOWED_SERVICES": "s3,ec2,lambda,cloudwatch"
}
}
}
}
VS Code Integration
For VS Code with GitHub Copilot, add to settings.json:
{
"github.copilot.chat.mcpServers": [
{
"name": "aws",
"command": "npx",
"args": ["-y", "aws-mcp"],
"env": {
"AWS_PROFILE": "default",
"AWS_REGION": "us-east-1"
}
}
]
}
Core Functionality and Technical Usage
Available Tools
The AWS MCP server implements a comprehensive set of tools across major AWS service categories:
1. EC2 Operations
-
aws_ec2_describe_instances: Lists EC2 instances with filters
interface DescribeInstancesOptions { region?: string; filters?: Array<{ name: string; values: string[]; }>; instanceIds?: string[]; maxResults?: number; nextToken?: string; }
-
aws_ec2_control_instance: Manages instance state
interface ControlInstanceOptions { region?: string; instanceId: string; action: 'start' | 'stop' | 'reboot' | 'terminate'; force?: boolean; }
2. S3 Operations
-
aws_s3_list_objects: Lists bucket contents
interface ListObjectsOptions { region?: string; bucket: string; prefix?: string; maxKeys?: number; delimiter?: string; continuationToken?: string; }
-
aws_s3_get_object: Retrieves object content
interface GetObjectOptions { region?: string; bucket: string; key: string; versionId?: string; responseContentType?: string; range?: string; }
3. Lambda Operations
-
aws_lambda_invoke: Invokes Lambda function
interface InvokeLambdaOptions { region?: string; functionName: string; payload?: any; invocationType?: 'RequestResponse' | 'Event' | 'DryRun'; logType?: 'None' | 'Tail'; qualifier?: string; }
-
aws_lambda_list_functions: Lists Lambda functions
interface ListFunctionsOptions { region?: string; functionVersion?: 'ALL' | '$LATEST'; maxItems?: number; marker?: string; }
4. CloudWatch Operations
-
aws_cloudwatch_get_metrics: Retrieves metrics
interface GetMetricsOptions { region?: string; namespace?: string; metricName?: string; dimensions?: Array<{ name: string; value: string; }>; startTime: string | Date; endTime: string | Date; period: number; statistics: Array<'Average' | 'Maximum' | 'Minimum' | 'SampleCount' | 'Sum'>; }
-
aws_cloudwatch_describe_alarms: Lists CloudWatch alarms
interface DescribeAlarmsOptions { region?: string; alarmNames?: string[]; alarmNamePrefix?: string; stateValue?: 'OK' | 'ALARM' | 'INSUFFICIENT_DATA'; maxRecords?: number; nextToken?: string; }
Technical Usage Patterns
Multi-step Resource Management
// List EC2 instances with specific tag
const instances = await tools.aws_ec2_describe_instances({
region: "us-east-1",
filters: [
{
name: "tag:Environment",
values: ["Production"]
},
{
name: "instance-state-name",
values: ["running"]
}
]
});
// Get CloudWatch metrics for these instances
const instanceIds = instances.Reservations.flatMap(r =>
r.Instances.map(i => i.InstanceId)
);
const cpuMetrics = await Promise.all(instanceIds.map(instanceId =>
tools.aws_cloudwatch_get_metrics({
region: "us-east-1",
namespace: "AWS/EC2",
metricName: "CPUUtilization",
dimensions: [{ name: "InstanceId", value: instanceId }],
startTime: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24 hours
endTime: new Date(),
period: 300, // 5-minute intervals
statistics: ["Average"]
})
));
// Identify instances with high CPU usage
const highCpuInstances = cpuMetrics
.filter(metric => {
const datapoints = metric.Datapoints || [];
const recentDatapoints = datapoints.slice(-12); // Last hour
const avgCpu = recentDatapoints.reduce((sum, dp) => sum + dp.Average, 0) /
(recentDatapoints.length || 1);
return avgCpu > 80; // CPU above 80%
})
.map(metric => metric.Dimensions.find(d => d.Name === "InstanceId").Value);
// Scale up instances with high CPU
await Promise.all(highCpuInstances.map(instanceId =>
tools.aws_ec2_control_instance({
region: "us-east-1",
instanceId,
action: "stop"
}).then(() =>
// Update instance type
tools.aws_ec2_modify_instance({
region: "us-east-1",
instanceId,
instanceType: "m5.2xlarge"
})
).then(() =>
// Restart instance
tools.aws_ec2_control_instance({
region: "us-east-1",
instanceId,
action: "start"
})
)
));
Infrastructure as Code Operation
// Retrieve CloudFormation template from S3
const templateObject = await tools.aws_s3_get_object({
bucket: "company-templates",
key: "infrastructure/web-stack.yaml"
});
const templateBody = templateObject.Body.toString('utf-8');
// Create or update CloudFormation stack
const stackResult = await tools.aws_cloudformation_create_stack({
stackName: "web-production",
templateBody,
parameters: [
{
parameterKey: "EnvironmentType",
parameterValue: "Production"
},
{
parameterKey: "InstanceType",
parameterValue: "t3.large"
}
],
capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"],
tags: [
{ key: "Project", value: "WebPlatform" },
{ key: "Environment", value: "Production" }
]
});
// Monitor stack creation progress
const stackId = stackResult.StackId;
let stackStatus = "CREATE_IN_PROGRESS";
while (["CREATE_IN_PROGRESS", "UPDATE_IN_PROGRESS"].includes(stackStatus)) {
await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds
const stackInfo = await tools.aws_cloudformation_describe_stacks({
stackName: stackId
});
stackStatus = stackInfo.Stacks[0].StackStatus;
console.log(`Stack status: ${stackStatus}`);
}
// Get outputs from the stack
const outputs = stackInfo.Stacks[0].Outputs;
const apiEndpoint = outputs.find(o => o.OutputKey === "ApiEndpoint").OutputValue;
const databaseEndpoint = outputs.find(o => o.OutputKey === "DatabaseEndpoint").OutputValue;
Advanced Implementation Considerations
Security and Permissions
AWS MCP implements robust security measures:
-
IAM Policy Evaluation:
class PolicyEvaluator { private readonly iam: IAMClient; constructor(region: string) { this.iam = new IAMClient({ region }); } async evaluateAction(action: string, resource: string): Promise<boolean> { try { const command = new SimulatePrincipalPolicyCommand({ PolicySourceArn: process.env.AWS_ROLE_ARN || "current-user", ActionNames: [action], ResourceArns: [resource] }); const response = await this.iam.send(command); const result = response.EvaluationResults[0]; return result.EvalDecision === "allowed"; } catch (error) { console.error("Policy evaluation error:", error); return false; // Default to denial on error } } }
-
Request Signing and Authentication:
async function getSignedRequest( service: string, region: string, endpoint: string, method: string, body?: any ): Promise<SignedRequest> { // Get credentials const credentials = await credentialsProvider.getCredentials(); // Calculate signature const date = new Date(); const amzDate = date.toISOString().replace(/[:-]|\.\d{3}/g, ''); const datestamp = amzDate.substring(0, 8); // Create canonical request const canonicalHeaders = `host:${endpoint}\nx-amz-date:${amzDate}\n`; const signedHeaders = 'host;x-amz-date'; const payloadHash = crypto .createHash('sha256') .update(body || '') .digest('hex'); const canonicalRequest = [ method, '/', '', canonicalHeaders, signedHeaders, payloadHash ].join('\n'); // Create string to sign const algorithm = 'AWS4-HMAC-SHA256'; const credentialScope = `${datestamp}/${region}/${service}/aws4_request`; const stringToSign = [ algorithm, amzDate, credentialScope, crypto.createHash('sha256').update(canonicalRequest).digest('hex') ].join('\n'); // Calculate signature const signingKey = getSignatureKey( credentials.secretAccessKey, datestamp, region, service ); const signature = crypto .createHmac('sha256', signingKey) .update(stringToSign) .digest('hex'); // Create authorization header const authHeader = [ `${algorithm} Credential=${credentials.accessKeyId}/${credentialScope}`, `SignedHeaders=${signedHeaders}`, `Signature=${signature}` ].join(', '); return { url: `https://${endpoint}/`, method, headers: { 'Host': endpoint, 'X-Amz-Date': amzDate, 'Authorization': authHeader, 'Content-Type': 'application/json' }, body }; }
Cost Control and Resource Boundaries
To prevent unexpected cloud resource costs:
class CostController {
private readonly serviceLimits = {
ec2: {
maxInstances: 5,
allowedTypes: ['t3.micro', 't3.small', 't3.medium']
},
s3: {
maxBucketCount: 3,
maxStorageGB: 100
},
lambda: {
maxConcurrentExecutions: 10,
maxMemoryMB: 512
}
};
async validateEC2Launch(instanceType: string, count: number): Promise<boolean> {
// Check instance type is allowed
if (!this.serviceLimits.ec2.allowedTypes.includes(instanceType)) {
throw new Error(`Instance type ${instanceType} is not allowed`);
}
// Check instance count doesn't exceed limit
const existingInstances = await countRunningInstances();
if (existingInstances + count > this.serviceLimits.ec2.maxInstances) {
throw new Error(`Cannot launch ${count} new instances: would exceed limit of ${this.serviceLimits.ec2.maxInstances}`);
}
// Check estimated cost is within budget
const estimatedCost = await calculateEstimatedCost(instanceType, count);
if (estimatedCost > this.dailyBudget) {
throw new Error(`Estimated cost ${estimatedCost} exceeds daily budget of ${this.dailyBudget}`);
}
return true;
}
// Additional validation methods for other services...
}
Troubleshooting Common Technical Issues
AWS API Throttling Handling
For managing AWS API rate limits:
class ThrottleHandler {
private requestCounts: Map<string, number> = new Map();
private lastReset: number = Date.now();
private readonly resetInterval: number = 60000; // 1 minute in ms
async executeWithThrottling<T>(
serviceKey: string,
operation: () => Promise<T>,
maxAttempts: number = 3
): Promise<T> {
this.updateCounts();
const currentCount = this.requestCounts.get(serviceKey) || 0;
this.requestCounts.set(serviceKey, currentCount + 1);
let attempt = 0;
let lastError;
while (attempt < maxAttempts) {
try {
return await operation();
} catch (error) {
if (error.name === 'ThrottlingException' ||
error.name === 'TooManyRequestsException' ||
error.code === 'RequestLimitExceeded') {
attempt++;
lastError = error;
if (attempt < maxAttempts) {
// Exponential backoff with jitter
const delayMs = Math.min(
1000 * Math.pow(2, attempt) + Math.random() * 100,
30000 // Max 30 seconds
);
await new Promise(resolve => setTimeout(resolve, delayMs));
continue;
}
}
throw error; // Non-throttling error or max attempts reached
}
}
throw lastError;
}
private updateCounts() {
const now = Date.now();
if (now - this.lastReset >= this.resetInterval) {
this.requestCounts.clear();
this.lastReset = now;
}
}
}
Credential Management Issues
For handling credential refresh and rotation:
class CredentialRefresher {
private credentials: AWS.Credentials | null = null;
private expirationTime: number = 0;
private refreshPromise: Promise<AWS.Credentials> | null = null;
async getCredentials(): Promise<AWS.Credentials> {
const now = Date.now();
// If credentials are still valid, return them
if (this.credentials && now < this.expirationTime - 15 * 60 * 1000) {
return this.credentials;
}
// If refresh is in progress, wait for it
if (this.refreshPromise) {
return this.refreshPromise;
}
// Start credential refresh
this.refreshPromise = this.refreshCredentials();
try {
this.credentials = await this.refreshPromise;
// Set expiration time with 15-minute buffer
if (this.credentials.expireTime) {
this.expirationTime = this.credentials.expireTime.getTime();
} else {
// Default to 1 hour if no expiration is set
this.expirationTime = now + 60 * 60 * 1000;
}
return this.credentials;
} finally {
this.refreshPromise = null;
}
}
private async refreshCredentials(): Promise<AWS.Credentials> {
// Implement appropriate credential refresh logic based on
// authentication method (IAM role, STS, etc.)
// Example: Assuming IAM role
if (process.env.AWS_ROLE_ARN) {
const sts = new AWS.STS();
const result = await sts.assumeRole({
RoleArn: process.env.AWS_ROLE_ARN,
RoleSessionName: 'MCP-Session'
}).promise();
return new AWS.Credentials({
accessKeyId: result.Credentials.AccessKeyId,
secretAccessKey: result.Credentials.SecretAccessKey,
sessionToken: result.Credentials.SessionToken,
expireTime: result.Credentials.Expiration
});
}
// Default: Load from environment or shared credentials file
return new AWS.SharedIniFileCredentials({
profile: process.env.AWS_PROFILE || 'default'
});
}
}
Conclusion
AWS MCP servers represent a sophisticated technical bridge between conversational AI and the extensive capabilities of Amazon Web Services. By implementing the Model Context Protocol with AWS's comprehensive API ecosystem, these servers enable AI assistants to securely manage cloud infrastructure, analyze telemetry data, and execute complex operational workflows—all through natural language interaction.
This implementation establishes a foundation for building secure, scalable cloud operations that can be triggered and managed through conversational interfaces. As both MCP and AWS services continue to evolve, we can expect further advancements in security features, service coverage, and integration options.
For developers seeking to extend their AI systems with cloud capabilities, AWS MCP offers a standardized, secure approach that abstracts the complexity of AWS APIs while providing AI assistants with powerful tools to operate within the world's most comprehensive cloud infrastructure platform.