cd /news/ai-agents/productionizing-agentic-genai-on-aws… · home topics ai-agents article
[ARTICLE · art-133226] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Productionizing Agentic GenAI on AWS: What Actually Breaks When You Scale MCP

A developer detailed the operational failures that surface when moving agentic GenAI and MCP servers from local demos to production on AWS, arguing that containerizing MCP servers on ECS or EKS inherits unnecessary Kubernetes overhead for mostly request/response workloads. The writeup recommends Amazon Bedrock AgentCore Runtime for serverless agent hosting and AgentCore Gateway for exposing existing APIs as governed MCP tools, plus per-tool IAM scoping, session-level cost tagging, gateway rate limits, and decision-path tracing to catch silent agent loops that only show up on the invoice.

by read6 min views4 publishedSep 18, 2026

Every MCP demo looks the same. Someone spins up a local server, points a client at it, watches a tool call succeed once, and calls the architecture done. Then the business asks when it ships, and suddenly you're dealing with non-deterministic compute, IAM roles that need to be scoped per tool instead of per service, and a token bill that can move faster than anyone's watching it.

This is what that transition looks like in practice, and what tends to get skipped when implementing MCP in production.

Containerizing the MCP server and running it on ECS or EKS is the default move, because that's what "production" means on most platform teams. It works, but you inherit Kubernetes' entire operational surface: autoscaling tuned for a workload that doesn't scale in any predictable pattern, IAM role assumption per agent session, all of it. And this is for something that's mostly request/response with an occasional long-running tool call in the middle.

Two AWS-native paths are worth trying first.

Amazon Bedrock Agents is the original managed agent service, and a reasonable fit if your use case matches its orchestration and action-group model.

Amazon Bedrock AgentCore is the newer suite and the more relevant one here, though it's worth being precise about what's actually inside it since the naming gets muddled in most blog posts written about it. AgentCore Runtime is the serverless compute layer that hosts your agent or MCP server code so you're not managing containers. AgentCore Gateway solves a different problem: it's a managed front door that turns existing APIs, Lambda functions, and OpenAPI or Smithy specs into MCP-compatible tools, and can front other agents through passthrough targets, aggregating and securing access to whatever's already running rather than hosting anything new itself. If the goal is to stop managing containers, that's Runtime. If the goal is exposing ten internal APIs as one governed MCP endpoint without hand-writing a server for each, that's Gateway. It's an easy pair to mix up on a first read.

Whichever one fits, put it in Terraform, mainly because "which IAM role got attached to which agent in which environment" is a question you don't want to answer by clicking through the console six months from now, after whoever set it up has moved teams.

A 500, a stack trace, a page in the middle of the night: that's usually how you find out something broke. Agents don't give you that. One that gets stuck retrying, or decides to re-check its own output a few times, or loops back through the same tool call, won't throw an error for any of it. It just spends money, and you find out when the invoice lands, roughly a month too late to do anything about it.

A few things are worth the setup cost. Tagging at the session or tenant level, not just the service level, matters because "cost per API call" tells you nothing when the real unit of spend is "cost per agent decision" — you want to answer which customer, which run, and which day, without stitching together six log tables to get there.

Rate limiting deserves more attention than teams usually give it. Put real limits on the gateway in front of the MCP server, not aspirational ones. A burst limit of 20 and a sustained rate of 10 requests per second looks overly cautious right up until an agent stuck in a loop hits it four seconds into a session, at which point it's the only thing standing between you and a much worse morning.

And trace the decision path, not just the request path. X-Ray and CloudWatch will hand you latency numbers and error rates, but the thing worth seeing is why the agent called tool B right after tool A. Without that, "the agent got expensive" stays a mystery instead of turning into a fix.

The moment an agent can write instead of just read, being wrong stops meaning bad output and starts meaning modified infrastructure. That's the point where a bug goes from embarrassing to expensive.

Scope IAM roles per tool, not per agent. A tool built to query one table shouldn't ride on a role that also happens to reach S3 or security groups, no matter how tempting it is to reuse one role across five tools to save setup time. Authenticate before any of this fires; Cognito in front of the MCP endpoint is unglamorous, and it's still the right call. And put a guardrail layer, Bedrock Guardrails or something equivalent, between the model and anything sensitive. Prompt injection aimed at getting an agent to leak data or push an unauthorized change is already one of the more common ways these systems get abused.

This covers only the identity and routing skeleton. Deployment, stage, method, and usage-plan-to-API-key resources are left out on purpose, to keep the snippet focused on the pieces that matter for the argument above.

resource "aws_iam_role" "mcp_agent_role" {
  name = "mcp-agent-execution-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "bedrock.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "mcp_tool_s3_read" {
  name = "mcp-tool-s3-read-policy"
  role = aws_iam_role.mcp_agent_role.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:GetObject", "s3:ListBucket"]
      Resource = [
        "arn:aws:s3:::mcp-agent-data-source",
        "arn:aws:s3:::mcp-agent-data-source/*"
      ]
    }]
  })
}

resource "aws_api_gateway_rest_api" "mcp_api" {
  name        = "mcp-gateway"
  description = "API Gateway for MCP agent execution"
}

resource "aws_api_gateway_authorizer" "cognito" {
  name            = "mcp-cognito-authorizer"
  rest_api_id     = aws_api_gateway_rest_api.mcp_api.id
  type            = "COGNITO_USER_POOLS"
  provider_arns   = [aws_cognito_user_pool.mcp_pool.arn]
  identity_source = "method.request.header.Authorization"
}

resource "aws_api_gateway_usage_plan" "mcp_usage_plan" {
  name        = "mcp-standard-tier"
  description = "Throttling and daily quota for MCP agent invocations"

  api_stages {
    api_id = aws_api_gateway_rest_api.mcp_api.id
    stage  = aws_api_gateway_stage.prod.stage_name
  }

  quota_settings {
    limit  = 1000
    offset = 0
    period = "DAY"
  }

  throttle_settings {
    burst_limit = 20
    rate_limit  = 10
  }
}

One thing worth flagging before you copy this: if you need to track spend per tenant, this snippet alone won't get you there. A usage plan without an aws_api_gateway_usage_plan_key tied to an actual API key enforces its limits at the stage level, not per client. Add an API key per tenant and attach it to the plan if that attribution is a real requirement and not just something that'd be nice to have.

It's the same stuff you'd already do for any service and infrastructure: scope access tightly, put limits in front of it, trace what it's doing, make sure it's authenticated. The only reason it feels like a bigger deal here is that when it goes wrong, nothing pages you. You just get a bill, or a change you didn't approve, weeks after the fact. Put the guardrails in before that happens, not after you've already found out the hard way.

── more in #ai-agents 4 stories · sorted by recency
── more on @aws 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/productionizing-agen…] indexed:0 read:6min 2026-09-18 ·