What agentregistry is
Solo's enterprise control plane for the agentic assets a platform team needs to govern — agents, MCP tool servers, skills, prompts — plus the runtimes and deployments that put them onto a cluster. One catalog, one set of policies, one observability surface across every runtime you support.
First enterprise release shipped May 1st 2026 as
v2026.05.0 — arctl CLI, Helm chart, container images.
Initial runtime support: Kubernetes with kagent and
AWS AgentCore. Cross-runtime access policy
via agentgateway is the next roadmap item, then approval workflows,
drift detection, and remote MCP integration.
This page is the reference for the six resources the catalog is built
from — what each kind models, what fields it carries, and copy-paste
YAML you can arctl apply -f.
If you've written Istio or kgateway YAML, the agentregistry resources will
look familiar — same apiVersion / kind / metadata / spec / status
envelope, same kubectl-style apply UX. The thing to know
upfront is that these are not Kubernetes CRDs.
The "CRD" in the title is a borrowed shorthand.
There is no CustomResourceDefinition registered with the
Kubernetes API server for these kinds — running kubectl get crd | grep ar.dev
returns nothing.
What agentregistry actually does is reuse the Kubernetes resource
envelope as its API contract. You write a manifest, run
arctl apply -f (or POST /v0/apply), and the
agentregistry server stores it in PostgreSQL. The apiVersion
on every kind is ar.dev/v1alpha1 — that's
agentregistry's group, not k8s.io's. From a user's
point of view, you write the same YAML you would for Kubernetes; the
difference is where it lives and who reconciles it.
Heads-up if you also use kagent: the names overlap, the CRDs don't.
Every kind on this page is in the ar.dev/v1alpha1 group and is stored
in the registry's Postgres. kagent's Agent is a separate, real
Kubernetes CRD in kagent.dev/v1alpha2, and kmcp's
MCPServer is a separate, real Kubernetes CRD in
kmcp.dev/v1alpha1 — both registered on the cluster.
Same names, different groups, different objects.
The link is one-way. When a Deployment reconciles onto a
Runtime of type: Kubernetes, the runtime adapter
translates the registry's Agent / MCPServer rows into kagent.dev
/ kmcp.dev CRDs and applies those to the cluster. Registry rows
are the catalogue you author and version; the kagent / kmcp CRDs are what
the cluster actually runs. This page covers the registry side only — the
kagent CRD reference is its own page.
Six kinds today. Four of them are tagged content artifacts
— Agent, MCPServer, Skill, Prompt — versioned by a metadata.tag
that defaults to latest. Two are mutable control-plane
objects — Runtime, Deployment — keyed by metadata.name
only, no tag. That split is the load-bearing part of the model: artifacts
are something you publish multiple immutable revisions of; control objects
are something there's exactly one of, that you keep editing in place.
How the pieces fit together
How to read it: clients on top, six resource kinds in the
middle (split by mutability — artifacts on the left have tags, control
objects on the right don't), runtime targets at the bottom. The
agentregistry server stores everything in Postgres; when a
Deployment row is upserted the reconciler resolves its
runtimeRef, looks the adapter up by
Runtime.spec.type, and calls
DeploymentAdapter.Apply. Local writes
agent-gateway.yaml + docker-compose.yaml and runs
compose up. Kubernetes translates the target into
kagent / kmcp CRDs and applies them. The dashed enterprise box on the right
is what additional adapters look like — same interface, different push
target.
What a deploy actually does
Push a Deployment manifest and here's the path it follows. The
reconciler runs everything after POST /v0/apply asynchronously
— Apply returns Progressing=True immediately, and the
adapter's watch loop later flips Ready=True via
Store.PatchStatus when the workload converges.
The kinds, group by group
One section per family, with copy-paste YAML and field-level explainers underneath. The colour stripe matches the diagram. The four content artifact kinds first, then the two control-plane kinds.
🤖 Agent ar.dev/v1alpha1 · tagged
An Agent bundles an identity with the things it needs at
runtime: a container image (source.image), the LLM it talks
to (modelProvider + modelName), and references
to the MCPServers it can call. The agent's code
lives in the image; the agent's capabilities are everything in
spec.mcpServers.
Agent · minimal definitionone image + one model + one MCP ref
# scaffold a starter project (agent.yaml + arctl.yaml + .env)
arctl init agent summarizer \
--framework adk --language python \
--model-provider gemini --model-name gemini-2.5-flash
# build the container image and push it
arctl build ./summarizer --push
# register the manifest with the registry
arctl apply -f ./summarizer/agent.yaml
# list every revision you've published
arctl get agent summarizer --all-tags
apiVersion: ar.dev/v1alpha1
kind: Agent
metadata:
name: summarizer
tag: stable # default: "latest"
spec:
description: "Summarizes long documents into bullet points."
source:
image: ghcr.io/acme/summarizer:v1.0.0
repository: # optional — link to source
url: https://github.com/acme/summarizer
modelProvider: gemini # gemini | openai | anthropic | azureopenai | agentgateway
modelName: gemini-2.5-flash
mcpServers:
- kind: MCPServer
name: acme/fetch
tag: stable # pin — omit for "latest at deploy time"
What each field controls
metadata.tag- The version label for this revision of the agent. The Agent / MCPServer / Skill / Prompt kinds are tagged artifacts — re-applying the same name with a different tag produces a new row, not a mutation.
arctl get agent summarizer --all-tagslists every tag you've published. Omit the field and it lands aslatest. source.image- The OCI image the runtime will pull. This is the agent's executable container — the kagent ADK runtime image, your own Go binary in a Distroless base, etc. The registry doesn't validate that the image runs; it validates the reference and stores it.
source.repository- Optional pointer at the source-code origin. Useful for
arctl pull agent summarizer(which re-fetches the source so a teammate can build it locally) and for audit — "where did this image come from?". Not used at runtime. modelProvider+modelName- What model the agent talks to.
modelProvideris the SDK-level provider name;modelNameis the model ID. SetmodelProvider: agentgatewayto route LLM calls through your Solo agentgateway instead of straight to the provider — that's where you get prompt caching, token rate-limiting and cross-provider failover. mcpServers[]- Pure
ResourceRefs — kind + name (+ optional tag) — pointing at other rows already in the registry. No inline config; if you need to override an env var per deployment that goes on theDeployment, not here. Per the v1alpha1 audit,Skills andPrompts used to be referenceable too but were removed for being ADK-Python-specific.
How to publish one: arctl init agent summarizer --framework adk --language python --model-provider gemini --model-name gemini-2.5-flash scaffolds a project; arctl build summarizer/ --push builds + pushes the image; arctl apply -f summarizer/agent.yaml registers it. The full-stack.yaml example shows the agent + every dependency in one file.
🔌 MCPServer ar.dev/v1alpha1 · tagged
An MCPServer describes a tool surface — usually one or more
MCP tools the agent can call. Two flavours: a bundled
server (the registry pulls/runs it from a package — npm, pypi, oci, mcpb)
and a remote server (the registry doesn't run it, it
just references an already-running endpoint, e.g. a SaaS MCP).
MCPServer · bundled (OCI image) · stdio transportthe registry runs the container
# scaffold a starter MCP project (mcp.yaml + arctl.yaml + .env)
arctl init mcp acme/fetch --framework fastmcp --language python
# build the container image and push it
arctl build ./fetch --push
# register the manifest with the registry
arctl apply -f ./fetch/mcp.yaml
# inspect what was stored
arctl get mcp acme/fetch -o yaml
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
name: acme/fetch # namespace/name is conventional for MCP names
tag: stable
spec:
title: Fetch Server
description: "Fetches and extracts content from URLs."
source:
package:
registryType: oci # oci | npm | pypi | mcpb | nuget
identifier: ghcr.io/acme/fetch:1.0.0
version: "1.0.0" # optional — derived from identifier if blank
runtimeHint: docker # hint for the local runtime
transport:
type: stdio # stdio | sse | streamable-http
environmentVariables:
- name: FETCH_TIMEOUT_MS
value: "8000"
- name: FETCH_API_KEY
isSecret: true
isRequired: true
What gets validated, what gets stored
registryType- Where the package lives. Each value has its own validator —
ocirequires the image to carry aLABEL io.modelcontextprotocol.server.nameownership annotation if you're publishing to the public MCP catalogue; private deployments can turn that check off.npmcalls the npm registry,pypicalls PyPI,mcpblooks for an MCP bundle archive. transport.typestdiofor containers and local binaries (most common — kagent's MCP runtime image talks stdio),ssefor HTTP Server-Sent-Events MCP servers,streamable-httpfor the newer chunked-HTTP MCP transport. agentgateway speaks all three and bridges between them, which is the point of going through it.environmentVariables[]- The env vars the runtime injects when starting the container.
isSecret: truemeans the registry surfaces it as a secret in the UI;isRequired: truemeans the deploy fails if no value is supplied. Values can be set per-Deployment viaspec.env— see the Deployment section.
For npm / pypi packages, identifier is the package name and version is the version — at deploy time the runtime hint decides whether the local runtime invokes npx or uvx. runtimeArguments and packageArguments (omitted above) let you wire positional / named arguments through to the package's entry point — the schema is the same as the upstream MCP registry's.
MCPServer · remote (pre-deployed SaaS / cloud endpoint)registry doesn't run anything — just references
# remote MCPServers are hand-authored — there's no init template
# (no container to build; the URL is the artifact).
# Write the YAML, then:
arctl apply -f databricks-unity-catalog.yaml
# confirm it's stored
arctl get mcp com.databricks.cloud.workspace/unity-catalog -o yaml
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
name: com.databricks.cloud.workspace/unity-catalog
tag: stable
spec:
title: Databricks Unity Catalog
description: "Pre-deployed Unity Catalog MCP server hosted by Databricks."
remote:
type: streamable-http # sse | streamable-http
url: https://workspace.cloud.databricks.com/mcp
headers:
- name: Authorization
value: "Bearer {{ .Secrets.databricks_token }}"
Use this when the MCP server already exists
spec.remote(notspec.source)- Switches the kind from "registry deploys this" to "registry catalogs this and routes through agentgateway". There is no
DeploymentAdapter.Applystep for remote MCPServers — the workload is somebody else's problem. What you get from the registry is governance: a name, a tag, audit trail, and the gateway config that lets approved agents reach the URL. headers[]- HTTP headers the gateway should set on the upstream call. Templated values (
{{ .Secrets.* }}) resolve through agentregistry's secret store at deploy time — never put raw bearer tokens in the YAML.
A bundled MCPServer carries spec.source; a remote MCPServer carries spec.remote. The validator rejects manifests that set both. Agents reference both kinds with the same mcpServers: [{kind: MCPServer, name: …}] ref — they don't need to know which kind it is.
📚 Skill & Prompt ar.dev/v1alpha1 · tagged
Both kinds carry knowledge rather than runnable code. A
Skill is a SKILL.md bundle (markdown + code
examples + reference docs) that an agent loads to extend what it knows
how to do. A Prompt is a reusable instruction template you
can version and share across agents. Neither one has a runtime — they
just exist to be referenced.
Skill · package agent knowledge from a git repoSKILL.md + supporting files
# scaffold a starter SKILL.md bundle + skill.yaml envelope
arctl init skill summarize
# commit + push the skill folder to a git repo, then fill
# spec.source.repository in skill.yaml — and register it:
arctl apply -f skill.yaml
# fetch a previously-published skill back to disk for inspection
arctl pull skill summarize
apiVersion: ar.dev/v1alpha1
kind: Skill
metadata:
name: summarize
tag: stable
spec:
title: Summarization
description: "How to write concise, accurate summaries with bullet points."
source:
repository:
url: https://github.com/acme/agent-skills
branch: main
subfolder: skills/summarize # SKILL.md + assets live here
commit: e1f2a3b… # optional — pin to a commit SHA
What's in a skill, and how an agent uses it
source.repository- The skill content is in git, not in the YAML.
subfolderis the path inside the repo; the runtime fetches the folder, looks forSKILL.md, and surfaces the bundle to the agent.commitpins to a specific SHA — without it the runtime resolves the branch at deploy time, which is fine for dev and dangerous for prod. - How the agent loads it
- The Agent kind doesn't have a
skillsfield today — that ref was removed in the v1alpha1 audit because it was ADK-Python-specific. What you do instead: build a Skill into the agent's container image atarctl buildtime, or have the agent's runtime pull the skill on startup using a name baked into its config. The registry is the source of truth for "this skill exists at this version"; the runtime decides when to load it.
arctl init skill summarize scaffolds a starter SKILL.md; arctl pull skill summarize fetches the skill folder back to disk for inspection. The same tag-based identity as Agent/MCPServer applies — --tag stable, --tag 1.2.0, --all-tags.
Prompt · reusable system / instruction templateinline content, versioned
# scaffold a prompt.yaml with an empty spec.content block
arctl init prompt summarizer-system-prompt
# fill in spec.content, then register
arctl apply -f prompt.yaml
# publish a second revision under a different tag
arctl apply -f prompt.yaml --tag experiment-shorter
apiVersion: ar.dev/v1alpha1
kind: Prompt
metadata:
name: summarizer-system-prompt
tag: stable
spec:
description: "Default system prompt for summarization agents."
content: |
You are a document summarization assistant. Given a document,
produce a concise summary that captures the key points.
Guidelines:
- Keep summaries under 200 words
- Preserve factual accuracy
- Use bullet points for lists of 3+ items
- Highlight any action items or deadlines
When to use it
spec.content- The prompt body is stored inline — that's why
Promptdoesn't have asource. For tiny prompts (one paragraph, a few rules) this is the right shape. For prompts with bundled examples, code snippets and reference docs, use aSkillinstead so it lives in git. - Versioning + reuse
- Prompts are tagged the same way artifacts are, so you can publish
summarizer-system-prompt:stableandsummarizer-system-prompt:experiment-shorterside by side, and have different agents pin to different revisions.arctl get prompt summarizer-system-prompt --all-tagslists every revision.
Same as Skill — there is no prompts: ref on the Agent kind today. The agent's runtime is responsible for fetching summarizer-system-prompt:stable by name when it starts. Prompts are still useful even without a ref because they're discoverable from the catalogue, which is the actual point of the registry — a prompt review board can find and approve them.
🛠️ Runtime ar.dev/v1alpha1 · mutable
A Runtime is a connection handle to one execution target —
"the local docker daemon", "the cluster at api.kagent-dev.internal", "the
AWS account where Bedrock AgentCore lives". spec.type picks
which DeploymentAdapter reconciles it; spec.config
is the adapter-specific config map. OSS ships Local and
Kubernetes; enterprise adds Kagent,
BedrockAgentCore and GeminiAgentRuntime.
Runtime · Local · docker-compose on this machineOSS · default for arctl run
# Runtimes are control-plane objects — no init scaffolding,
# you author the YAML and apply it.
arctl apply -f local-runtime.yaml
# list registered runtimes
arctl get runtime
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
name: local
spec:
type: Local
# No config — Local picks up the daemon's docker socket.
telemetryEndpoint: http://otel-collector:4318
What "Local" actually does
spec.type: Local- Dispatches to the local-runtime adapter. On Apply, the adapter renders an
agent-gateway.yaml+docker-compose.yamlin the agentregistry runtime directory, then runsdocker compose up. Both files together describe the agentgateway service, every deployed MCP backend, and every deployed agent. - No
spec.config - Local is the simplest type — it reads the daemon directory it was constructed with, not any field in the manifest. The validator still requires
spec.type; nothing else. telemetryEndpoint- Exported as
OTEL_EXPORTER_OTLP_ENDPOINTinto every workload this Runtime serves. Telemetry is a property of where things run, not of the individual Deployment — every container started by this Runtime gets the same endpoint, so traces show one consistent picture.
Runtime · Kubernetes · kagent-equipped clusterOSS · in-cluster or external
# author the YAML — spec.config.kubeconfig if registering a remote
# cluster, or spec.config.inCluster: true for in-cluster pods
arctl apply -f cluster-prod-runtime.yaml
arctl get runtime cluster-prod -o yaml
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
name: cluster-prod
spec:
type: Kubernetes
config:
# All controller-runtime client knobs:
inCluster: true # use the pod's service account
namespace: kagent # default namespace for kagent CRDs
# kubeconfig: | # alternative: explicit kubeconfig
# apiVersion: v1
# ...
telemetryEndpoint: http://otel-collector.observability:4318
What "Kubernetes" actually does
spec.type: Kubernetes- Dispatches to the Kubernetes-runtime adapter. On Apply, the adapter builds a controller-runtime client from
spec.config, translates the Deployment's Agent / MCPServer target into the corresponding kagent Agent CRD or kmcp MCPServer CRD, and applies them. Removal is by label selector — every kagent resource owned by this Deployment carries adeploymentIDlabel so a single delete-by-label sweep cleans up. config.inClustervsconfig.kubeconfiginCluster: truemeans the agentregistry pod uses its own service account — fine when agentregistry runs in the cluster it's deploying to.kubeconfigembeds the full kubeconfig as a string and lets agentregistry deploy into a different cluster, which is the multi-cluster operator pattern.- Note on "Kagent" type
- The enterprise build also registers a separate
Kagentruntime type that talks to the kagent controller directly via its REST API (kagentUrl: http://kagent-controller.kagent:8083) instead of writing CRDs. Same effect — different mechanism — and it's how Solo Enterprise for kagent's UI sees agents the registry has registered.
Adapter dispatch is exact-match on the canonical CamelCase value (Local, Kubernetes, Kagent, BedrockAgentCore, GeminiAgentRuntime). You can write any casing in YAML; Runtime.Validate() canonicalises in place.
Runtime · BedrockAgentCore · AWS Bedrock runtimeenterprise-only
# enterprise-only adapter — the OSS registry will store the YAML
# but the reconciler rejects Deployments with "unknown runtime type"
# until the enterprise BedrockAgentCore adapter is registered.
arctl apply -f aws-bedrock-runtime.yaml
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
name: aws-bedrock-prod
spec:
type: BedrockAgentCore
config:
region: us-east-1
# IAM role agentregistry assumes to create / invoke AgentCore runtimes.
# SigV4 calls to bedrock-agentcore.us-east-1.amazonaws.com are signed
# with credentials minted from this role.
iamRoleArn: arn:aws:iam::253915036081:role/agentregistry-deployer
# Optional default execution role passed to AgentCore on create.
executionRoleArn: arn:aws:iam::253915036081:role/AgentCoreRuntimeRole
telemetryEndpoint: https://otel-gateway.acme.com:4318
What the BedrockAgentCore adapter does on Apply
spec.type: BedrockAgentCore- Enterprise-only. On Apply, the adapter assumes
iamRoleArnvia STS, builds aCreateAgentRuntimerequest body from the Deployment's Agent target, SigV4-signs the HTTPS call (service =bedrock-agentcore, region = the configured region), and POSTs to AWS Bedrock AgentCore. AWS returns a runtime ARN likearn:aws:bedrock-agentcore:us-east-1:253915036081:runtime/summarizer-9f— the adapter stamps that intoDeployment.statusand into the deployment's runtime annotations. - Why this isn't OSS
- The Bedrock adapter and the equivalent
GeminiAgentRuntimeadapter are part of the governance story Solo Enterprise for agentregistry sells — they tie cloud-hosted runtimes to the same audited catalogue the in-cluster ones live in. The OSS registry is happy to store aRuntimewithtype: BedrockAgentCore, but without the enterprise adapter registered the reconciler will reject the Deployment with "unknown runtime type".
SigV4 in one line: AWS rejects any unsigned API call. SigV4 means the request carries an Authorization: AWS4-HMAC-SHA256 … header whose signature is an HMAC-SHA256 over (request method + URI + sorted canonical headers + payload hash), keyed by a chain derived from your AWS secret + date + region + service. The Bedrock adapter does this signing in-process every Apply call.
🚀 Deployment ar.dev/v1alpha1 · mutable
A Deployment is the join: which artifact, on which
runtime, with which per-instance overrides. Identity is
namespace + name only — re-applying the same name mutates the existing
row, with status conditions tracking convergence. Multiple Deployments of
the same Agent on the same Runtime are allowed as long as they have
distinct names — that's how you do canary / dev-vs-prod splits without
forking the Agent.
Deployment · deploy the summarizer agent on KubernetestargetRef + runtimeRef + overrides
# Deployments are control-plane objects — no init scaffolding.
# Author the YAML pointing at an existing Agent + Runtime, then:
arctl apply -f summarizer-prod-deployment.yaml
# observe convergence (Progressing → Ready or Degraded)
arctl get deployment summarizer-prod -o yaml
arctl watch deployment summarizer-prod
# tear it down (sets desiredState: undeployed and removes the row)
arctl delete deployment summarizer-prod
apiVersion: ar.dev/v1alpha1
kind: Deployment
metadata:
name: summarizer-prod
spec:
targetRef:
kind: Agent # must be Agent or MCPServer
name: summarizer
tag: stable # pin a revision — omit for "latest at apply time"
runtimeRef:
kind: Runtime
name: cluster-prod
desiredState: deployed # deployed (default) | undeployed
env: # per-instance env overrides
LOG_LEVEL: info
OPENAI_API_KEY: "{{ .Secrets.openai_prod }}"
runtimeConfig: # adapter-specific extras
replicas: 3
requests:
cpu: "500m"
memory: "512Mi"
What each field controls
targetRef+runtimeRef- Both required. The Reconciler resolves them at Apply time:
targetRefmust point to an existing Agent or MCPServer (other kinds are rejected by the per-adapterSupportedTargetKinds);runtimeRefmust point to an existing Runtime whosespec.typehas a registered adapter. Both refs are checked structurally before anything else happens. desiredState- Lifecycle intent.
deployed(or empty) callsDeploymentAdapter.Apply— go ensure the workload runs.undeployedroutes straight toRemove— tear down external state, leave the row. Soft-delete (a DELETE on the row) also firesRemoveon the way out. Both paths are idempotent. env- Per-instance environment variables, layered on top of whatever the Agent / MCPServer manifest declares. This is the right place for environment-specific secrets —
{{ .Secrets.* }}templating resolves through the secret store at deploy time so the manifest itself stays cleanable. runtimeConfig- Free-form, adapter-specific. The Kubernetes adapter reads
replicasand resource requests; the Local adapter ignores them. Putting something here that the target adapter doesn't understand is silently dropped — there's no schema validation on this map.
Status conditions: Apply returns Progressing=True immediately. The adapter's watch loop later flips Ready=True when the workload converges. Permanent failures surface as Degraded=True with a reason; transient ones bubble back as a returned error and the reconciler retries. arctl get deployment summarizer-prod -o yaml shows the live status block.
Multi-resource apply · agent + MCP server + runtime + deployment in one filerecommended workflow
# one file, every dependency — documents are applied in order so refs resolve
arctl apply -f stack.yaml
# override the registry endpoint (defaults to http://localhost:12121)
arctl apply -f stack.yaml --registry-url https://registry.acme.com
# preview without writing
arctl apply -f stack.yaml --dry-run
# pipe from stdin
cat stack.yaml | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: MCPServer
metadata:
name: acme/fetch
tag: stable
spec:
title: Fetch Server
source:
package:
registryType: oci
identifier: ghcr.io/acme/fetch:1.0.0
runtimeHint: docker
transport: { type: stdio }
---
apiVersion: ar.dev/v1alpha1
kind: Agent
metadata:
name: summarizer
tag: stable
spec:
source: { image: ghcr.io/acme/summarizer:v1.0.0 }
modelProvider: gemini
modelName: gemini-2.5-flash
description: "Summarizes documents using Gemini."
mcpServers:
- kind: MCPServer
name: acme/fetch
tag: stable
---
apiVersion: ar.dev/v1alpha1
kind: Runtime
metadata:
name: local
spec:
type: Local
---
apiVersion: ar.dev/v1alpha1
kind: Deployment
metadata:
name: summarizer-dev
spec:
targetRef: { kind: Agent, name: summarizer, tag: stable }
runtimeRef: { kind: Runtime, name: local }
Why one file is the right shape
- Document order = apply order
- The
POST /v0/applybatch endpoint applies documents in order, so the MCPServer lands before the Agent that references it, the Agent lands before the Deployment that targets it, and the Runtime is created before anything tries to reconcile against it. Reverse the order and the agent apply will fail with "MCPServer/acme/fetch not found". - Per-doc results
- The response is one status entry per document —
created,configured,unchanged, orfailed. A failure mid-batch doesn't roll back the successes; everything that succeeded before the failure stays in the database. Re-applying is idempotent, so the recovery path is "fix the broken doc and re-apply the whole file".
Run with arctl apply -f stack.yaml. --registry-url overrides the default http://localhost:12121. Idempotent — re-applying the same file produces the same set of rows, with configured / unchanged status verbs rather than created.
All six kinds, one table
The full reference. Identity is what the registry rows are keyed on. Adapter / consumer is what does something with the row at reconcile time — for content artifacts it's whichever Deployment references them; for control objects it's a DeploymentAdapter.
| Kind | Group | apiVersion | Identity | What it models | Adapter / consumer |
|---|---|---|---|---|---|
Agent |
artifact | ar.dev/v1alpha1 |
namespace / name / tag | An agent: image + model + MCP refs. | referenced by Deployment.targetRef |
MCPServer |
artifact | ar.dev/v1alpha1 |
namespace / name / tag | An MCP tool surface — bundled (package) or remote (URL). | referenced by Agent.mcpServers · Deployment.targetRef |
Skill |
artifact | ar.dev/v1alpha1 |
namespace / name / tag | A SKILL.md knowledge bundle from git. | consumed by agent runtimes at startup |
Prompt |
artifact | ar.dev/v1alpha1 |
namespace / name / tag | A reusable, versioned instruction template (inline content). | consumed by agent runtimes at startup |
Runtime |
control | ar.dev/v1alpha1 |
namespace / name | An execution target — Local / Kubernetes / Kagent / BedrockAgentCore / GeminiAgentRuntime. | DeploymentAdapter dispatch key |
Deployment |
control | ar.dev/v1alpha1 |
namespace / name | The join: target × runtime × overrides × lifecycle intent. | reconciled by the matching DeploymentAdapter |
How this connects to the governance story
Governance is what these six kinds buy you. Every agent and every MCP
server an application can reach has to exist as a row first — Agent,
MCPServer, target — and the runtime only ever sees what the registry
compiled out of those rows. If it isn't registered, it isn't callable.
That's the allowlist, and it's the whole point: a single audited list
of what's approved to run, with a CLI (arctl) and an API
in front of it so platform teams can review and admit things before
application teams pick them up.
Today the enforcement is config-time. When
arctl deploy runs, the registry generates the agentgateway
routing table (Local) or the kagent CRDs (Kubernetes) from exactly the
rows the Deployment references. A tool whose ARN or package isn't in
the registry doesn't get a route — the gateway has no way to call it,
because it was never told it existed.
The enterprise roadmap moves this to request-time. The May 2026 release update lists Access Policy as the next piece — agentgateway will pull from the registry continuously and check each call against it, instead of relying on whatever snapshot was compiled at deploy. Same six kinds, same YAML. The difference is the gateway asks the registry on the hot path instead of reading a frozen routing table, so revoking access to a tool takes effect on the next call rather than the next redeploy.
For implementation depth the agentregistry-dev/agentregistry repo is the source of truth — the same six kinds, plus the adapter interface that Local, Kubernetes and the enterprise hosted runtimes plug into. Once you've got the kinds straight, the Agentic / MCP lab is the next stop for what agentgateway does with them.