Amazon Bedrock AgentCore: Build Interactive MCP Apps with Sandboxed HTML Widgets

Build interactive MCP Apps using Amazon Bedrock AgentCore

You ask an assistant, “Can you show all unicorns?” and instead of a paragraph you get a grid of cards: photos, prices, an available-from date, and a clickable Book button that starts a transaction without leaving the chat. That’s the core idea behind MCP Apps on Amazon Bedrock AgentCore, letting conversational hosts render rich HTML widgets rather than plain text, while your back-end keeps owning the business logic.

What you’re looking at (short roadmap)

The pattern is intentionally simple: a thin Model Context Protocol (MCP) adapter exposes tools and widget resources to AI hosts, and your business logic and state stay in your existing services. The sample implementation, “Unicorn Rentals, ” shows browse, book, view bookings, and return flows. It’s a learning demo, useful for experimentation, not a production-ready product.

How MCP Apps deliver interactive widgets

  • AI hosts discover available operations by calling MCP endpoints like tools/list and resources/list.
  • When the host invokes a tool (tools/call), the MCP adapter returns structuredContent (a JSON payload the widget consumes) and may include a pointer to a widget via tool._meta.ui.resourceUri (a URL the host fetches with resources/read).
  • The host fetches that resource (resources/read) and renders the returned HTML inside a sandboxed iframe. The host injects structuredContent into the widget so the UI is driven by server-provided data rather than opaque text.

In short: tools expose actions, resources expose widget HTML, structuredContent carries the data, and _meta.ui.resourceUri tells the host where to fetch the widget.

Architecture and the role of AgentCore

The sample positions a lightweight AgentCore runtime (an Express.js TypeScript app built on @modelcontextprotocol/sdk and @modelcontextprotocol/ext-apps) inside an AgentCore runtime (Node.js 22 environment). An AgentCore Gateway exposes a single managed endpoint to external AI hosts and invokes the runtime.

For the demo the Gateway accepts inbound requests without authentication as a convenience. Mark that clearly as a demo-only pattern. For production you should require inbound authentication (API keys, mTLS, JWT or similar). The Gateway typically invokes the runtime using an IAM execution role with SigV4-signed requests, and the runtime should enforce invocation only from the Gateway via a resource-based policy.

Business logic remains outside the MCP layer. The Unicorn Rentals sample delegates operations to an AWS Lambda that persists data in Amazon DynamoDB. Static widget assets (images, CSS, JS) are served via Amazon CloudFront with S3 as origin. That separation keeps the MCP adapter a reusable protocol shim while existing services retain ownership of data, transactions, and business rules.

