MastertheMesh
Solo Enterprise for kagent · Reference
Visual reference

kagent CRDs — a visual map

TO
Tom O'Rourke
EMEA Field CTO · Solo.io

Every CRD kagent ships today — Agent, SandboxAgent, ToolServer, RemoteMCPServer, ModelConfig, ModelProviderConfig, Memory — plus the two Solo Enterprise bolt-ons, AccessPolicy and KubernetesCluster. What each one models, what the controller does with it, and copy-paste YAML.

kagent kubectl · kagent-cli A2A protocol MCP kagent-enterprise

What kagent is

kagent runs AI agents on Kubernetes the same way you run Deployments. You write an Agent CRD; the controller reconciles a pod that speaks A2A over HTTP and MCP to its tools. The agent pulls its model from a ModelConfig, its tools from ToolServer or RemoteMCPServer, and — on Solo Enterprise — is gated by an AccessPolicy and registered on a KubernetesCluster.

Declarative An Agent CRD is the spec; the controller runs the LLM loop.
MCP tools Tools come from ToolServer (in-cluster) or RemoteMCPServer (URL).
A2A Agents talk to other agents over the A2A HTTP protocol.
Multi-provider OpenAI, Anthropic, Gemini, Vertex, Azure, Bedrock, Ollama, SAP AI Core.
Enterprise auth AccessPolicy projects to Istio AuthorizationPolicy on mesh clusters.

OSS upstream is kagent-dev/kagent; the storage version moved to kagent.dev/v1alpha2 for Agent, SandboxAgent, ModelConfig, ModelProviderConfig and RemoteMCPServer. ToolServer and Memory are still served at v1alpha1. Solo Enterprise adds two more groups — policy.kagent-enterprise.solo.io/v1alpha1 (AccessPolicy) and kagent-enterprise.solo.io/v1alpha1 (KubernetesCluster) — plus a management-plane platform.solo.io/v1alpha1 KubernetesCluster for multi-cluster.

This page is the reference for the nine kinds shipped in the kagent-enterprise-crds + management-crds Helm charts — what each one models, what fields it carries, and copy-paste YAML you can kubectl apply -f.

If you've written Istio or kgateway YAML, kagent will feel familiar — same apiVersion / kind / metadata / spec / status envelope, same kubectl UX, same controller-reconciles-into-pods loop. The twist is that the pod the controller produces is an AI agent: it speaks A2A to other agents and MCP to its tools, and the LLM behind it gets swapped by editing a ModelConfig, not by rebuilding the image.

What's OSS and what's Enterprise

kagent OSS · 7 CRDs

Apache-2.0, upstream kagent-dev/kagent. Group kagent.dev.

  • Agent — the agent itself, Declarative or BYO
  • SandboxAgent — ephemeral / scratch agent
  • ModelConfig — provider + model + key reference
  • ModelProviderConfig — provider-wide defaults (Bedrock, Vertex, Azure)
  • ToolServer — an in-cluster MCP tool surface
  • RemoteMCPServer — an MCP server reached by URL
  • Memory — Pinecone-backed long-term store

Solo Enterprise additions · 3 CRDs

Same OSS CRDs reshipped (no fork), plus bolt-on kinds in two more groups.

  • AccessPolicy · policy.kagent-enterprise.solo.io — gates agent invocations by OIDC group, ServiceAccount or peer Agent. On mesh clusters it projects to an Istio AuthorizationPolicy so enforcement runs in the sidecar / ztunnel.
  • KubernetesCluster · kagent-enterprise.solo.io — one per dataplane, holds the cluster domain.
  • KubernetesCluster · platform.solo.io — the management-plane roster the Solo UI reads from for multi-cluster.

"Enterprise" is not a fork. Solo Enterprise for kagent ships the exact same OSS CRDs at the same storage version. The enterprise install adds two extra API groups (policy.kagent-enterprise.solo.io, kagent-enterprise.solo.io), a management-plane chart, a UI, OIDC, and an opinionated OpenTelemetry pipeline. Everything you can do with OSS still works — the enterprise CRDs sit alongside, they don't replace.

The kagent-enterprise-crds Helm chart in this repo is pinned to OSS commit fb611b089985 from kagent-dev/kagent, plus the enterprise AccessPolicy CRD; the management-crds chart adds the two KubernetesCluster kinds. They install side-by-side, no overlap, no conflict.

