MastertheMesh
Solo · AgentRegistry · arctl · MCP · skill · kagent · AgentCore · GeminiAgentRuntime · AccessPolicy · agentgateway · kind
Built · three runtimes, MCP tools, and a tool-level AccessPolicy

AgentRegistry end to end, part 3: one agent, three runtimes, governed MCP tools

You take one published agent and run it, unchanged, on three runtimes: Solo Enterprise for kagent on kind, AWS Bedrock AgentCore, and Google Cloud (Vertex, through a GeminiAgentRuntime), with only its runtimeRef changing between them. The agent pulls approved MCP tool servers from the AgentRegistry catalog and bakes in a published skill, so it never wires arbitrary tools. Then you lock down which of those tools it may call with a kagent AccessPolicy, enforced at an agentgateway waypoint, and watch the agent's tool list shrink live as printenv drops out. This is the governance payoff for the AgentRegistry lifecycle from parts 1 and 2, and it stands on its own kind cluster. Every output below is captured from the live run.

Series: AgentRegistry end to end. This is part 3. Part 1 is the lifecycle, scaffold to a hosted agent. Part 2 adds registry identity, AccessPolicies, and per-team catalog visibility. Part 3 stands on its own kind cluster and focuses on approved MCP tools, multiple runtimes, and tool-level authorization.

The use case

An agent is only as useful as the tools it can reach, and only as safe as the tools you let it reach. A platform team wants developers to pull tools from an approved catalog rather than wiring arbitrary MCP servers, wants the same published agent to run wherever the workload belongs (a local cluster, a managed cloud runtime), and wants to draw a line around exactly which tools any given agent may call. AgentRegistry plus Solo Enterprise for kagent provides all three: the catalog and packaging, deployment by reference onto any registered runtime, and an identity-aware AccessPolicy that an agentgateway waypoint enforces at the tool level.

What the lab builds

Agent · agentdemo

dice agent

what
Local tools roll_die + check_prime
stack
ADK Python, Anthropic claude-haiku-4-5
uses
The catalog MCP tools + the baked skill
MCPServers · catalog

approved tool servers

what
everything-server (sum, echo, to_uppercase, reverse_text, printenv) + my-mcp (word_count)
stack
FastMCP, Python, HTTP transport
ships as
OCI images published to the catalog by setup
Skill · dice-game

dice-game

what
A house format for reporting rolls and prime checks
stack
Markdown with YAML frontmatter
ships as
Published to the catalog; baked into the agent at build

The agent calls the MCP servers' tools at runtime, and the skill is folded into its instruction at build time. The MCP servers and the skill are published once by the lab setup, so in the demo they are already in the catalog, waiting to be pulled.

The flow

YOUR LAPTOP arctl init·build·apply local OCI registry localhost:5001 build --push → registry; cluster pulls IN-CLUSTER PLATFORM · kind · agentgateway ingress *.localtest.me AgentRegistry agentregistry-system · catalog/UI/API + Postgres + ClickHouse agentregistry.localtest.me Keycloak realm agentregistry keycloak.localtest.me kagent controller OIDC validate · A2A Enterprise UI · kagent.localtest.me AgentRegistry runs inside the cluster, behind the agentgateway ingress. There are no port-forwards; arctl logs in with arctl user login against the gateway issuer and deploys by reference to each runtime. consoles: agentregistry.localtest.me (AR) · kagent.localtest.me (Enterprise UI) · keycloak.localtest.me (Keycloak) RUNTIME 1 · KIND · Solo Enterprise for kagent agentdemo pod ADK · claude-haiku-4-5 agentgateway waypoint tool authz MCP server pods everything-server · my-mcp kmcp · :3000/mcp AccessPolicy (DENY printenv) → EnterpriseAgentgatewayPolicy programs the waypoint · hides the tool admin-user · Keycloak bearer · A2A RUNTIME 2 · AWS BEDROCK AGENTCORE agentdemo runtime Bedrock Claude · in your AWS account clones agent source from git same published agent, different runtimeRef RUNTIME 3 · GOOGLE CLOUD · Vertex GeminiAgentRuntime Vertex · same published agent arctl runtime setup gemini-agent-runtime same published agent, different runtimeRef apply / publish pull image deploy · runtime 1 deploy · runtime 2 deploy · runtime 3
One agent, several runtimes. The only difference between the deployments is the Deployment's runtimeRef. On kagent the agent runs the OCI image and uses your Anthropic key. On AgentCore it runs native Bedrock Claude in your AWS account and clones its source from a git repo at deploy time, so AgentCore needs a repo URL where the lab setup pushes the scaffolded agentdemo/. The same published agent also deploys to Google Cloud through a GeminiAgentRuntime (Vertex), registered with arctl runtime setup gemini-agent-runtime, by pointing the runtimeRef at that runtime.

