Disclaimer: AWS code samples are example code that demonstrates practical implementations of AWS services for specific use cases and scenarios. These application solutions are not supported products in their own right, but educational examples to help our customers use our products for their applications. As our customer, any applications you integrate these examples into should be thoroughly tested, secured, and optimized according to your business's security standards & policies before deploying to production or handling production workloads.
Enterprises adopting AI agents face a real tension: they want to use the best models from multiple providers (Anthropic Claude on AWS, Google Gemini), but they also need agents to be accessible from their existing productivity tools — which increasingly means Gemini Enterprise. Meanwhile, their data, APIs, and infrastructure live on AWS.
This demo proves that these worlds don't have to be separate. It shows:
-
Model flexibility without lock-in. The same agent framework code runs on Anthropic Claude (via Amazon Bedrock) or Google Gemini — swap the model string, keep everything else. Enterprises can evaluate models side-by-side and switch without rewriting agents.
-
Meet users where they are. Agents deployed on AWS infrastructure are consumed directly from Gemini Enterprise's chat interface — the same UI employees already use for email and documents. No new app to roll out, no new login to remember.
-
Meet workloads where they are. Amazon Bedrock AgentCore Runtime deploys next to your existing AWS workloads — same VPC, same IAM, same region. Agents access internal APIs, databases, and services without crossing network boundaries or building new integrations. The compute runs where the data already lives.
-
Identity travels end-to-end. When a user talks to an agent from Gemini Enterprise or the React frontend, the agent knows who they are — Google Sign-In federated through Amazon Cognito, with email and name in the JWT all the way to the agent. This is the foundation for personalized responses, access control, and audit trails.
-
Open protocols, not proprietary glue. Agents speak A2A (Agent-to-Agent protocol) and consume tools via MCP (Model Context Protocol). These are open standards — any compliant client can talk to any compliant agent, regardless of who built either side.
-
Enterprise-grade infrastructure. Amazon Cognito handles auth, Amazon API Gateway handles routing and rate limiting, Amazon Bedrock AgentCore Runtime handles scaling and session management. No self-managed containers, no custom auth middleware in production.
The result: an enterprise can deploy AI agents on AWS, expose them to thousands of employees through Gemini Enterprise, let each employee be recognized by name, and swap LLM backends without touching the frontend. That's the multi-cloud agent story.
Four agents (2 ADK + 2 Strands Agents) registered in Gemini Enterprise, each backed by a different framework and LLM.
Multi-cloud AI agent interoperability: Agent Development Kit (ADK) and Strands Agents SDK on Amazon Bedrock AgentCore Runtime, exposed via A2A protocol, using two LLM backends (Anthropic Claude via Amazon Bedrock, Google Gemini), with tools served through Amazon Bedrock AgentCore Gateway and direct MCP connections. Consumed by a React frontend and Gemini Enterprise.
User identity (Google Sign-In → Amazon Cognito federation → JWT with email) propagated end-to-end — agents greet users by name.
An employee talks to the Strands Bedrock Agent directly from Gemini Enterprise — the agent is identified, authenticated, and responds using Amazon Bedrock AgentCore tools.
Open docs/architecture.html in a browser for the full interactive diagram.
graph TD
subgraph GCP["☁️ Google Cloud"]
GE["🔷 Gemini Enterprise"]
GAPI["✦ Gemini 2.5 Flash API"]
end
FE["🖥️ React Frontend<br/><small>Google Sign-In → Amazon Cognito PKCE</small>"]
subgraph AWS["☁️ AWS (eu-west-1)"]
subgraph PROXY["Gemini Proxy — Multi-Agent Router"]
AUTH["🔐 Custom Authorizer"]
ROUTER["🔀 AWS Lambda Router<br/><small>/bedrock /gemini /strands-*</small>"]
end
subgraph RT_ADK["Amazon Bedrock AgentCore Runtime — ADK"]
ADK_B["🧠 ADK Bedrock<br/><small>Anthropic Claude Sonnet 4.6</small>"]
ADK_G["🧠 ADK Gemini<br/><small>Gemini 2.5 Flash</small>"]
end
subgraph RT_STRANDS["Amazon Bedrock AgentCore Runtime — Strands Agents"]
ST_B["⚡ Strands Bedrock<br/><small>Anthropic Claude Sonnet 4.6</small>"]
ST_G["⚡ Strands Gemini<br/><small>Gemini 2.5 Flash</small>"]
end
subgraph GW["Amazon Bedrock AgentCore Gateway"]
L1["λ Weather"]
L2["λ Calculator"]
MCP_A["🔌 MCP Server A<br/><small>word_count · summarize · keywords</small>"]
end
subgraph MCP_B["MCP Server B (Direct)"]
T["🌡️ temperature"]
D["📏 distance"]
W["⚖️ weight"]
end
end
FE -->|"A2A · Amazon Cognito JWT (email)"| RT_ADK
FE -->|"A2A · Amazon Cognito JWT (email)"| RT_STRANDS
GE -->|"A2A · Google → Amazon Cognito"| PROXY
PROXY --> RT_ADK
PROXY --> RT_STRANDS
ADK_G -.->|"model invocation"| GAPI
ST_G -.->|"model invocation"| GAPI
RT_ADK -->|MCP| GW
RT_STRANDS -->|MCP| GW
RT_ADK -->|MCP| MCP_B
RT_STRANDS -->|MCP| MCP_B
- AWS account with Amazon Bedrock AgentCore access in eu-west-1
- AWS CDK bootstrapped (
cdk bootstrap aws://<account>/eu-west-1) - Amazon Bedrock model access enabled for Claude Sonnet 4.6 (eu-west-1)
- Active Gemini Enterprise subscription (required for the Gemini Enterprise client integration)
- Google Cloud project with the following APIs enabled: Vertex AI Search & Conversation (Discovery Engine)
- Google API key for Gemini model invocation (used by ADK Gemini and Strands Gemini agents). Generate one at Google AI Studio and save to
.google-api-key(gitignored). - OAuth consent screen configured with test users, and a Google OAuth 2.0 client ID (for Cognito federation)
- Node.js 18+, Python 3.12+, AWS CDK CLI (
npm install -g aws-cdk) - Docker (Colima or Docker Desktop) — for building ARM64 agent container images
uv(Python package manager) — for local agent development
First deploy creates the Amazon Cognito User Pool and M2M client. Subsequent deploys need the M2M client secret.
cd infra
npm install
npm run build
# First deploy — creates Cognito pool + M2M client (no secret needed yet)
AWS_PROFILE=<profile> AWS_REGION=eu-west-1 npx cdk deploy AwsGcpAgentsGateway
# Retrieve the M2M client secret (created by CDK, stored in Cognito)
M2M_SECRET=$(aws cognito-idp describe-user-pool-client \
--user-pool-id $(aws cloudformation describe-stacks --stack-name AwsGcpAgentsGateway \
--query 'Stacks[0].Outputs[?OutputKey==`UserPoolId`].OutputValue' --output text) \
--client-id $(aws cloudformation describe-stacks --stack-name AwsGcpAgentsGateway \
--query 'Stacks[0].Outputs[?OutputKey==`MachineClientId`].OutputValue' --output text) \
--query 'UserPoolClient.ClientSecret' --output text)
# Deploy all stacks (pass M2M secret + Google API key)
bash ../scripts/bundle-agent.sh # Copy shared/ into each agent dir
AWS_PROFILE=<profile> AWS_REGION=eu-west-1 npx cdk deploy --all \
-c clientSecret=$M2M_SECRET \
-c googleApiKey=$(cat ../.google-api-key) \
-c deployStrands=true \
--require-approval neverNote: The M2M client secret is auto-generated by Amazon Cognito when CDK creates the client. It's passed as a CDK context parameter (
-c clientSecret=...) because CDK cannot export secrets as stack outputs. Store it securely — e.g. in.aws-profilealongside your AWS profile (both gitignored).
Stacks deployed:
| Stack | What |
|---|---|
AwsGcpAgentsGateway |
Amazon Cognito User Pool (Google IdP, Pre-Token V2 trigger), Amazon Bedrock AgentCore Gateway + AWS Lambda tools, frontend client |
AwsGcpAgentsRuntime |
4 Amazon Bedrock AgentCore Runtimes (ADK + Strands Agents, Bedrock + Gemini), Docker ARM64 |
AwsGcpAgentsMcpServers |
MCP Server A (text analysis) + MCP Server B (unit conversion) on Amazon Bedrock AgentCore |
AwsGcpAgentsGeminiProxy |
Amazon API Gateway + AWS Lambda proxy with multi-agent routing + custom authorizer |
npx cdk deploy AwsGcpAgentsGatewayWiring \
-c clientSecret=<secret> \
-c serverAArn=<mcp-server-a-arn>Agents are registered via the Discovery Engine REST API. Each agent needs its own authorization resource (OAuth, one per agent — cannot be shared).
# Set variables
PROJECT=<gcp-project-id>
ENGINE=<gemini-enterprise-app-id>
TOKEN=$(gcloud auth print-access-token)
BASE=https://global-discoveryengine.googleapis.com/v1alpha/projects/$PROJECT/locations/global
# Create authorization resource (one per agent)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -H "x-goog-user-project: $PROJECT" \
"$BASE/authorizations?authorizationId=<auth-id>" \
-d '{
"serverSideOauth2": {
"clientId": "<cognito-auth-code-client-id>",
"clientSecret": "<cognito-auth-code-client-secret>",
"tokenUri": "https://<cognito-domain>/oauth2/token",
"authorizationUri": "https://<cognito-domain>/oauth2/authorize?response_type=code&scope=openid+email+profile+aws-gcp-agents-gateway%2Fread+aws-gcp-agents-gateway%2Fwrite&redirect_uri=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Foauth-redirect&identity_provider=Google"
}
}'
# Register agent
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -H "x-goog-user-project: $PROJECT" \
"$BASE/collections/default_collection/engines/$ENGINE/assistants/default_assistant/agents" \
-d '{
"displayName": "Agent Display Name",
"description": "Agent description",
"a2aAgentDefinition": {
"jsonAgentCard": "<escaped agent card JSON with url pointing to API Gateway route>"
},
"authorizationConfig": {
"agentAuthorization": "projects/<project-number>/locations/global/authorizations/<auth-id>"
}
}'
# List agents
curl -s -H "Authorization: Bearer $TOKEN" -H "x-goog-user-project: $PROJECT" \
"$BASE/collections/default_collection/engines/$ENGINE/assistants/default_assistant/agents"API Gateway routes → Agents:
| Route | Agent |
|---|---|
/ (default) |
Strands Bedrock (Claude Sonnet 4.6) |
/bedrock/ |
ADK Bedrock (Claude Sonnet 4.6) |
/gemini/ |
ADK Gemini (Gemini 2.5 Flash) |
/strands-gemini/ |
Strands Gemini (Gemini 2.5 Flash) |
cd frontend
npm install
npm run dev # http://localhost:3000Click Sign in → Google Sign-In → Amazon Cognito federation → JWT with email/name → agents greet you by name. Sign-in is required to interact with agents.
src/
├── agents/
│ ├── shared/ # Shared agent code (both ADK + Strands)
│ │ ├── agent_core.py # ADK: tools, instruction, IdentityMiddleware, /ping
│ │ ├── strands_agent_core.py # Strands: MCP client, A2A server, identity
│ │ ├── gateway_tools.py # Amazon Bedrock AgentCore Gateway MCP toolset
│ │ └── direct_mcp_tools.py # Direct MCP Server B toolset
│ ├── bedrock-agent/ # ADK Bedrock (thin wrapper)
│ ├── gemini-agent/ # ADK Gemini (thin wrapper)
│ ├── strands-bedrock-agent/ # Strands Bedrock (native BedrockModel)
│ └── strands-gemini-agent/ # Strands Gemini (native GeminiModel)
├── mcp-server-gateway/ # MCP Server A: word_count, summarize, keywords
├── mcp-server-direct/ # MCP Server B: temperature, distance, weight
├── lambda-tools/ # AWS Lambda functions (weather, calculator)
└── gemini-proxy/ # AWS Lambda proxy + custom authorizer
infra/
├── bin/app.ts # CDK app entry
└── lib/ # AWS CDK stacks (gateway, runtime, mcp, proxy)
frontend/ # React + Vite + Tailwind
docs/ # Architecture diagram (HTML)
scripts/ # bundle-agent.sh, test-deployed-agent.sh
- Identity propagation: Pre-Token-Generation V2 AWS Lambda trigger injects email/name into Amazon Cognito access tokens. AWS CDK escape hatch forces
V2_0(CDK defaults toV1_0).requestHeaderConfigurationallowlistsAuthorizationheader on Amazon Bedrock AgentCore Runtimes. ASGIIdentityMiddlewaredecodes JWT, stores incontextvars.get_current_usertool reads identity. - A2A protocol: JSON-RPC 2.0 over HTTP. Agent card at
/.well-known/agent.json. Health check at/ping({"status": "Healthy", "time_of_last_update": <epoch>}). - Static agent cards: Passed to
to_a2a(agent_card=path)to skip MCP tool enumeration at startup (30s init limit on Amazon Bedrock AgentCore). - MCP on Amazon Bedrock AgentCore:
terminate_on_close=Falserequired. Auth headers inStreamableHTTPConnectionParams.headers, notheader_provider. - Docker: ARM64 platform for Amazon Bedrock AgentCore Runtime.
scripts/bundle-agent.shcopiesshared/into each agent dir before AWS CDK deploy. - Multi-agent routing: Amazon API Gateway routes (
/bedrock,/gemini,/strands-bedrock,/strands-gemini) → AWS Lambda proxy resolves target agent ARN from path prefix.
This is a sample application for educational purposes. The code is intentionally kept simple to focus on demonstrating multi-cloud agent interoperability patterns. For production deployments, you should:
- IAM policies: The sample uses broad permissions (
bedrock-agentcore:*,Resource: "*") for simplicity. In production, scope IAM policies to specific resource ARNs and use least-privilege actions. - Amazon Bedrock Guardrails: No content filtering, prompt attack protection, or PII detection is configured. Production deployments should enable Amazon Bedrock Guardrails for content safety.
- Amazon API Gateway: Access logging and AWS WAF are not enabled. Production APIs should enable Amazon CloudWatch access logging, AWS WAF, and request throttling.
- Secrets management: All sensitive credentials (Cognito M2M secret, Google API key) are stored in AWS Secrets Manager and fetched at runtime via boto3. See SECURITY.md for details on the CDK synthesis caveat and production upgrade path.
This demo uses public endpoints for cross-cloud communication (Gemini API calls from AWS, A2A traffic from Gemini Enterprise). For production deployments requiring private connectivity between AWS and Google Cloud, AWS Interconnect – multicloud (public preview) provides fully managed, private Layer 3 connectivity — provisioned in minutes, MACsec encrypted, with traffic flowing entirely on AWS and Google Cloud private backbones. See the announcement blog post and reference architectures.
Alternatives include AWS Site-to-Site VPN + Google Cloud HA VPN (lower throughput, simpler setup) or Google Cloud Cross-Cloud Interconnect via shared colocation facilities.