How the pieces fit together

CLIENTS kubectl · A2A clients · Solo UI CRDs · kagent.dev (and enterprise groups) RECONCILED · pods + Istio AuthZ + cluster roster kubectl apply · get · describe A2A clients other Agents · external apps · curl Solo UI enterprise — OIDC-gated kagent-cli / Claude scaffold + apply Agent YAML kagent controller RECONCILE · A2A DISPATCH · MCP BRIDGE · STATUS Watches every kagent.dev CRD + enterprise groups when installed Renders Agent → Deployment + Service runs the LLM loop inside the pod Projects AccessPolicy → Istio AuthZ enterprise only · mesh-aware KNOWLEDGE · models & memory kagent.dev/v1alpha2 (Memory: v1alpha1) ModelConfig provider + model + apiKeySecret ModelProviderConfig provider-wide defaults / endpoint Memory Pinecone (only provider today) referenced by Agent.spec.declarative.modelConfig + memory UNIT OF WORK · the agent kagent.dev/v1alpha2 · stored shape Agent spec.type: Declarative | BYO SandboxAgent same shape — ephemeral Declarative: kagent runs the LLM loop BYO: you ship the container controller reconciles → Deployment + Service TOOL SURFACE · MCP kagent.dev (ToolServer v1alpha1) ToolServer stdio · sse · streamableHttp (one of) RemoteMCPServer URL + protocol (SSE | STREAMABLE_HTTP) Agent.spec.declarative.tools[].mcpServer refs either kind by name + apiGroup allowedHeaders · requireApproval · toolNames ENTERPRISE 2 extra groups AccessPolicy policy.kagent- enterprise.solo.io ALLOW | DENY → Istio AuthZ KubernetesCluster kagent-enterprise .solo.io + platform.solo.io dataplane + mgmt Agent pod Deployment + Service · A2A on HTTP Loads the model from ModelConfig, opens MCP sessions to its ToolServers, streams turns back over A2A. Declarative: kagent runs the loop. BYO: your container does. Istio AuthorizationPolicy enterprise · projected from AccessPolicy When the cluster has Istio installed the controller projects every AccessPolicy into an Istio AuthorizationPolicy, enforced in the sidecar / ztunnel. Cluster roster enterprise · mgmt plane platform.solo.io KubernetesCluster on the mgmt plane lists every workload cluster the Solo UI can address. dataplane has its own (clusterDomain only) controller reconcile loop
unit of work (Agent / SandboxAgent) tool surface (ToolServer / RemoteMCPServer) knowledge (ModelConfig / ModelProviderConfig / Memory) enterprise additions kagent controller

How to read it: clients on top, three OSS CRD families in the middle (knowledge on the left, the Agent in the centre, tool surfaces on the right) with the two enterprise groups boxed off on the far right. The controller reconciles every Agent into a Deployment + Service that speaks A2A over HTTP and opens MCP sessions to its ToolServers. When the enterprise build runs on a mesh cluster, each AccessPolicy is also projected into an Istio AuthorizationPolicy — enforcement runs in the sidecar / ztunnel, not in the agent process. The KubernetesCluster kinds register each cluster on the management plane.

The CRDs, group by group

Four sections, in the order they show up in the diagram: the agent itself, the tool surfaces it calls, the knowledge it pulls from, and the enterprise bolt-ons. The colour stripe matches the diagram. Every YAML below was written against the actual CRD OpenAPI schema shipped in the kagent-enterprise-crds / management-crds charts — no invented fields.

🤖 Agent kagent.dev/v1alpha2 · namespaced

The unit of work. spec.type picks the shape: Declarative (the controller runs the LLM loop, you just describe behaviour) or BYO (you ship the container, kagent handles identity, A2A wiring and discovery). 90% of the time you want Declarative.

Agent · Declarative — model + system prompt + one MCP toolthe common shape
Apply it
# apply the agent
kubectl apply -f sre-copilot.yaml

# watch the controller produce the Deployment + Service
kubectl get agent,deploy,svc -n kagent-system -l app=sre-copilot

# tail the agent's pod logs to see A2A turns
kubectl logs -n kagent-system -l app=sre-copilot -f
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
  name: sre-copilot
  namespace: kagent-system