What the sample implements

  • Sample app: Unicorn Rentals (repo: https://github.com/aws-samples/sample-agentcore-mcp-apps).
  • MCP tools in the demo: list_unicorns, book_unicorn, view_bookings, return_unicorn.
  • MCP resources used as example widgets: unicorn-list and booking-confirmation.
  • Runtime: AgentCore runtime (NODE_22 environment); MCP adapter implemented with Express.js and TypeScript.
  • Backend: AWS Lambda for business logic, DynamoDB for persistence, CloudFront + S3 for static assets.

Deploying the sample, checklist and commands

  • AWS account with permissions to deploy the stack (CloudFormation/CDK, ECR, Lambda, DynamoDB, S3, CloudFront, IAM, API Gateway/AgentCore). Follow the repo’s IAM guidance before running deployment scripts.
  • Install Node.js 22+ locally (used for builds and aligns with the runtime NODE_22 requirement).
  • AWS CLI v2 and AWS CDK CLI installed and configured (ensure your AWS profile has the needed permissions and region set).
  • Clone and deploy the sample:
    git clone https://github.com/aws-samples/sample-agentcore-mcp-apps.git
    cd sample-agentcore-mcp-apps
    bash deploy.sh

    Note: inspect the scripts and CDK stacks before running. Deploy scripts can be interactive and may create AWS resources that incur charges.

  • To remove resources:
    cd infrastructure/cdk
    npx cdk destroy
  • Deployment outputs include a GatewayResourceUrl you use to register the host plugin/connector (ChatGPT plugin manifest, Claude connector, etc.).

Security, observability, and production hardening

Samples often relax security for clarity. Treat the Unicorn Rentals repo as an experiment and apply production controls before exposing any user traffic.

  • Authentication and least privilege: Require authenticated inbound access to the Gateway (API keys, mutual TLS, JWT). Enforce least-privilege IAM for runtime invocation and limit which principals can call the runtime via resource-based policies.
  • Input validation and server-side enforcement: Validate tool arguments with strict JSON schemas at the MCP boundary and re-validate in your business Lambda before making state changes. Never trust client-side validation in widget HTML.
  • Edge protections: Put AWS WAF in front of the Gateway for IP allowlisting, managed threat-detection rules, and rate limiting.
  • Widget sandboxing and host differences: Hosts render widget HTML in sandboxed iframes, but sandbox rules, CSP enforcement, allowed network egress, and caching semantics vary by host. Test each target host rather than assuming uniform behavior.
  • Observability and tracing: Monitor Gateway request rates, 4xx/5xx counts, request latency (P50/P95/P99), runtime container health (start time, CPU/memory), Lambda invocations and durations, and DynamoDB throttling. Correlate traces end-to-end with X-Ray or OpenTelemetry and propagate trace IDs through the full call path.
  • Responsible AI: Validate inputs, filter or redact sensitive data crossing trust boundaries, and use content-filtering/guardrail tools such as Amazon Bedrock Guardrails where appropriate.

Cost and scaling guidance

AgentCore runtime billing depends on several factors, including container runtime hours, invocations, network transfer, and any Gateway or data transfer fees. Pricing details change; consult the official AgentCore pricing page before budgeting. A practical approach is to run a staged load test, measure peak concurrent sessions and average container CPU/memory, then estimate container runtime cost plus Lambda invocations and DynamoDB capacity based on observed metrics.

Right-size container CPU and memory, configure Lambda concurrency limits, and use DynamoDB autoscaling or appropriate provisioned capacity to avoid throttling. Use load tools (k6, vegeta) to simulate expected traffic patterns and measure cold starts, latency percentiles, and DB contention.

Host testing checklist (practical tests to run)

  • Load the widget and validate postMessage handshake between host and widget (if used).
  • Confirm iframe sandbox attributes and Content Security Policy, test whether scripts run, whether network calls are allowed, and whether storage access is blocked.
  • Measure caching and revalidation behavior: how long will hosts cache tools/resources metadata or widget HTML, and how are updates propagated?
  • Test fallback UX: return 500 from resources/read and verify the host falls back to a textual response or an error card.
  • Verify session isolation: ensure structuredContent for one user cannot be read by another session.
  • Run load tests across Gateway → runtime → Lambda → DynamoDB and watch for throttles and latency spikes.

Developer ergonomics and reuse

Keep the MCP adapter focused on the protocol and widget registration (registerAppTool / registerAppResource via the @modelcontextprotocol SDK). All business logic and persistent data remain in existing services (the Lambda + DynamoDB in the sample). That lets you expose the same tools and widget resources to multiple AI hosts (ChatGPT, Claude, or others that support MCP Apps) without duplicating back-end code.

“Browse unicorns, book rentals, view active bookings, and return unicorns. Shows rich UI cards with pricing and booking confirmations.”

Example prompts used in the demo include: “Can you show all unicorns?”, “I would like to book Stardust unicorn”, “Show me my unicorn bookings”, and “I would like to return my unicorn”. Those map to the demo’s MCP tools and return structuredContent + widget resource pointers the host renders.

Caveats and open questions to explore before production

  • Host fragmentation: MCP Apps aims for host-agnostic delivery, but hosts differ, sandboxing, caching, allowed widget capabilities, and manifest requirements vary. Maintain a compatibility matrix and CI tests for each host you support.
  • Caching and invalidation: Confirm how long hosts cache resources and whether you must version widgets or emit cache-control hints to force updates.
  • Operational limits: Clarify AgentCore runtime and Gateway quotas (concurrent sessions, request rates) for your scale plan.
  • Error handling and UX fallbacks: Design clear textual fallbacks for widget load failures and implement retries and circuit breakers between runtime and back ends.
  • Compliance and data residency: Ensure logging, audit trails, and data residency controls meet regulatory requirements for your customers.

Where to look next

Find the sample repository at https://github.com/aws-samples/sample-agentcore-mcp-apps. Expect an infrastructure/CDK stack and application code split; review IAM policies the repo uses before deployment and adapt them to least-privilege for your environment.

This work and sample were prepared by Dantis Stephen (Senior Solutions Architect, Data and AI, AWS UK) and Babs Khalidson (Machine Learning Engineer, AWS Generative AI Innovation Centre, London).

Key questions (and short answers)

  • How do AI hosts render interactive widgets from an MCP App?

    Hosts call tools/call to invoke an action; the MCP adapter returns structuredContent and includes a widget pointer in tool._meta.ui.resourceUri. The host fetches that URL via resources/read and renders the returned HTML inside a sandboxed iframe, injecting structuredContent into the widget.

  • Where should my business logic live?

    Keep business logic and state in your existing services. The MCP adapter should act only as a protocol shim (tool/resource registration and validation). The sample delegates logic to an AWS Lambda that persists data in DynamoDB.

  • Is the sample production-ready out of the box?

    No. Treat the repo as a learning example. Add hardened inbound auth, strict IAM, CSP and sandbox verification per host, end-to-end tracing, rate limits, and comprehensive input re-validation before going live.

  • How should I protect users from unsafe content or leaked PII?

    Validate inputs with strict schemas at the MCP boundary and re-validate server-side. Apply content filters and guardrails (for example, Amazon Bedrock Guardrails) to redact or block risky content crossing the trust boundary.