Prerequisites

You bring a few tools and credentials; ./scripts/setup.sh installs the whole platform into a throwaway kind cluster. Nothing in the “what setup installs” list below needs to exist beforehand, the scripts stand it all up.

What you provide

The two clouds are independent. Run the lab kagent-only, kagent + AWS, kagent + Google, or all three: the kagent runtime is always registered, and each cloud runtime registers only when you supply its credentials.

What ./scripts/setup.sh installs

bashsetup
./scripts/setup-env.sh      # capture credentials (Anthropic key, Solo licenses; optional AWS profile / GCP project)
./scripts/setup.sh          # bring up the platform (~20 min first run)
./scripts/open-consoles.sh  # open the AgentRegistry + Enterprise UIs
# then open demo.ipynb (Bash kernel) and run top to bottom
Consoles. Everything is reached through the agentgateway ingress at *.localtest.me, no port-forwards: the AgentRegistry UI/API at http://agentregistry.localtest.me, the Enterprise UI at http://kagent.localtest.me, and Keycloak at http://keycloak.localtest.me. The UI login is admin-user / password (a realm user in the admins group: registry superuser and kagent Admin). The arctl CLI logs in as the same user with arctl user login against the gateway issuer.

Connect

Before any step below, load your credentials and log arctl in to the in-cluster registry. This reads .env.local, so run ./scripts/setup-env.sh first (it creates .env.local with your Anthropic key, Solo licenses, and any AWS/GCP details) if you have not already. Then, once per shell:

bashconnect
source scripts/connect.sh

connect.sh loads .env.local, puts arctl on the PATH, points ARCTL_API_BASE_URL at http://agentregistry.localtest.me, and runs arctl user login as admin-user against the Keycloak issuer. The notebook's first cell does exactly this; after it, the arctl get commands below work.

Step 1: browse the registry, then create the agent wired to one approved tool