spec:
  type: Declarative                # Declarative | BYO  (default: Declarative)
  description: "Answers questions about cluster state."
  declarative:
    modelConfig: gpt-4o            # name of a ModelConfig in this namespace
    systemMessage: |
      You are an SRE assistant. Use kubectl tools to investigate.
      Be concise; show your reasoning when you take an action.
    runtime: python                # python | go  (default: python)
    stream: true
    tools:
      - type: McpServer            # McpServer | Agent  (subagent ref)
        mcpServer:
          apiGroup: kagent.dev
          kind: ToolServer         # or RemoteMCPServer
          name: kubectl-mcp
          # toolNames:             # optional — restrict which tools are exposed
          #   - get
          #   - describe
          # requireApproval: false

What each field controls

spec.type
Either Declarative (kagent runs the LLM loop using spec.declarative) or BYO (you supply a spec.byo.deployment PodSpec — kagent just gives it an A2A address and wires discovery). The schema defaults to Declarative.
declarative.modelConfig
Just a string — the metadata.name of a ModelConfig in the same namespace. Omit it and the controller looks for one called default-model-config. There is no inline model config in the Agent itself.
declarative.tools[]
Each tool is either type: McpServer (with mcpServer.apiGroup / kind / name referencing a ToolServer or RemoteMCPServer) or type: Agent (with agent.apiGroup / kind / name referencing a peer agent as a subagent). mcpServer.toolNames narrows which of the server's tools are exposed; omit to allow all.
declarative.runtime
python (default) or go. Picks which kagent runtime image the controller schedules — the Python ADK loop or the Go reimplementation.
declarative.systemMessage / systemMessageFrom
Inline string, or a reference into a ConfigMap/Secret if you want it managed separately. Mutually exclusive.

Other fields on spec: byo (the BYO PodSpec), sandbox (sandbox config for code execution), skills (string list — agent self-described skills, surfaced in the Solo UI), allowedNamespaces (which namespaces are allowed to invoke this agent over A2A). Inside declarative you'll also find a2aConfig, context, deployment (PodSpec overrides), executeCodeBlocks, memory, promptTemplate.

Agent · BYO — you ship the containerescape hatch
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
  name: legacy-router
  namespace: kagent-system
spec:
  type: BYO
  description: "Routes requests across a legacy agent fleet."
  byo:
    deployment:                   # PodSpec the controller schedules
      replicas: 2
      template:
        spec:
          containers:
            - name: agent
              image: ghcr.io/acme/legacy-router:1.4.0
              ports:
                - containerPort: 8080  # must speak A2A on this port
              env:
                - name: ROUTING_TABLE_URL
                  value: http://routes.acme.svc:8080

When to reach for BYO

You already have an agent that speaks A2A
An ADK/Python/LangGraph/whatever process you've packaged. BYO registers it with the kagent controller so the rest of the platform — discovery, AccessPolicy, the Solo UI — treats it the same as a Declarative Agent. No model loop, no MCP wiring; that's your container's job.
You need a runtime kagent doesn't ship
The Declarative shape supports runtime: python and runtime: go today. Anything else — TypeScript, Rust, a heavy LangChain stack — goes through BYO.

The byo.deployment is a stripped-down PodSpec (template + replicas). The controller fills in the labels and service it needs to wire discovery; you're responsible for the image and the env. The contract is "speak A2A on the port you advertise" — that's it.

🧪 SandboxAgent kagent.dev/v1alpha2 · namespaced

Same shape as Agent — same spec.type, same declarative / byo sub-blocks — but explicitly flagged as ephemeral. Use it for scratch work, evals, one-shot runs where the agent shouldn't stick around as a long-lived service.

SandboxAgent · throwaway agent for an eval runsame shape as Agent
apiVersion: kagent.dev/v1alpha2
kind: SandboxAgent
metadata:
  name: eval-summarizer-2026-05-19
  namespace: kagent-evals
spec:
  type: Declarative
  description: "Eval run for summarizer prompt v3 — delete after."
  declarative:
    modelConfig: gpt-4o
    systemMessage: "Summarize the input into 3 bullets."
    runtime: python

The schema is identical to Agent — same fields, same validators. The difference is operational: SandboxAgent rows are expected to be short-lived, so the Solo UI lists them separately and cleanup tooling can sweep them by Kind. If you find yourself keeping a SandboxAgent around, promote it to Agent instead.

