AWS Bedrock AgentCore
Set up Solo Enterprise for agentregistry on an EKS cluster and deploy agents and MCP servers to AWS Bedrock AgentCore with VPC networking through a managed agentgateway.
Solo Enterprise for agentregistry is an enterprise-grade AI artifact platform to manage the lifecycle of your AI artifacts, including AI agents, MCP tool servers, skills, and more. With Solo Enterprise for agentregistry, you can easily orchestrate deployments across runtimes, and monitor the efficiency and cost of your AI deployments. The product builds on the open-source agentregistry project. To learn more about Solo Enterprise for agentregistry, see About.
About this guide
This guide walks you through the full setup of Solo Enterprise for agentregistry, from authentication to deploying your first AI agent and MCP server to AWS Bedrock AgentCore with VPC networking. By the end, you have:
- A Keycloak identity provider that runs in your cluster and is configured with the realm, clients, and users that Solo Enterprise for agentregistry requires for OIDC authentication.
- An Solo Enterprise for agentregistry control plane that is secured with OIDC authentication and backed by PostgreSQL and ClickHouse.
- The
arctlCLI that can authenticate with Solo Enterprise for agentregistry so that you can get started with managing and deploying AI artifacts. - The Solo Enterprise for agentregistry UI that you can use to browse and manage AI artifacts in the registry catalog.
- A connection from the registry to AWS Bedrock AgentCore, using either IAM user credentials or EKS Pod Identity.
- A managed gateway in your VPC that routes all agent and MCP server traffic and enforces access policies.
- A Python ADK agent that uses the AWS Bedrock Claude provider. You build and publish the agent to the registry catalog. Then, you use Solo Enterprise for agentregistry to deploy the agent to AWS Bedrock AgentCore inside your VPC.
- A source-built MCP server registered in the registry catalog and deployed to AWS Bedrock AgentCore inside the same VPC, so the agent can reach it over the private network.
The following diagram shows the components that you set up and how they relate to each other.
Before you begin
Install the following CLIs.
- helm: Install the Solo Enterprise for agentregistry Helm chart.
- kubectl: Interact with your Kubernetes cluster.
- aws: Interact with AWS Bedrock AgentCore and your EKS cluster.
- eksctl: Create the IAM service account for the AWS Load Balancer Controller.
- jq: Used as a script wrapper in this guide.
- openssl: Generate a unique private key for JWT signing. Install with
brew install openssl.
Create or use an existing AWS VPC. You deploy your AWS Bedrock AgentCore runtimes, managed gateway, and EKS cluster into this VPC to allow secure private networking between agents and MCP servers. Make sure that your VPC has the following configuration:
- At least one private subnet.
- A security group that allows inbound traffic from within the VPC. The default security group in the VPC is sufficient for this guide.
Save the networking details of your VPC as environment variables.
Set the VPC ID and AWS region.
export VPC_ID=<vpc-ID> export AWS_REGION=<aws-region>Retrieve the private subnet IDs from the VPC. Private subnets are identified by
map-public-ip-on-launch=false. The gateway that you later deploy in this guide uses a single subnet. The agent and MCP server runtime deployments however use all available private subnets for redundancy.# Single private subnet for the gateway export SUBNET_ID=$(aws ec2 describe-subnets \ --region "${AWS_REGION}" \ --filters "Name=vpc-id,Values=${VPC_ID}" "Name=map-public-ip-on-launch,Values=false" \ --query 'Subnets[0].SubnetId' \ --output text) # All private subnets formatted as YAML for Deployment specs export SUBNET_IDS_YAML=$(aws ec2 describe-subnets \ --region "${AWS_REGION}" \ --filters "Name=vpc-id,Values=${VPC_ID}" "Name=map-public-ip-on-launch,Values=false" \ --query 'Subnets[].SubnetId' \ --output json | jq -r '.[] | " - " + .') echo "Gateway subnet: ${SUBNET_ID}" echo "Agent and MCP server deployment subnets:" echo "${SUBNET_IDS_YAML}"Retrieve the default security group ID for the VPC.
export SG_ID=$(aws ec2 describe-security-groups \ --region "${AWS_REGION}" \ --filters "Name=vpc-id,Values=${VPC_ID}" "Name=group-name,Values=default" \ --query 'SecurityGroups[0].GroupId' \ --output text) echo "SG_ID: ${SG_ID}"
Create or use an existing Amazon EKS cluster and save the name in an environment variable. Make sure that the cluster is deployed into the VPC that you created earlier. You use the EKS cluster to install Solo Enterprise for agentregistry. For more information about how to create the cluster, see the EKS documentation.
export EKS_CLUSTER_NAME=<your-eks-cluster-name>Install the AWS Load Balancer Controller in your EKS cluster. Keycloak and the OpenTelemetry collector are both exposed as
LoadBalancerservices.Associate an OIDC provider with your cluster. This is required to use IAM Roles for Service Accounts (IRSA), which the controller uses to call AWS APIs.
eksctl utils associate-iam-oidc-provider \ --region $AWS_REGION \ --cluster $EKS_CLUSTER_NAME \ --approveDownload the IAM policy that grants the controller the permissions it needs.
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.11.0/docs/install/iam_policy.json aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document file://iam_policy.jsonCreate the IAM service account and attach the policy.
eksctl create iamserviceaccount \ --cluster=$EKS_CLUSTER_NAME \ --namespace=kube-system \ --name=aws-load-balancer-controller \ --role-name AmazonEKSLoadBalancerControllerRole \ --attach-policy-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:policy/AWSLoadBalancerControllerIAMPolicy \ --approve \ --region $AWS_REGIONInstall the controller with Helm.
helm repo add eks https://aws.github.io/eks-charts helm repo update eks helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=$EKS_CLUSTER_NAME \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controllerVerify that the controller is running.
kubectl -n kube-system rollout status deployment/aws-load-balancer-controller
Enable AWS Outbound Identity Federation at the account level. The managed gateway that is provisioned as part of this guide requires this feature to validate JWT tokens from Keycloak via STS. To enable it, open the IAM Identity Center console and follow the prompts, or contact your AWS account administrator.
Step 1: Configure Keycloak
Solo Enterprise for agentregistry does not bundle its own identity provider. Instead, it relies on an external OIDC provider to authenticate users, issue access tokens, and enforce role-based access control. In this quickstart, you use Keycloak as your OIDC provider.
Before you deploy Keycloak, you configure it by creating a ConfigMap that defines the realm, clients, protocol mappers, groups, and users that Solo Enterprise for agentregistry requires. Keycloak reads this file at startup and automatically creates the following resources, so you do not have to configure them manually through the Keycloak UI.
| Resource | Name | Description |
|---|---|---|
| Realm | agentregistry | A dedicated Keycloak realm that isolates agentregistry’s identity configuration from other applications. The registry server uses the realm’s issuer URL to discover OIDC endpoints. |
| Client | ar-backend | A confidential client that is used by the registry server to validate access tokens that are issued by the CLI and UI clients. |
| Client | ar-cli-interactive | A public client for the arctl CLI. The client uses the device authorization grant (RFC 8628), which lets users log in from a terminal by opening a printed URL in a browser. |
| Client | ar-cli-password | A public client for scripted user authentication in CI/CD pipelines. Uses the password credentials grant with a username and password. |
| Client | ar-ui | A public client for the browser-based registry UI. This client uses the authorization code flow with PKCE. The redirectUris and webOrigins are set to * for local testing. In production deployments, these URLs must be replaced with the origin that your UI uses. |
| Protocol mapper | groups | An oidc-group-membership-mapper that writes the user’s Keycloak group memberships into the Groups JWT claim. The registry reads this claim to determine RBAC roles. The claim is added to access tokens that are issued for the ar-cli-interactive, ar-cli-password, and ar-ui clients. |
| Protocol mapper | ar-backend-audience | An oidc-audience-mapper that adds ar-backend to the aud claim of every access token. Without the correct aud claim, the registry server rejects tokens with a 401 Unauthorized error, even when the user’s groups claim is correct, because the aud claim must match the backend client ID. The claim is added to access tokens that are issued for the ar-cli-interactive, ar-cli-password, and ar-ui clients. |
| Group | admins | A group that grants full admin access in the registry. Assigned to admin-user so they have superuser rights on first login. |
| User | admin-user | A test admin user with the password password, assigned to the admins group. |
Create the Keycloak namespace.
kubectl create namespace keycloakCreate the ConfigMap with your Solo Enterprise for agentregistry OIDC configuration.
kubectl apply -f- <<EOF apiVersion: v1 kind: ConfigMap metadata: name: keycloak-agentregistry-realm namespace: keycloak data: agentregistry-realm.json: | { "realm": "agentregistry", "enabled": true, "accessTokenLifespan": 86400, "ssoSessionIdleTimeout": 86400, "ssoSessionMaxLifespan": 86400, "clients": [ { "clientId": "ar-backend", "enabled": true, "publicClient": false, "standardFlowEnabled": false, "directAccessGrantsEnabled": false }, { "clientId": "ar-cli-interactive", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": false, "attributes": { "oauth2.device.authorization.grant.enabled": "true" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-cli-password", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": true, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-ui", "enabled": true, "publicClient": true, "standardFlowEnabled": true, "directAccessGrantsEnabled": false, "redirectUris": ["*"], "webOrigins": ["*"], "attributes": { "pkce.code.challenge.method": "S256" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] } ], "groups": [ {"name": "admins"} ], "users": [ { "username": "admin-user", "email": "admin-user@example.com", "firstName": "Admin", "lastName": "User", "enabled": true, "credentials": [ {"type": "password", "value": "password", "temporary": false} ], "groups": ["admins"] } ] } EOF
Step 2: Deploy Keycloak
With the realm configuration in place, you can now deploy Keycloak itself. This step creates the Keycloak pod and exposes it through a load balancer service so that both the registry server and the arctl CLI can reach it. The --import-realm flag instructs Keycloak to load the ConfigMap from Step 1 on startup to apply all the realm, client, and user configuration automatically.
Create the Keycloak deployment and service from the configuration in your ConfigMap.
kubectl apply -f- <<EOF apiVersion: v1 kind: Service metadata: name: keycloak namespace: keycloak labels: app: keycloak spec: ports: - name: http port: 8080 targetPort: 8080 selector: app: keycloak type: LoadBalancer --- apiVersion: apps/v1 kind: Deployment metadata: name: keycloak namespace: keycloak labels: app: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak spec: containers: - name: keycloak image: quay.io/keycloak/keycloak:26.1.3 args: ["start-dev", "--import-realm"] env: - name: KEYCLOAK_ADMIN value: "admin" - name: KEYCLOAK_ADMIN_PASSWORD value: "admin" - name: PROXY_ADDRESS_FORWARDING value: "true" - name: KC_PROXY value: "edge" ports: - name: http containerPort: 8080 readinessProbe: httpGet: path: /realms/master port: 8080 volumeMounts: - name: realm-config mountPath: /opt/keycloak/data/import volumes: - name: realm-config configMap: name: keycloak-agentregistry-realm EOFWait for the Keycloak rollout to finish.
kubectl -n keycloak rollout status deploy/keycloakSet the Keycloak endpoint details from the load balancer service. These environment variables are used throughout the rest of this guide. The
KEYCLOAK_ISSUERis the OIDC discovery URL that the registry uses to find the token and JWKS endpoints.KEYCLOAK_URLis the base URL that is needed to retrieve thear-backendclient secret in the next step.export ENDPOINT_KEYCLOAK=$(kubectl -n keycloak get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}'):8080 export HOST_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f1) export PORT_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f2) export KEYCLOAK_URL=http://${ENDPOINT_KEYCLOAK} export KEYCLOAK_REALM=agentregistry export KEYCLOAK_ISSUER=$KEYCLOAK_URL/realms/${KEYCLOAK_REALM} echo $KEYCLOAK_ISSUERGet the client secret that Keycloak automatically generated for the
ar-backendclient. The registry server uses this secret to verify access tokens with Keycloak. After a user logs in with an access token, the registry server calls Keycloak’s introspection endpoint by using thear-backendclient secret to confirm that the token is valid and has not been tampered with. You pass this secret to the Solo Enterprise for agentregistry Helm chart in the next step.export KEYCLOAK_TOKEN=$(curl -s \ -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \ -d "grant_type=password" \ "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \ | jq -r .access_token) export AR_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=ar-backend" \ | jq -r '.[0].id') export AR_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AR_BACKEND_ID}/client-secret" \ | jq -r .value) echo "AR_BACKEND_SECRET: $AR_BACKEND_SECRET"
Step 3: Install the arctl CLI
The arctl CLI is the primary tool for managing AI artifacts in Solo Enterprise for agentregistry. You use it to create, publish, and deploy AI artifacts. Before you can run any arctl commands, you must authenticate the CLI with Keycloak to obtain a valid access token.
Download and install the
arctlCLI.curl -sSL https://storage.googleapis.com/agentregistry-enterprise/install.sh | ARCTL_VERSION=v2026.7.0 sh export PATH=$HOME/.arctl/bin:$PATHLog in to the registry. Choose between the interactive device authorization and the static username and password credential flow.
Use this flow for interactive logins from a terminal. The CLI uses the
ar-cli-interactivepublic client and starts a device authorization grant flow that prints a URL and a one-time code for you to approve in a browser.Configure the CLI to use the Keycloak issuer and
ar-cli-interactiveclient. Then, log in to Solo Enterprise for agentregistry.export OIDC_ISSUER=$KEYCLOAK_ISSUER export OIDC_CLIENT_ID=ar-cli-interactive arctl user loginExample output:
To complete the login process, please: 1. Open: http://172.18.0.13:8080/realms/agentregistry/device 2. Enter the code: SYSQ-SNSP - or go to: http://172.18.0.13:8080/realms/agentregistry/device?user_code=SYSQ-SNSPOpen the URL in a web browser and log into Keycloak with the
admin-userusername andpasswordpassword. Confirming the code in the browser tells Keycloak to issue an access token for the CLI session. After authorization is complete, the CLI stores the token in your system keychain so you do not have to log in again until it expires.
Use this flow for scripted logins where a specific user identity is required in CI/CD pipelines. The CLI uses the
ar-cli-passwordpublic client and exchanges a username and password directly for an access token.Log in to Solo Enterprise for agentregistry with the
admin-user/passwordcredentials.export OIDC_ISSUER=$KEYCLOAK_ISSUER arctl user login \ --oidc-flow=password-credentials \ --oidc-client-id=ar-cli-password \ --oidc-username=admin-user \ --oidc-password=passwordSave the access token in the
ARCTL_API_TOKENenvironment variable.export ARCTL_API_TOKEN=$(arctl user info --show-tokens | jq -r .access_token)
Verify that you can run
arctlcommands.arctl user whoamiExample output:
FIELD VALUE Subject 8d7ffd9c-1f75-459a-b900-bfa4f5e3b492 Email admin-user@example.com Issuer http://172.18.0.10:8080/realms/agentregistry Superuser true (overrides role-based permission checks) ROLE STATUS admins no explicit permissions configuredIf you see401errors in the CLI, you might have previously stored a token in theARCTL_API_TOKENenvironment variable. This environment variable takes precedence over the token that you obtain via the CLI login flow. Try runningunset ARCTL_API_TOKEN. Then, repeat the CLI login flow.
Step 4: Prepare the AWS environment
To deploy agents and MCP servers to AWS Bedrock AgentCore, Solo Enterprise for agentregistry needs permission to act in your AWS account on your behalf. In this step, you:
- Grant AWS access to the registry server: You can choose between two authentication methods to grant the registry server access to your AWS account so that the server can deploy and manage AWS Bedrock AgentCore resources on your behalf.
- IAM user credentials: Use static long-lived access keys.
- EKS Pod Identity: Use short-lived credentials that are automatically rotated by EKS.
- Create a cross-account IAM role: You generate a CloudFormation template and deploy it into your account. The template creates an IAM role that Solo Enterprise for agentregistry assumes when it deploys or manages resources in AWS Bedrock AgentCore.
- Register the runtime: You register the AWS runtime in Solo Enterprise for agentregistry.
Configure the AWS CLI with credentials for an IAM user that has permission to create IAM users, roles, policies, and access keys. Run the following command and enter your AWS Access Key ID, Secret Access Key, and default region when prompted.
aws configureSet your AWS Region and account ID as environment variables.
export AWS_REGION=<your-aws-region> export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) echo "AWS_ACCOUNT_ID: $AWS_ACCOUNT_ID"Create the IAM policy documents and managed policies. These policies grant the registry server and Bedrock AgentCore the permissions they need. You attach these policies to either an IAM user or an IAM role in the next step, depending on the authentication method that you choose.
cat > general-access-policy.json << 'EOF' { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "NotAction": [ "iam:*", "organizations:*", "account:*" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "account:GetAccountInformation", "account:GetGovCloudAccountInformation", "account:GetPrimaryEmail", "account:ListRegions", "iam:CreateServiceLinkedRole", "iam:DeleteServiceLinkedRole", "iam:ListRoles", "organizations:DescribeEffectivePolicy", "organizations:DescribeOrganization" ], "Resource": "*" } ] } EOFThegeneral-access-policy.jsonuses a broadNotActionstatement that allows all AWS actions except IAM, Organizations, and Account management. This includes the EC2 permissions (ec2:RunInstances,ec2:DescribeInstances,ec2:TerminateInstances, and related) that are required later in this guide to provision the managed gateway’s EC2 instance.cat > bedrock-agentcore-policy.json << 'EOF' { "Version": "2012-10-17", "Statement": [ { "Sid": "BedrockAgentCoreFullAccess", "Effect": "Allow", "Action": ["bedrock-agentcore:*"], "Resource": "arn:aws:bedrock-agentcore:*:*:*" }, { "Sid": "IAMListAccess", "Effect": "Allow", "Action": [ "iam:GetRole", "iam:GetRolePolicy", "iam:ListAttachedRolePolicies", "iam:ListRolePolicies", "iam:ListRoles" ], "Resource": "arn:aws:iam::*:role/*" }, { "Sid": "BedrockAgentCorePassRoleAccess", "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::*:role/*BedrockAgentCore*", "Condition": { "StringEquals": { "iam:PassedToService": "bedrock-agentcore.amazonaws.com" } } }, { "Sid": "SecretsManagerAccess", "Effect": "Allow", "Action": [ "secretsmanager:CreateSecret", "secretsmanager:PutSecretValue", "secretsmanager:GetSecretValue", "secretsmanager:DeleteSecret" ], "Resource": "arn:aws:secretsmanager:*:*:secret:bedrock-agentcore*" }, { "Sid": "BedrockAgentCoreKMSReadAccess", "Effect": "Allow", "Action": ["kms:ListKeys", "kms:DescribeKey"], "Resource": ["arn:aws:kms:*:*:key/*"], "Condition": { "StringEquals": { "aws:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "BedrockAgentCoreKMSAccess", "Effect": "Allow", "Action": ["kms:Decrypt", "kms:GenerateDataKey", "kms:ListGrants"], "Resource": ["arn:aws:kms:*:*:key/*"], "Condition": { "StringEquals": { "aws:ResourceAccount": "${aws:PrincipalAccount}" }, "ForAnyValue:StringEquals": { "aws:CalledVia": ["bedrock-agentcore.amazonaws.com"] } } }, { "Sid": "BedrockAgentCoreKMSGrantsAccess", "Effect": "Allow", "Action": ["kms:CreateGrant"], "Resource": ["arn:aws:kms:*:*:key/*"], "Condition": { "StringEquals": { "kms:GrantConstraintType": "EncryptionContextSubset" }, "StringLike": { "kms:ViaService": ["bedrock-agentcore.*.amazonaws.com"], "kms:EncryptionContext:aws:bedrock-agentcore-gateway:arn": "arn:aws:bedrock-agentcore:*:*:gateway/*" }, "ForAllValues:StringEquals": { "kms:GrantOperations": ["Decrypt", "GenerateDataKey"] } } }, { "Sid": "BedrockAgentCoreS3Access", "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::bedrock-agentcore-gateway-*"], "Condition": { "StringEquals": { "aws:CalledViaLast": "bedrock-agentcore.amazonaws.com", "s3:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "BedrockAgentCoreGatewayLambdaAccess", "Effect": "Allow", "Action": ["lambda:ListFunctions"], "Resource": ["arn:aws:lambda:*:*:*"] }, { "Sid": "BedrockAgentCoreGatewayApiGateway", "Effect": "Allow", "Action": ["apigateway:GET"], "Resource": ["arn:aws:apigateway:*::/restapis/*/stages/*/exports/*"] }, { "Sid": "LoggingAccess", "Effect": "Allow", "Action": [ "logs:Get*", "logs:List*", "logs:StartQuery", "logs:StopQuery", "logs:Describe*", "logs:TestMetricFilter", "logs:FilterLogEvents" ], "Resource": [ "arn:aws:logs:*:*:log-group:/aws/bedrock-agentcore/*", "arn:aws:logs:*:*:log-group:/aws/application-signals/data:*", "arn:aws:logs:*:*:log-group:aws/spans:*" ] }, { "Sid": "ObservabilityReadOnlyPermissions", "Effect": "Allow", "Action": [ "application-autoscaling:DescribeScalingPolicies", "application-signals:BatchGet*", "application-signals:Get*", "application-signals:List*", "autoscaling:Describe*", "cloudwatch:BatchGet*", "cloudwatch:Describe*", "cloudwatch:GenerateQuery", "cloudwatch:Get*", "cloudwatch:List*", "oam:ListSinks", "rum:BatchGet*", "rum:Get*", "rum:List*", "synthetics:Describe*", "synthetics:Get*", "synthetics:List*", "xray:BatchGet*", "xray:Get*", "xray:List*", "xray:StartTraceRetrieval", "xray:CancelTraceRetrieval", "logs:DescribeLogGroups", "logs:StartLiveTail", "logs:StopLiveTail" ], "Resource": "*" }, { "Sid": "TransactionSearchXRayPermissions", "Effect": "Allow", "Action": [ "xray:GetTraceSegmentDestination", "xray:UpdateTraceSegmentDestination", "xray:GetIndexingRules", "xray:UpdateIndexingRule" ], "Resource": "*" }, { "Sid": "TransactionSearchLogGroupPermissions", "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutRetentionPolicy" ], "Resource": [ "arn:aws:logs:*:*:log-group:/aws/application-signals/data:*", "arn:aws:logs:*:*:log-group:aws/spans:*" ] }, { "Sid": "TransactionSearchLogsPermissions", "Effect": "Allow", "Action": [ "logs:DescribeResourcePolicies", "logs:PutResourcePolicy" ], "Resource": ["*"], "Condition": { "StringEquals": { "aws:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "TransactionSearchApplicationSignalsPermissions", "Effect": "Allow", "Action": ["application-signals:StartDiscovery"], "Resource": "*" }, { "Sid": "CloudWatchApplicationSignalsCreateServiceLinkedRolePermissions", "Effect": "Allow", "Action": "iam:CreateServiceLinkedRole", "Resource": "arn:aws:iam::*:role/aws-service-role/application-signals.cloudwatch.amazonaws.com/AWSServiceRoleForCloudWatchApplicationSignals", "Condition": { "StringLike": { "iam:AWSServiceName": "application-signals.cloudwatch.amazonaws.com" } } }, { "Sid": "CloudWatchApplicationSignalsGetRolePermissions", "Effect": "Allow", "Action": "iam:GetRole", "Resource": "arn:aws:iam::*:role/aws-service-role/application-signals.cloudwatch.amazonaws.com/AWSServiceRoleForCloudWatchApplicationSignals" }, { "Sid": "CreateBedrockAgentCoreNetworkServiceLinkedRolePermissions", "Effect": "Allow", "Action": "iam:CreateServiceLinkedRole", "Resource": "arn:aws:iam::*:role/aws-service-role/network.bedrock-agentcore.amazonaws.com/AWSServiceRoleForBedrockAgentCoreNetwork", "Condition": { "StringEquals": { "iam:AWSServiceName": "network.bedrock-agentcore.amazonaws.com" } } }, { "Sid": "CreateBedrockAgentCoreRuntimeIdentityServiceLinkedRolePermissions", "Effect": "Allow", "Action": "iam:CreateServiceLinkedRole", "Resource": "arn:aws:iam::*:role/aws-service-role/runtime-identity.bedrock-agentcore.amazonaws.com/AWSServiceRoleForBedrockAgentCoreRuntimeIdentity", "Condition": { "StringEquals": { "iam:AWSServiceName": "runtime-identity.bedrock-agentcore.amazonaws.com" } } }, { "Sid": "CloudWatchApplicationSignalsCloudTrailPermissions", "Effect": "Allow", "Action": ["cloudtrail:CreateServiceLinkedChannel"], "Resource": "arn:aws:cloudtrail:*:*:channel/aws-service-channel/application-signals/*" }, { "Sid": "BedrockAgentCoreRuntimeS3WriteAccess", "Effect": "Allow", "Action": [ "s3:CreateBucket", "s3:PutBucketPolicy", "s3:PutBucketVersioning", "s3:PutObject" ], "Resource": ["arn:aws:s3:::bedrock-agentcore-runtime-*"], "Condition": { "StringEquals": { "s3:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "BedrockAgentCoreRuntimeS3ReadAccess", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:GetObjectVersion", "s3:ListBucket", "s3:ListBucketVersions" ], "Resource": "arn:aws:s3:::*", "Condition": { "StringEquals": { "s3:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "BedrockAgentCoreRuntimeS3ListAccess", "Effect": "Allow", "Action": ["s3:ListAllMyBuckets"], "Resource": "*", "Condition": { "StringEquals": { "s3:ResourceAccount": "${aws:PrincipalAccount}" } } }, { "Sid": "BedrockAgentCoreRuntimeECRAccess", "Effect": "Allow", "Action": [ "ecr:DescribeRepositories", "ecr:DescribeImages", "ecr:ListImages" ], "Resource": ["arn:aws:ecr:*:*:repository/*"] }, { "Sid": "AgentCoreEvaluationCloudWatchLogCreate", "Effect": "Allow", "Action": ["logs:CreateLogGroup"], "Resource": ["arn:aws:logs:*:*:log-group:/aws/bedrock-agentcore/evaluations/*"] }, { "Sid": "AgentCoreEvaluationCloudWatchLogIndexAccess", "Effect": "Allow", "Action": [ "logs:PutIndexPolicy", "logs:DescribeIndexPolicies" ], "Resource": [ "arn:aws:logs:*:*:log-group:aws/spans", "arn:aws:logs:*:*:log-group:aws/spans:*" ] }, { "Sid": "AgentCoreEvaluationBedrockInvokeAccess", "Effect": "Allow", "Action": [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream" ], "Resource": [ "arn:aws:bedrock:*::foundation-model/*", "arn:aws:bedrock:*:*:inference-profile/*" ] }, { "Sid": "AgentCoreEvaluationLambdaAccess", "Effect": "Allow", "Action": [ "lambda:InvokeFunction", "lambda:GetFunction" ], "Resource": "arn:aws:lambda:*:*:function:*" } ] } EOFCreate the managed policies in AWS IAM. Customer-managed policies support up to 6,144 characters and can be attached to multiple IAM principals without duplication.
aws iam create-policy \ --policy-name AgentRegistryGeneralAccess \ --policy-document file://general-access-policy.json aws iam create-policy \ --policy-name AgentRegistryBedrockAgentCoreAccess \ --policy-document file://bedrock-agentcore-policy.jsonChoose your AWS authentication method. The registry server uses these credentials whenever it deploys or manages Bedrock AgentCore resources on your behalf.
Use this method to create a dedicated IAM user and attach long-lived access key credentials. The registry server stores the access key ID and secret in a Kubernetes Secret and uses them to call AWS APIs.
Create a dedicated IAM user for Solo Enterprise for agentregistry.
aws iam create-user --user-name agentregistry-deployerAttach the general access policy to the user.
aws iam attach-user-policy \ --user-name agentregistry-deployer \ --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AgentRegistryGeneralAccessAttach the Bedrock AgentCore access policy to the user.
aws iam attach-user-policy \ --user-name agentregistry-deployer \ --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AgentRegistryBedrockAgentCoreAccessCreate an access key for the
agentregistry-deployeruser and export the credentials as environment variables.ACCESS_KEY_OUTPUT=$(aws iam create-access-key --user-name agentregistry-deployer) export AWS_ACCESS_KEY_ID=$(echo $ACCESS_KEY_OUTPUT | jq -r '.AccessKey.AccessKeyId') export AWS_SECRET_ACCESS_KEY=$(echo $ACCESS_KEY_OUTPUT | jq -r '.AccessKey.SecretAccessKey') echo "AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID"
Use this method to attach an IAM role to the registry server’s Kubernetes service account by using EKS Pod Identity. The EKS Pod Identity agent automatically injects short-lived, rotating AWS credentials into the registry pod. No long-lived access keys are stored.
Install the EKS Pod Identity Agent into your EKS cluster.
aws eks create-addon --cluster-name $EKS_CLUSTER_NAME --addon-name eks-pod-identity-agent --region $AWS_REGIONCreate a trust policy document that allows the EKS Pod Identity service to assume the role.
cat > pod-identity-trust-policy.json << 'EOF' { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "pods.eks.amazonaws.com" }, "Action": [ "sts:AssumeRole", "sts:TagSession" ] } ] } EOFCreate the IAM role with the trust policy.
aws iam create-role \ --role-name agentregistry-pod-identity-role \ --assume-role-policy-document file://pod-identity-trust-policy.json export POD_IDENTITY_ROLE_ARN=$(aws iam get-role \ --role-name agentregistry-pod-identity-role \ --query 'Role.Arn' --output text) echo "POD_IDENTITY_ROLE_ARN: $POD_IDENTITY_ROLE_ARN"Attach the general access policy to the role.
aws iam attach-role-policy \ --role-name agentregistry-pod-identity-role \ --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AgentRegistryGeneralAccessAttach the Bedrock AgentCore access policy to the role.
aws iam attach-role-policy \ --role-name agentregistry-pod-identity-role \ --policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/AgentRegistryBedrockAgentCoreAccessCreate the Pod Identity Association. This binds the IAM role to the
agentregistry-enterpriseservice account in theagentregistry-systemnamespace. The EKS Pod Identity agent uses this binding to inject temporary credentials into the registry pod.aws eks create-pod-identity-association \ --cluster-name $EKS_CLUSTER_NAME \ --namespace agentregistry-system \ --service-account agentregistry-enterprise \ --role-arn $POD_IDENTITY_ROLE_ARN \ --region $AWS_REGION
Generate the AWS CloudFormation template and save it in a YAML file. The
arctl runtime setup awscommand generates a resource stack template that is scoped to your account ID. When you deploy the template, it creates an IAM role that Solo Enterprise for agentregistry assumes to deploy and manage Bedrock AgentCore resources on your behalf, along with all required IAM policies. With CloudFormation, you get a reviewable, auditable definition of all the resources that are created in your account.arctl runtime setup bedrock-agent-core --aws-account-id $AWS_ACCOUNT_ID > cloudformation.yamlThe External ID and Role Name are printed to your CLI output and are also embedded in the
cloudformation.yamlfile. However, the External ID parameter is markedNoEcho: true, which means AWS masks it when you describe the stack or view it in the console. Make sure to note the External ID from the CLI output now so you have it later.Example output:
# External ID: NzqfvveAy0XSEsL4ZtKcHR1li-3puJAbipQDQG8ylmg # Role Name: AgentRegistryAccessRole-a11111Save the External ID in an environment variable. Replace
<external-id>with the value that you retrieved in the previous step.export AWS_EXTERNAL_ID="<external-id>"Optional: Review the template.
cat cloudformation.yamlUse the
awsCLI to deploy the resource stack to your account.Choose a name for your AWS stack, such as
agentregistry-cli-stack.export STACK_NAME="<stack-name>"Create the AWS stack. The
--capabilities CAPABILITY_NAMED_IAMflag is required, because the template creates an IAM role, and AWS requires explicit acknowledgment when a CloudFormation template creates IAM resources. If the command succeeds, you see aStackIdandOperationIdin your CLI output.aws cloudformation create-stack \ --stack-name $STACK_NAME \ --template-body file://cloudformation.yaml \ --region $AWS_REGION \ --capabilities CAPABILITY_NAMED_IAMExample output:
{ "StackId": "arn:aws:cloudformation:us-west-2:1234567890:stack/agentregistry-cli-stack/1a1aaa11-1111-11a1-aa11-1111a1111aa1", "OperationId": "1aaa1a1a-1111-11aa-1a1a-1aaa11a1a111" }Wait for the stack to finish deploying. The command exits when the stack status is
CREATE_COMPLETE.aws cloudformation wait stack-create-complete \ --stack-name $STACK_NAME \ --region $AWS_REGION
Store the
RoleArnof the IAM role that was created by the CloudFormation template in an environment variable. Solo Enterprise for agentregistry assumes this role to deploy and manage Bedrock AgentCore resources in your AWS account.export AWS_ROLE_ARN=$(aws cloudformation describe-stacks \ --stack-name $STACK_NAME \ --query 'Stacks[0].Outputs[?OutputKey==`RoleArn`].OutputValue' \ --output text \ --region $AWS_REGION) echo $AWS_ROLE_ARN
Step 5: Install Solo Enterprise for agentregistry
Install Solo Enterprise for agentregistry and connect it to Keycloak and your AWS account. Choose the tab that matches the authentication method you configured in the previous step.
Save your Solo Enterprise for agentregistry license key in an environment variable. To obtain the key, contact an account representative. For more options, such as providing the key in a secret, see Licensing.
export AGENTREGISTRY_LICENSE_KEY=<license_key>Install Solo Enterprise for agentregistry.
helm upgrade --install agentregistry \ oci://us-docker.pkg.dev/solo-public/agentregistry-enterprise/helm/agentregistry-enterprise \ --version 2026.7.0 \ --namespace agentregistry-system \ --create-namespace \ --set licensing.createSecret=true \ --set licensing.licenseKey=$AGENTREGISTRY_LICENSE_KEY \ --set oidc.issuer=$KEYCLOAK_ISSUER \ --set oidc.clientId=ar-backend \ --set oidc.clientSecret=$AR_BACKEND_SECRET \ --set oidc.publicClientId=ar-ui \ --set oidc.roleClaim=Groups \ --set oidc.superuserRole=admins \ --set aws.enabled=true \ --set-string aws.accountId="${AWS_ACCOUNT_ID}" \ --set-string aws.region="${AWS_REGION}" \ --set-string aws.accessKeyId="${AWS_ACCESS_KEY_ID}" \ --set-string aws.secretAccessKey="${AWS_SECRET_ACCESS_KEY}" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-nlb-target-type=ip" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-scheme=internal" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-type=external" \ --set telemetry.service.type=LoadBalancerhelm upgrade --install agentregistry \ oci://us-docker.pkg.dev/solo-public/agentregistry-enterprise/helm/agentregistry-enterprise \ --version 2026.7.0 \ --namespace agentregistry-system \ --create-namespace \ --set licensing.createSecret=true \ --set licensing.licenseKey=$AGENTREGISTRY_LICENSE_KEY \ --set oidc.issuer=$KEYCLOAK_ISSUER \ --set oidc.clientId=ar-backend \ --set oidc.clientSecret=$AR_BACKEND_SECRET \ --set oidc.publicClientId=ar-ui \ --set oidc.roleClaim=Groups \ --set oidc.superuserRole=admins \ --set aws.enabled=true \ --set aws.usePodIdentity=true \ --set-string aws.region="${AWS_REGION}" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-nlb-target-type=ip" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-scheme=internal" \ --set-string "telemetry.service.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-type=external" \ --set telemetry.service.type=LoadBalancerHelm value Description licensing.createSecretSet to trueso that the chart creates the license secret from thelicensing.licenseKeyvalue.licensing.licenseKeyYour Solo Enterprise for agentregistry license key. oidc.issuerThe issuer URL of your Keycloak realm. The registry uses this URL to discover the OIDC provider’s endpoints. oidc.clientIdThe confidential backend client ID ( ar-backend).oidc.clientSecretThe client secret for the ar-backendclient.oidc.publicClientIdThe public client ID for the browser-based UI ( ar-ui). The server uses this client to log in to the UI.oidc.roleClaimThe JWT claim that you want to use to extract the role or group name. In this example, the claim must match the Token Claim Name in the group mapper ( Groups) that you configured in Keycloak.oidc.superuserRoleThe role name that grants full admin access ( admins).aws.enabledEnables the AWS integration in the registry server. aws.accessKeyId/aws.secretAccessKeyIAM user credentials for the agentregistry-deployeruser. Use these fields only if you chose the IAM user credential path earlier. The registry server uses the static IAM credentials when deploying agents and MCP servers to AWS Bedrock AgentCore.aws.usePodIdentityWhen true, the chart skips creating an AWS credentials Secret and relies on credentials that are injected by the EKS Pod Identity agent. Use this field only if you chose to authenticate via the EKS Pod Identity agent.aws.regionThe AWS region where Bedrock AgentCore resources are deployed. telemetry.service.annotationsAnnotations to apply to the telemetry service. In this example, the annotations configure an internal AWS Network Load Balancer (NLB) with IP-based target routing. telemetry.service.typeExposes the OTel collector as a LoadBalancerservice so that deployed agent and MCP server runtimes can reach it from outside the cluster.Verify that the Solo Enterprise for agentregistry pods are up and running.
kubectl get pods -n agentregistry-systemExample output:
NAME READY STATUS RESTARTS AGE agentregistry-enterprise-server-76f8fcc656-bmfsr 1/1 Running 0 2m21s agentregistry-enterprise-postgresql-764898cbff-5b5tn 1/1 Running 0 2m21s agentregistry-enterprise-telemetry-collector-777798c5b4-d9cwt 1/1 Running 0 2m21s agentregistry-clickhouse-shard0-0 1/1 Running 0 2m21sPort-forward the Solo Enterprise for agentregistry controller on port 12121. The registry service is not exposed externally by default, so port-forwarding is required to reach it from your local machine. The
arctlCLI and the UI both default tohttp://localhost:12121as the registry endpoint. To expose the registry server on a Load Balancer service, add the--set service.type=LoadBalancersetting to your Helm installation.kubectl -n agentregistry-system port-forward svc/agentregistry-enterprise-server 12121:12121Open the Solo Enterprise for agentregistry UI. Log in to Keycloak with the
admin-userusername andpasswordpassword. Verify that you see the dashboard.
Step 6: Register the AgentCore runtime
Register the AWS Bedrock AgentCore runtime with Solo Enterprise for agentregistry. The runtime connects the registry server to your AWS account so that agents and MCP servers can be deployed to Bedrock AgentCore.
Get the OpenTelemetry collector load balancer hostname. The registry deploys an OTel collector alongside the control plane. Agent and MCP server runtimes use this endpoint to export traces and metrics.
export OTEL_HOST=$(kubectl -n agentregistry-system get svc \ agentregistry-enterprise-telemetry-collector \ -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') echo "OTel endpoint: http://${OTEL_HOST}:4318"Register the AWS Bedrock AgentCoreruntime.
arctl apply -f- <<EOF apiVersion: ar.dev/v1alpha1 kind: Runtime metadata: name: agentcore spec: type: BedrockAgentCore telemetryEndpoint: http://${OTEL_HOST}:4318 config: roleArn: $AWS_ROLE_ARN externalId: $AWS_EXTERNAL_ID region: $AWS_REGION EOFExample output:
✓ Runtime/agentcore createdList the runtimes that are connected to Solo Enterprise for agentregistry and verify that the Bedrock AgentCore runtime is listed.
arctl get runtimesExample output:
NAME TYPE agentcore BedrockAgentCore kubernetes-default kubernetes local local
Step 7: Create the managed gateway
The managed gateway is a key component of the Solo Enterprise for agentregistry deployment. It provisions an EC2 instance in your VPC and installs the Solo Enterprise for agentgateway binary on it. All traffic between deployed agents and MCP servers is routed through this gateway, which allows the registry to enforce access policies and apply observability to every interaction.
Why do you need a gateway?
Bedrock AgentCore runtimes are isolated compute environments. When an agent calls an MCP server, that call must stay within your private VPC and cannot traverse the public internet. The managed gateway is deployed into the same VPC that your agents and MCP servers run in and proxies all traffic between the agent and the MCP server. Without the gateway, a Bedrock AgentCore agent cannot access tools that are exposed on an MCP server that is also deployed to AgentCore.
The gateway also enforces AccessPolicies to control whether or not an agent can call other agents or access MCP server tools during runtime.
Set the Keycloak JWKS endpoint. The gateway uses this URL to fetch the public keys it needs to validate JWT tokens.
export JWKS_URL="${KEYCLOAK_ISSUER}/protocol/openid-connect/certs" echo "JWKS URL: ${JWKS_URL}"Create the managed gateway. The gateway is provisioned in the private subnet you identified in Step 6 and attached to the default security group. The STS configuration tells the gateway to validate access tokens against your Keycloak JWKS endpoint and to pass the
Groupsclaim through for downstream policy evaluation.arctl apply -f- <<EOF apiVersion: ar.dev/v1alpha1 kind: Gateway metadata: name: gateway-agentcore spec: runtimeId: agentcore networkId: "${VPC_ID}" subnetId: "${SUBNET_ID}" mode: managed sts: allowedSubjectClaims: - Groups subjectValidator: remote: "${JWKS_URL}" aws: agentCoreRuntimeSecurityGroupIds: - "${SG_ID}" EOFExample output:
✓ Gateway/gateway-agentcore createdWait for the gateway to reach
phase=ready. Provisioning the EC2 instance and installing the agentgateway binary typically takes 5–10 minutes to complete.arctl get gatewaysRepeat the command every minute until you see
readyin thePHASEcolumn:NAME PROVIDER MODE PHASE gateway-agentcore agentcore managed readyIf the gateway reachesphase=failed, check the gateway details for an error message:arctl get gateway gateway-agentcore -o yaml.
Step 8: Scaffold and publish an MCP server
In this step, you create a Python MCP server by using the fastmcp framework. The server exposes an echo and sum tool that your agent can call during a conversation. You publish it to the Solo Enterprise for agentregistry catalog, where it becomes discoverable and can be attached to any agent.
Scaffold the MCP server.
The following command creates a
mymcpdirectory that contains the scaffold for your MCP server, including asrc/main.pyentry point and aDockerfile.arctl init mcp mymcp --framework fastmcp --language python \ --description "Sample MCP server" \ --image localhost:5001/mymcp:latestExample output:
✓ Created MCP server: mymcp (framework: fastmcp, language: python)Explore the MCP server scaffold.
ls -R mymcpExample output:
Dockerfile mcp.yaml pyproject.toml README.md src tests mymcp/src: core main.py tools mymcp/src/core: __init__.py server.py utils.py mymcp/src/tools: __init__.py echo.py sum.py mymcp/tests: test_discovery.py test_server.py test_tools.pyFile Description arctl.yamlThe MCP server details that you specified with the arctlCLI.DockerfileThe Dockerfile to spin up and run your MCP server in a containerized environment. mcp.yamlThe MCP server configuration file that defines server metadata, transport settings, version, and other server-specific configuration. pyproject.tomlThe Python project configuration file that defines project dependencies, build settings, and metadata for the MCP server. README.mdAn introduction to the MCP server that you created with instructions for how to further customize it. srcA directory that contains the details of the MCP server, such as supported tools and the Python script to bootstrap and run the server. src.coreA directory that contains the MCP server source code files. src.toolsA directory that defines the tools that the MCP server can use. The sample scaffold includes an echo and a sum tool. testsA directory that contains generated tests. Store the MCP server source code in a GitHub repository.
Each runtime packages the MCP server differently. Solo Enterprise for agentregistry fetches the source code from a GitHub repository at deploy time and packages it for the target runtime.
This tutorial uses thehttps://github.com/solo-io/doc-examplesSolo.io GitHub repository so that you do not have to push your MCP server code to a GitHub repository. Simply continue with the next step to publish the MCP server. If you already have an MCP server in a GitHub repository, you can use that repository URL instead.Edit the
mcp.yamlfile to add the GitHub repository information. This tells Solo Enterprise for agentregistry where to fetch the source code when deploying the MCP server.cat << EOF > mymcp/mcp.yaml apiVersion: ar.dev/v1alpha1 kind: MCPServer metadata: name: mymcp spec: description: mymcp MCP server source: package: launch: args: - type: positional value: src/main.py - type: positional value: --transport - type: positional value: http - type: positional value: --host - type: positional value: 0.0.0.0 - type: positional value: --port - type: positional value: "3000" command: python3 origin: identifier: localhost:5001/mymcp:latest oci: serverName: mymcp type: oci transport: path: /mcp port: 3000 type: http repository: url: https://github.com/solo-io/doc-examples subfolder: agentregistry/mymcp title: mymcp EOFPublish the MCP server to the Solo Enterprise for agentregistry catalog.
arctl apply -f mymcp/mcp.yamlExample output:
✓ MCPServer/mymcp (latest) createdVerify that the MCP server is registered in the catalog.
arctl get mcpExample output:
NAME TAG DESCRIPTION mymcp latest mymcp MCP server
Step 9: Publish an agent
In this step, you use the built-in scaffolding capability to create a Python agent with Google’s Agent Development Kit (ADK) that uses AWS Bedrock Claude as the LLM provider. The agent references the MCP server you published in the previous step so that, after deployment, it can call the MCP tools during a conversation.
Create an agent.
The following command creates the
myagentPython agent with the Google ADK framework configured to use the AWS Bedrock Claude provider.arctl init agent myagent --framework adk --language python \ --model-provider bedrock \ --model-name us.anthropic.claude-sonnet-4-6Want to see available options as you configure your agent? Try out the interactive CLI mode that walks you through the details for your agent scaffold. Simply runarctl init agentto start the CLI in interactive mode.Example output:
✓ Created agent: myagent (framework: adk, language: python, model: bedrock/us.anthropic.claude-sonnet-4-6) 🚀 Next steps: 1. Publish to the registry: arctl apply -f myagent/agent.yamlExplore the agent scaffold that was created for you. You can optionally make changes to the files to customize your agent further.
ls myagentFile Description arctl.yamlThe agent details that you specified with the arctlCLI.agent.yamlThe agent definition, including the agent framework, LLM provider, model, and agent image location. docker-compose.yamlA Docker compose file that is used to spin up and run your agent on your local machine. DockerfileThe Dockerfile to build and run your agent in a container. myagentA directory that includes the agent.pyscript that defines the agent.otel-collector-config.yamlAn OpenTelemetry configuration for the agent. pyproject.tomlThe dependency definition of your agent. README.mdAn introduction to the agent with instructions for how to customize it. Store the agent source code in a GitHub repository.
This tutorial uses thehttps://github.com/solo-io/doc-examplesSolo.io GitHub repository so that you do not have to push your agent code to a GitHub repository. Simply continue with the next step to publish the agent. If you already have an agent in a GitHub repository, you can use that repository URL instead.Edit the
agent.yamlfile to add the GitHub repository information and reference the MCP server. By default, the scaffold only includes the image location. ThemcpServersfield tells the registry to wire themymcpMCP server into the agent when it is deployed.cat << EOF > myagent/agent.yaml apiVersion: ar.dev/v1alpha1 kind: Agent metadata: name: myagent spec: source: image: localhost:5001/myagent:latest repository: url: https://github.com/solo-io/doc-examples subfolder: agentregistry/myagent modelProvider: bedrock modelName: us.anthropic.claude-sonnet-4-6 description: myagent agent mcpServers: - kind: MCPServer name: mymcp EOFPublish the agent to Solo Enterprise for agentregistry.
arctl apply -f myagent/agent.yamlExample output:
→ Injecting labels from arctl.yaml: arctl.dev/framework=adk, arctl.dev/language=python ✓ Agent/myagent (latest) createdOpen the Solo Enterprise for agentregistry catalog. Verify that you see the
myagentagent.You can also use the
arctlCLI to list published agents.arctl get agentsExample output:
NAME TAG PROVIDER MODEL myagent latest bedrock us.anthropic.claude-sonnet-4-6
Step 10: Deploy the MCP server
Deploy the source MCP server to AWS Bedrock AgentCore inside your VPC. The MCP server must be deployed and reach deployed status before you deploy the agent, because the agent’s deployment depends on a running MCP server backend to route tool calls through the gateway.
Create a Deployment for the MCP server. The registry fetches the MCP server source code from the GitHub URL you provided when you published it, packages it for AWS Bedrock AgentCore, and provisions the runtime in the specified VPC.
arctl apply -f - <<EOF apiVersion: ar.dev/v1alpha1 kind: Deployment metadata: name: mymcp spec: targetRef: kind: MCPServer name: mymcp runtimeRef: kind: Runtime name: agentcore runtimeConfig: region: $AWS_REGION entryPointFile: src/main.py networkMode: vpc subnetIds: ${SUBNET_IDS_YAML} securityGroupIds: - ${SG_ID} EOFExample output:
✓ Deployment/mymcp createdOpen the Instances view in the UI and select the MCP server deployment. Monitor the Instance Logs section until the deployment status shows
deployed. The initial deployment can take several minutes while Bedrock AgentCore provisions the runtime and builds the container.
Step 11: Deploy the agent
Now that the MCP server is deployed and running in your VPC, you can deploy the agent to AWS Bedrock AgentCore. The deploymentRefs field links the agent deployment to the MCP server deployment, so the registry knows to route MCP tool calls from the agent through the gateway to the MCP server.
Create a Deployment for the agent. The registry fetches the agent source from the GitHub URL you provided when you published the agent, packages it for AWS Bedrock AgentCore, and provisions the runtime in the specified VPC.
arctl apply -f - <<EOF apiVersion: ar.dev/v1alpha1 kind: Deployment metadata: name: myagent spec: targetRef: kind: Agent name: myagent runtimeRef: kind: Runtime name: agentcore deploymentRefs: - name: mymcp runtimeConfig: region: $AWS_REGION workdir: agentregistry/myagent networkMode: vpc subnetIds: ${SUBNET_IDS_YAML} securityGroupIds: - ${SG_ID} EOFField Description deploymentRefsLinks the agent deployment to the MCP server deployment. The registry uses this reference to wire the MCP server’s gateway backend into the agent so that MCP tool calls are automatically routed through the gateway. runtimeConfig.workdirThe subdirectory within the repository that contains the agent’s source code. runtimeConfig.networkModeSet to vpcto deploy the agent inside your private VPC. This is required for the agent to reach the MCP server over the private network.runtimeConfig.subnetIdsThe private subnets in which the agent runtime is deployed. runtimeConfig.securityGroupIdsThe security groups applied to the agent runtime. Must allow inbound traffic from the MCP server’s security group. Example output:
✓ Deployment/myagent createdOpen the Instances view in the UI and select the agent deployment. Monitor the Instance Logs section until the deployment status shows
deployed. Note that the deployment can take a few minutes to complete.
Step 12: Chat with the agent
You can chat with the agent by using the Solo Enterprise for agentregistry UI or the AWS Bedrock AgentCore UI.
Open the Instances view in the UI and select the
myagentagent.In the Agent Details card, click Open Chat.
Chat with the agent. For example, you can ask it what it can do for you. Then, hit Enter and wait for the agent to reply. Verify that the agent lists the following capabilities:
- Roll a dice (default agent tool)
- Check prime numbers (default agent tool)
- Echo messages (
echoMCP server tool) - Add numbers (
sumMCP server tool)
Open the AWS Bedrock AgentCore agents overview and select the agent that you deployed.
Go to the
ENDPOINTScard, and click the Default endpoint.

Click Test endpoint to open the agent sandbox.
In the agent sandbox, make sure that your agent is selected from the Runtime drop-down.
In the Input card, enter the following prompt and click Run. Verify that the agent lists the following capabilities:
- Roll a dice (default agent tool)
- Check prime numbers (default agent tool)
- Echo messages (
echoMCP server tool) - Add numbers (
sumMCP server tool)
{ "jsonrpc": "2.0", "id": "1", "method": "message/send", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "what can you do?" } ], "messageId": "msg-1", "contextId": "f5bd2a40-74b6-4f7a-b649-ea3f09890003" } } }
Congratulations! You successfully set up Solo Enterprise for agentregistry, deployed a source-built MCP server and an AI agent to AWS Bedrock AgentCore inside your VPC, and routed all traffic through the managed agentgateway.
Next
Explore other topics.