Rather than wiring arbitrary MCP servers, the developer pulls from an approved catalog. Open the AgentRegistry UI (http://agentregistry.localtest.me) and go to Tool Servers, or list the catalog from the CLI. The skills and the runtimes the registry can target are there too: kind-kagent (Solo Enterprise for kagent) always, plus aws-agentcore (AWS Bedrock AgentCore) and/or gcp-vertex (Google Cloud, Vertex AI) for whichever clouds you connected at setup.

bashbrowse the catalog
arctl get mcpservers
arctl get skills
arctl get runtimes
textarctl get mcpservers / runtimes
NAME                TAG      DESCRIPTION
everything-server   latest   Approved org tool server: sum, echo, uppercase, reverse.
my-mcp              latest   Text utilities tool server: word_count.

NAME            TYPE
aws-agentcore   BedrockAgentCore
kind-kagent     Kagent
local           Local

Scaffold the agent and reference one approved server from the catalog with --mcp, my-mcp (the word_count tool). A second one is added later to show a tool coming online. arctl records the reference in agent.yaml under spec.mcpServers, and the scaffolded agent also ships two local tools, roll_die and check_prime.

bashscaffold
arctl init agent agentdemo --framework adk --language python \
  --model-provider anthropic --model-name claude-haiku-4-5 --mcp my-mcp@latest

Step 2: build and run it locally

Build the image, then run it in an interactive chat in a terminal. Ask it to roll a die: it uses the local roll_die and check_prime tools. The approved catalog tool word_count (from my-mcp) comes online when the agent is deployed, where the registry stands the MCP server up beside it.

bashbuild + local run
arctl build ./agentdemo
arctl run ./agentdemo          # interactive A2A chat in your terminal

Step 3: build multi-arch, publish, push the source

Build for both architectures (kagent runs arm64 here, AgentCore runs amd64), publish the agent to the catalog, and push the source to the agent's git repo so AgentCore can clone it.

bashpublish
arctl build ./agentdemo --platform linux/amd64,linux/arm64 --push
arctl apply -f agentdemo/agent.yaml
./scripts/git-push.sh                 # AgentCore clones this at deploy

Step 4a: kick off the AgentCore deploy (runtime 2)

The same published agent, deployed to a second runtime. This signs in to AWS, gives the in-cluster registry your AWS credentials, then builds, pushes to ECR, and deploys in the background while you carry on with the kagent runtime. AgentCore publishes its own agent record (agentdemo-agentcore, an ECR image), so it never collides with the kagent agentdemo agent. The agent runs on Bedrock Claude in your AWS account and clones its source from the git repo.

bashAgentCore (background)
source scripts/agentcore.sh &       # foreground AWS sign-in, then build/push/deploy in the background
tail -f /tmp/agentcore-deploy.log   # watch it provision (~2-4 min)

Step 4b: kick off the Google Cloud deploy (runtime 3)

Optional, and only if you connected Google at setup (GCP_PROJECT_ID set). The same published agent, deployed to a third runtime: it lands on Vertex AI Agent Engine and its MCP tool on Cloud Run, both built from the same git source. One thing differs from kagent: on Google the agent's MCP tools are wired explicitly, so the script deploys the MCP to Cloud Run first, waits for it, then deploys the agent and links it through deploymentRefs. That whole sequence runs in the background while you carry on with the kagent runtime.

bashGoogle Cloud (background)
source scripts/gcp.sh &        # deploy MCP -> Cloud Run, then agent -> Vertex AI, in the background
tail -f /tmp/gcp-deploy.log    # watch it provision (~8-12 min)

Step 5: deploy onto kagent and work with it (runtime 1)

Now the in-cluster runtime. Deploy the approved MCP tool server and the agent onto kind-kagent. The registry binds the agent to the runtime and derives its MCP_SERVERS_CONFIG from the MCP servers deployed there, which is why the tool server is deployed first. The agent then sits behind the kagent controller's OIDC enforcement.

bashdeploy + pods
arctl apply -f yaml/deploy-mcp-my-mcp.yaml
kubectl --context kind-agentcore-demo -n kagent wait --for=condition=Ready mcpserver/my-mcp --timeout=180s
envsubst < yaml/deploy-kagent.yaml | arctl apply -f -
kubectl --context kind-agentcore-demo -n kagent get pods

Ask what tools it has, over the controller's OIDC-protected A2A endpoint. Right now it lists word_count (from my-mcp) alongside the local roll_die and check_prime.

bashask.sh (over OIDC)
./scripts/ask.sh "List the exact names of every tool you can call. Output only a comma-separated list, nothing else."

Add another approved server by declaring it in agent.yaml (in the UI: Catalog → agentdemo → Edit → add everything-server, or paste the snippet below under spec.mcpServers) and re-applying. No rebuild: re-publish the agent, deploy the new MCP server, and re-deploy the agent so it re-derives its tool list.

yamladd everything-server to agent.yaml
  - kind: MCPServer
    name: everything-server
    tag: latest
bashapply the new tool, redeploy
arctl apply -f agentdemo/agent.yaml
arctl apply -f yaml/deploy-mcp-everything-server.yaml
envsubst < yaml/deploy-kagent.yaml | arctl apply -f -
kubectl --context kind-agentcore-demo -n kagent wait --for=condition=Ready mcpserver/everything-server --timeout=180s
kubectl --context kind-agentcore-demo -n kagent rollout status deploy/agentdemo --timeout=150s

Ask again, and now sum, echo, printenv, reverse_text, and to_uppercase appear alongside word_count and the local tools. The tool came online by adding it to the spec and redeploying, with no rebuild. Then run a real task and watch the trace: give the agent a multi-step prompt that exercises the whole chain.

agentdemo "Roll a 13-sided die, add 5 to the result, then tell me if it's prime."
A
agentdemo roll_die → sum (MCP) → check_prime

- Die roll (13-sided): 4
- 4 + 5: 9
- Is 9 prime?: No, 9 is not a prime number (it's divisible by 3)

The arithmetic is done by the MCP sum tool, not the model, and the answer follows the dice-game skill's house format. Open the Enterprise UI (http://kagent.localtest.me) → Tracing and the run shows up as a span tree, invocation → call_llm → generate_content → execute_tool roll_die → sum → check_prime, with the model and token usage on each LLM span.

Step 6: govern the MCP tools with an AccessPolicy

The everything-server exposes a sensitive printenv tool that returns the server's environment. A platform owner restricts the agent to only the tools it needs, in the Enterprise UI (http://kagent.localtest.me) → Access Policies → Create New Access Policy, doing the steps in order (the agent picker only populates once the cluster and namespace are set):

  1. Policy: name it; Cluster agentcore-demo; Namespace kagent.
  2. Subjects: Subject Kind Agentagentdemo.
  3. Action: ALLOW; tools sum.
  4. Target: Target Type MCP Servereverything-server.

This is an allowlist, so on the everything-server the agent may now call only sum, and printenv and the rest are denied. (Prefer the CLI? ./scripts/accesspolicy-on.sh applies the identical policy.) The MCP server is already behind an agentgateway waypoint, which the registry labels at deploy time and which the ambient mesh from the prerequisites brings up. That waypoint is the enforcement point: the kagent controller compiles the AccessPolicy into an EnterpriseAgentgatewayPolicy the waypoint enforces.

bashconfirm the waypoint, show the policy
kubectl --context kind-agentcore-demo -n kagent label mcpserver everything-server kagent.solo.io/waypoint=true --overwrite
kubectl --context kind-agentcore-demo -n kagent get accesspolicy -o yaml

Ask the agent for its tools before and after. The list shrinks, printenv is gone, and sum and the rest still work:

"List the exact names of every tool you can call."
before the policy 8 tools

roll_die, check_prime, echo, printenv, reverse_text, sum, to_uppercase, word_count

A
after the policy printenv denied

roll_die, check_prime, echo, reverse_text, sum, to_uppercase, word_count

The enforcement point is the waypoint, not the application: the agent never sees printenv in its tool list and a direct call is refused. Revert with ./scripts/accesspolicy-off.sh and the full list returns.

Step 7a: invoke the same agent on AgentCore (runtime 2)

By now the AgentCore runtime kicked off in step 4a has provisioned. Invoke it with the same kind of task: this runs the identical governed agent in AWS, not your cluster. ac-invoke.sh waits for the runtime to be READY first, then prints the answer.

bashac-invoke.sh
./scripts/ac-invoke.sh "Roll a 13-sided die, add 5 to the result, then tell me if it is prime."

Step 7b: invoke the same agent on Google (runtime 3)

The Google deploy from step 4b has provisioned by now. Invoke it with the same task: this runs the identical governed agent on Vertex AI Agent Engine, calling its MCP tool on Cloud Run. gcp-invoke.sh waits for the runtime to be READY, then queries the Vertex Reasoning Engine and prints the answer. (Verified: it rolls a die, adds five, and tells you whether the total is prime, using its roll_die and check_prime tools.)

bashgcp-invoke.sh
./scripts/gcp-invoke.sh "Roll a 13-sided die, add 5 to the result, then tell me if it is prime."

Teardown

Everything lives in the throwaway kind cluster except the cloud-runtime resources, which bill until removed. cleanup.sh tears down each cloud independently, then the cluster:

bashcleanup
./scripts/cleanup.sh gcp        # Vertex reasoning engine + Cloud Run MCP + the deployer SA & custom roles
./scripts/cleanup.sh agentcore  # AgentCore runtime + CloudFormation role + ECR repo
./scripts/cleanup.sh all        # both clouds, then the kind cluster and local registry

Each cloud path no-ops cleanly when that cloud was never set up, so all is safe whichever runtimes you deployed. Pass KEEP_GCP_SA=true to keep the Google deployer service account and custom roles for a later re-deploy.

Versions

Built and verified on:

Enterprise
arctlv2026.6.1
AgentRegistry (Solo Enterprise)2026.6.1
Solo Enterprise for kagent0.4.3
Solo Enterprise management0.4.3
Gloo Operator0.5.2
Solo Istio1.29.2-patch0
enterprise agentgatewayv2026.5.1
Gateway APIv1.4.0
Keycloak26.3
Kubernetes (kind)v1.35.0