🔌 ToolServer kagent.dev/v1alpha1 · namespaced

An MCP tool surface the controller can run in the cluster. Exactly one of spec.config.stdio, spec.config.sse or spec.config.streamableHttp is set — that picks the transport. The stdio shape is the common one: the controller starts the binary, opens a stdio MCP session, and proxies it to any Agent that references this ToolServer.

ToolServer · stdio — runs a binary in the clusterthe common shape
apiVersion: kagent.dev/v1alpha1
kind: ToolServer
metadata:
  name: kubectl-mcp
  namespace: kagent-system
spec:
  description: "kubectl as MCP tools — get/describe/logs/exec."
  config:
    stdio:
      command: /usr/local/bin/kubectl-mcp     # required
      args:
        - --read-only
      env:
        - name: KUBECONFIG
          value: /var/run/secrets/kubernetes.io/serviceaccount/kubeconfig
      envFrom:
        - secretRef:
            name: kubectl-mcp-creds
      readTimeoutSeconds: 10                  # default: 10

One transport per ToolServer

config.stdio
Most MCP servers ship as a binary that speaks JSON-RPC over stdio. command is required; args / env / envFrom behave like a PodSpec container. readTimeoutSeconds bounds how long the controller waits on the binary per read.
config.sse
Server-Sent-Events MCP transport. Use this for tool servers that already run as long-lived HTTP services and don't fit the spawn-a-binary shape.
config.streamableHttp
The newer chunked-HTTP MCP transport. Functionally equivalent to SSE for kagent's purposes; pick whichever your tool server speaks.

An Agent references this resource as tools[].mcpServer.kind: ToolServer, name: kubectl-mcp, apiGroup: kagent.dev. The optional mcpServer.toolNames field on the Agent narrows which of the server's tools that particular agent is allowed to call.

🌐 RemoteMCPServer kagent.dev/v1alpha2 · namespaced

Same idea as ToolServer, but for an MCP server you don't run — a SaaS endpoint, a sidecar in another team's namespace, a cloud-hosted service. The controller doesn't spawn anything; it just opens an HTTP/SSE session at spec.url and bridges it to Agents that reference this kind.

RemoteMCPServer · external SaaS MCP, bearer-token authkagent doesn't run it — just reaches it
apiVersion: kagent.dev/v1alpha2
kind: RemoteMCPServer
metadata:
  name: linear-issues
  namespace: kagent-system
spec:
  description: "Linear issues MCP — search, comment, close."
  url: https://mcp.linear.app/v1                 # required
  protocol: STREAMABLE_HTTP                       # SSE | STREAMABLE_HTTP  (default: STREAMABLE_HTTP)
  timeout: 30s
  sseReadTimeout: 5m
  terminateOnClose: true
  headersFrom:
    - name: Authorization
      valueFrom:
        type: Secret
        name: linear-mcp-token
        key: bearer                               # value becomes "Bearer "
  # allowedNamespaces:                            # optional — restrict who can ref this
  #   from: Selector
  #   selector:
  #     matchLabels:
  #       team: platform

What's required, what's a knob

spec.url + spec.description
The only two required fields. url is the MCP endpoint; description shows up in the Solo UI's tool picker.
spec.protocol
Either SSE or STREAMABLE_HTTP. Defaults to STREAMABLE_HTTP — match whatever the remote server speaks.
spec.headersFrom[]
Headers added to every outbound MCP request. Each entry is a ValueRef — either value: inline or valueFrom: { type: Secret|ConfigMap, name, key }. Use Secret refs for tokens; never inline credentials.
spec.allowedNamespaces
Restricts which Agents are allowed to reference this resource. from: All | Selector; with Selector you supply a label selector. Useful when one team owns the MCP integration and others should opt in.

🧠 ModelConfig kagent.dev/v1alpha2 · namespaced

Provider, model name, and a reference to the secret holding the API key. Multiple Agents share one ModelConfig — rotate the key in one place, every Agent that references it picks up the new value on the next reconcile. The provider enum has nine values today: OpenAI, Anthropic, Gemini, GeminiVertexAI, AnthropicVertexAI, AzureOpenAI, Bedrock, Ollama, SAPAICore.

ModelConfig · OpenAI · gpt-4othe minimal shape — three required-ish fields
apiVersion: kagent.dev/v1alpha2
kind: ModelConfig
metadata:
  name: gpt-4o
  namespace: kagent-system
spec:
  provider: OpenAI               # required — enum (see above)
  model: gpt-4o                  # required
  apiKeySecret: openai-key       # secret in the same namespace
  apiKeySecretKey: api-key       # key inside the secret (default: "api-key")
  # apiKeyPassthrough: false     # if true, expect the key in request headers
  openAI:                        # provider-specific subblock (one per provider)
    baseUrl: https://api.openai.com/v1
    organization: org-abc123

What's required, what's per-provider

spec.provider + spec.model
The only two fields the schema marks required. provider picks the SDK shape; model is the model ID the SDK passes through.
spec.apiKeySecret
The name of a Secret in the same namespace — not the value. For SAP AI Core the secret must contain client_id + client_secret instead of an API key. For apiKeyPassthrough: true, the key flows through from the inbound request and this field is ignored.
Provider subblocks
One sibling block per provider — openAI, anthropic, gemini, geminiVertexAI, anthropicVertexAI, azureOpenAI, bedrock, ollama, sapAICore. Each holds the provider-specific knobs that don't fit the generic spec. You only fill in the block matching spec.provider.
spec.defaultHeaders / spec.tls
Cross-provider extras. defaultHeaders is a map of strings injected on every model call; tls carries the usual insecureSkipVerify / caBundle shape for private endpoints.

About the field name — it's apiKeySecret (a string, the secret's name) in v1alpha2. v1alpha1 used apiKeySecretRef; the served-but-not-stored v1alpha1 version is still around for old manifests, but new YAML should target v1alpha2.

ModelConfig · AWS Bedrock · Claude Sonnetprovider subblock + IAM
apiVersion: kagent.dev/v1alpha2
kind: ModelConfig
metadata:
  name: bedrock-sonnet
  namespace: kagent-system
spec:
  provider: Bedrock
  model: anthropic.claude-sonnet-4-20250514-v1:0
  apiKeySecret: aws-bedrock-creds
  bedrock:
    region: us-east-1
    # The bedrock subblock holds region + any model-specific knobs the
    # Bedrock SDK exposes. Auth is via the secret's AWS credentials.

If you have many ModelConfigs pointing at the same Bedrock account, factor the auth + region defaults into a ModelProviderConfig and reference it — that's exactly what that kind is for.

🔧 ModelProviderConfig kagent.dev/v1alpha2 · namespaced

Provider-wide defaults — endpoint, region, auth — so individual ModelConfigs don't have to repeat them. Useful when you have one Bedrock account / one Azure deployment / one Vertex project that backs many models.

ModelProviderConfig · shared Bedrock defaultsendpoint + secret reuse
apiVersion: kagent.dev/v1alpha2
kind: ModelProviderConfig
metadata:
  name: bedrock-prod
  namespace: kagent-system
spec:
  type: Bedrock                  # same enum as ModelConfig.spec.provider
  endpoint: https://bedrock-runtime.us-east-1.amazonaws.com
  secretRef: aws-bedrock-creds   # name of the Secret holding provider auth

How a ModelConfig finds it

spec.type
Same provider enum as ModelConfig.spec.provider. The controller matches a ModelProviderConfig to a ModelConfig by provider type within the same namespace.
spec.endpoint / spec.secretRef
Where to send the calls and what credential to use. Individual ModelConfigs inherit these and only need to specify model + any per-model overrides.

This kind only ships at v1alpha2 — there's no v1alpha1 served version. Safe to write fresh YAML against it.

🗄️ Memory kagent.dev/v1alpha1 · namespaced

Long-term store for things the agent should remember across invocations. The provider enum has exactly one value today: Pinecone. The schema is shaped to be extended (there's room for more providers), but if you're writing YAML right now, Pinecone is the only target the controller knows about.

Memory · Pinecone-backed long-term memorythe only provider shipped today
apiVersion: kagent.dev/v1alpha1
kind: Memory
metadata:
  name: sre-history
  namespace: kagent-system
spec:
  provider: Pinecone              # only enum value today
  apiKeySecretRef: pinecone-key   # note: Ref-style — different from ModelConfig
  apiKeySecretKey: api-key
  pinecone:
    indexHost: https://sre-history-abc.svc.pinecone.io
    namespace: prod
    topK: 5
    scoreThreshold: 0.75
    recordFields:
      - text
      - source
      - ts

Field shape — and the subtle ModelConfig diff

spec.provider
Enum with one value, Pinecone. Pinecone is the only backend wired up in the controller today; the schema is provider-shaped so more can be added without re-versioning.
spec.apiKeySecretRef (vs apiKeySecret)
This kind is still v1alpha1 and uses the older apiKeySecretRef field name — not apiKeySecret like ModelConfig v1alpha2. A small wart, easy to trip on. apiKeySecretKey is the key within the secret.
spec.pinecone
Where the index lives (indexHost), which Pinecone namespace to use, how many results to retrieve (topK), the relevance floor (scoreThreshold), and which record fields to surface back into the agent (recordFields).

An Agent references a Memory via spec.declarative.memory — the controller takes care of the retrieval loop inside the agent runtime.

🛡️ AccessPolicy policy.kagent-enterprise.solo.io/v1alpha1 · ENTERPRISE · namespaced

OSS has no answer for "who is allowed to call this agent". Enterprise's AccessPolicy is that answer. The shape is deliberately Istio-AuthorizationPolicy-shaped — action + from.subjects + targetRef — and on mesh-equipped clusters the controller projects each AccessPolicy into an actual Istio AuthorizationPolicy, so enforcement happens in the sidecar or ztunnel, not in the agent's pod.

AccessPolicy · ALLOW — only the SRE OIDC group may call this AgentUserGroup subject
apiVersion: policy.kagent-enterprise.solo.io/v1alpha1
kind: AccessPolicy
metadata:
  name: sre-copilot-sre-only
  namespace: kagent-system
spec:
  action: ALLOW                          # ALLOW | DENY
  from:
    subjects:
      - kind: UserGroup                  # UserGroup | ServiceAccount | Agent
        userGroup:
          issuer: https://login.acme.com
          claimName: groups
          claimValue: sre
          audiences:
            - kagent
  targetRef:
    kind: Agent                          # Agent | MCPServer
    name: sre-copilot

What every field carries

spec.action
ALLOW or DENY. Required. Multiple AccessPolicies stack the way Istio's do — if there's any ALLOW that matches, traffic is allowed; if any DENY matches, it's denied; DENY wins ties.
spec.from.subjects[]
The source identities the policy covers. kind is one of UserGroup (OIDC group from a JWT), ServiceAccount (standard K8s SA — needs name + namespace), or Agent (a peer kagent Agent — same name + namespace shape). For UserGroup, the userGroup subblock carries issuer / claimName / claimValue / audiences / jwksKey.
spec.targetRef
kind: Agent or kind: MCPServer plus name. When kind: MCPServer you may also set tools[] to narrow which of the server's tools the subjects can invoke; for kind: Agent the tools field is rejected by a CEL validator.
Zero-subjects + wildcard target
The schema permits zero subjects when targetRef.name == "*", and the controller treats that combination as a namespace-wide deny-all-by-default baseline. Subsequent ALLOWs then grant access on top.

Projection to Istio: on clusters where Istio is detected, the controller emits an Istio AuthorizationPolicy that mirrors the AccessPolicy and binds it to the agent's workload. Enforcement runs in the sidecar (Istio classic) or ztunnel (Istio ambient) — the agent process doesn't see denied requests at all. Off-mesh clusters fall back to in-process enforcement.

AccessPolicy · ALLOW — a peer Agent calling a Linear MCP, restricted to two toolsAgent subject, MCPServer target with tools[]
apiVersion: policy.kagent-enterprise.solo.io/v1alpha1
kind: AccessPolicy
metadata:
  name: triage-bot-linear-read-only
  namespace: kagent-system
spec:
  action: ALLOW
  from:
    subjects:
      - kind: Agent
        name: triage-bot
        namespace: kagent-system
  targetRef:
    kind: MCPServer
    name: linear-issues
    tools:                               # only valid when kind: MCPServer
      - search_issues
      - get_issue

tools[] is a server-specific allow-list and the schema is intentionally open (just []string) — kagent has no central registry of MCP tool names, so it can't enum them. The controller validates only that tools is empty unless targetRef.kind == MCPServer.

🌍 KubernetesCluster enterprise · two flavours

Multi-cluster registration. Two kinds, same name, two groups — kagent-enterprise.solo.io for the dataplane (a tiny CRD that just carries the cluster domain) and platform.solo.io for the management plane (the open-spec roster the Solo UI reads from). Both are cluster-scoped.

KubernetesCluster · dataplane · just clusterDomainkagent-enterprise.solo.io/v1alpha1
apiVersion: kagent-enterprise.solo.io/v1alpha1
kind: KubernetesCluster
metadata:
  name: dataplane-eu-1            # cluster-scoped — no namespace
spec:
  clusterDomain: cluster.local    # default: "cluster.local"

The minimal one

spec.clusterDomain
The only field. Service-discovery suffix the controller plugs into outbound URLs (Istio, MCP, A2A). Most clusters leave it at the default cluster.local; some platforms (OpenShift, custom CoreDNS) configure something else and that's what goes here.
Cluster-scoped
One per workload cluster. Installed on every cluster the enterprise dataplane chart targets, alongside the rest of kagent-enterprise-crds.
KubernetesCluster · management plane · open specplatform.solo.io/v1alpha1
apiVersion: platform.solo.io/v1alpha1
kind: KubernetesCluster
metadata:
  name: dataplane-eu-1            # matches the dataplane CR's name
spec: {}                          # schema: open object — fields TBD

The management one

The same name, a different group
This kind lives on the management plane and represents a workload cluster the platform team has onboarded. Same metadata.name as the dataplane CR keeps the two halves obviously paired.
spec shape
The CRD's OpenAPI schema for this version is an open object (type: object with no defined properties) — the management plane reads the row's identity (name + status), and the connection details are wired up out-of-band by the Solo UI / mgmt chart. Expect this to firm up as the management plane API stabilises.
Cluster-scoped
Installed by the management-crds Helm chart on the management cluster only. Solo UI reads the list, presents the cluster picker, and routes API calls to the matching dataplane.

All ten CRDs, one table

The full reference. Stored version is what the API server writes to etcd — older versions are still served for backwards-compat where listed. Family matches the colour stripe used above.

Kind Family Group Stored Scope What it models
Agent work kagent.dev v1alpha2 (v1alpha1 served) Namespaced The agent — Declarative (kagent runs the LLM loop) or BYO (you ship the container).
SandboxAgent work kagent.dev v1alpha2 Namespaced Same shape as Agent, flagged as ephemeral — for evals / scratch runs.
ToolServer tool kagent.dev v1alpha1 Namespaced An MCP tool surface kagent runs in-cluster — stdio / sse / streamableHttp.
RemoteMCPServer tool kagent.dev v1alpha2 Namespaced An external MCP server reached by URL — kagent bridges, doesn't run.
ModelConfig knowledge kagent.dev v1alpha2 (v1alpha1 served) Namespaced Provider + model + API-key secret reference. Shared across Agents.
ModelProviderConfig knowledge kagent.dev v1alpha2 Namespaced Provider-wide defaults (endpoint + secret) shared across many ModelConfigs.
Memory knowledge kagent.dev v1alpha1 Namespaced Long-term store for the agent. Provider enum has one value today: Pinecone.
AccessPolicy enterprise policy.kagent-enterprise.solo.io v1alpha1 Namespaced ALLOW/DENY rules on who can call an Agent / MCPServer. Projects to Istio AuthZ on mesh clusters.
KubernetesCluster enterprise kagent-enterprise.solo.io v1alpha1 Cluster Dataplane cluster registration — carries the cluster domain.
KubernetesCluster enterprise platform.solo.io v1alpha1 Cluster Management-plane roster the Solo UI reads from for multi-cluster.

Where to go next

For implementation depth the kagent-dev/kagent repo is the source of truth for everything in the kagent.dev group. The enterprise CRDs (AccessPolicy, KubernetesCluster) ship in the Solo kagent-enterprise-crds and management-crds Helm charts; the schemas on this page were generated against the versions pinned in those charts.

If you're coming from the catalog side and wondering how Agent / MCPServer rows in ar.dev relate to the kagent CRDs here, that link is one-way and covered in the agentregistry CRDs page — short version: when an agentregistry Deployment reconciles onto a type: Kubernetes runtime, the runtime adapter translates the registry's Agent / MCPServer rows into the kagent.dev / kmcp.dev CRDs and applies them to the cluster. Registry rows are the catalogue you author; the kagent CRDs on this page are what the cluster actually runs.