VirtualMCPServerList is a list of VirtualMCPServer
APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
List of virtualmcpservers. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md
No Additional ItemsVirtualMCPServer is the Schema for the virtualmcpservers API
VirtualMCPServer aggregates multiple backend MCPServers into a unified endpoint
APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations
Each additional property must conform to the following schema
Type: stringCreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.
Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.
DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.
Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.
GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
If this field is specified and the generated name exists, the server will return a 409.
Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.
Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels
Each additional property must conform to the following schema
Type: stringManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object.
No Additional ItemsManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.
APIVersion defines the version of this resource that this field set applies to. The format is "group/version" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.
FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: "FieldsV1"
FieldsV1 holds the first JSON version format as described in the "FieldsV1" type.
Manager is an identifier of the workflow managing these fields.
Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.
Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.
Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over.
Same definition as creationTimestampName must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names
Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces
List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.
No Additional ItemsOwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.
API version of the referent.
If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.
If true, this reference points to the managing controller.
Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names
UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids
An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.
UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids
VirtualMCPServerSpec defines the desired state of VirtualMCPServer
AuthServerConfig configures an embedded OAuth authorization server.
When set, the vMCP server acts as an OIDC issuer, drives users through
upstream IDPs, and issues ToolHive JWTs. The embedded AS becomes the
IncomingAuth OIDC provider — its issuer must match IncomingAuth.OIDCConfigRef
so that tokens it issues are accepted by the vMCP's incoming auth middleware.
When nil, IncomingAuth uses an external IDP and behavior is unchanged.
BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be
included in every client registration. The embedded auth server unions these
scopes into the registered set returned by RFC 7591 Dynamic Client
Registration, so a client that narrows the scope field at /oauth/register
can still request the baseline scopes at /oauth/authorize. All values must
be present in the upstream-derived scopesSupported set; the auth server
fails to start if any value is missing.
Security: every client registered via /oauth/register will gain the
ability to request these scopes at /oauth/authorize, regardless of what
the client itself requested. Keep the baseline narrow (typically
"openid" and "offline_access"). Adding a privileged scope here — e.g.
"admin:read" — would grant it to every DCR-registered client, including
public clients like Claude Code, Cursor, and VS Code.
When cimd.enabled is true, every dynamically resolved CIMD client will
also gain the ability to request these scopes, including third-party
clients resolved from arbitrary HTTPS URLs.
Must contain a maximum of 10 items
^[\x21\x23-\x5B\x5D-\x7E]+$
Must be at least 1 characters long
CIMD configures Client ID Metadata Document support. When omitted, CIMD is disabled.
CacheFallbackTTL is the fixed TTL applied to every cached CIMD document.
Cache-Control header parsing is not yet implemented; all entries use this value.
Format: Go duration string (e.g. "5m", "10m", "1h").
Defaults to 5 minutes when Enabled is true and this field is omitted.
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
CacheMaxSize is the maximum number of CIMD documents held in the LRU cache.
Defaults to 256 when Enabled is true and this field is omitted.
Value must be greater or equal to 1
Enabled activates CIMD client lookup. When false (the default), the AS only
accepts client_id values that were registered via DCR.
DisableUpstreamTokenInjection prevents the embedded auth server from injecting
upstream IdP tokens into requests forwarded to the backend MCP server.
When true, the embedded auth server still handles OAuth flows for clients,
but instead of swapping ToolHive JWTs for upstream tokens the proxy STRIPS
the client's credential headers (Authorization, Cookie, Proxy-Authorization)
after validating the JWT — the backend receives an unauthenticated request.
Use headerForward to attach static credentials (e.g. an API key) if the
backend needs them. Cannot be combined with token exchange or AWS STS,
which would re-add credentials after the strip.
This is useful when the backend MCP server does not require authentication
(e.g., public documentation servers) but you still want client authentication.
HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing
authorization codes and refresh tokens (opaque tokens).
Current secret must be at least 32 bytes and cryptographically random.
Supports secret rotation via multiple entries (first is current, rest are for verification).
If not specified, an ephemeral secret will be auto-generated (development only -
auth codes and refresh tokens will be invalid after restart).
SecretKeyRef is a reference to a key within a Secret
Key is the key within the secret
Name is the name of the secret
InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
Only set this for in-cluster Kubernetes deployments where traffic between
pods traverses a trusted network (e.g. the in-cluster service mesh).
Production deployments reachable outside the cluster MUST use https://.
On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
structurally present but enforcement is deferred to pod startup via Config.Validate();
a misconfigured issuer will cause the pod to crash at startup rather than surface
as an operator condition.
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
^https?://[^\s?#]+[^/\s?#]$
PrimaryUpstreamProvider names the upstream IDP whose access token Cedar
should read claims from when authorising a request. Must match the name
of one of the entries in UpstreamProviders. When empty, the controller
auto-selects the first entry of UpstreamProviders.
Only meaningful on VirtualMCPServer, where multiple upstream providers
can be configured and Cedar needs to pick which token's claims to
evaluate. The VirtualMCPServer controller validates this field against
UpstreamProviders at admission and rejects unresolvable values.
On MCPServer and MCPRemoteProxy this field is structurally present (the
EmbeddedAuthServerConfig struct is shared) but has no runtime effect:
those CRDs are restricted to a single upstream so there is no choice to
make. Setting it on those CRDs is silently ignored.
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
Must be at least 1 characters long
Must be at most 63 characters long
SigningKeySecretRefs references Kubernetes Secrets containing signing keys for JWT operations.
Supports key rotation by allowing multiple keys (oldest keys are used for verification only).
If not specified, an ephemeral signing key will be auto-generated (development only -
JWTs will be invalid after restart).
Must contain a maximum of 5 items
SecretKeyRef is a reference to a key within a Secret
Key is the key within the secret
Name is the name of the secret
Storage configures the storage backend for the embedded auth server.
If not specified, defaults to in-memory storage.
Redis configures the Redis storage backend.
Required when type is "redis".
ACLUserConfig configures Redis ACL user authentication.
PasswordSecretRef references a Secret containing the Redis ACL password.
Key is the key within the secret
Name is the name of the secret
UsernameSecretRef references a Secret containing the Redis ACL username.
When omitted, connections use legacy password-only AUTH. Omit for managed
Redis tiers that do not support ACL users (e.g. GCP Memorystore Basic/Standard
HA, Azure Cache for Redis). Set for services that support ACL users (e.g. AWS
ElastiCache non-cluster with Redis 6+ RBAC).
Key is the key within the secret
Name is the name of the secret
Addr is the Redis server address (host:port). Required for standalone and cluster modes.
Use for managed Redis services that expose a single endpoint (GCP Memorystore basic tier,
AWS ElastiCache without cluster mode, or cluster-mode services when clusterMode is true).
Mutually exclusive with sentinelConfig.
ClusterMode enables the Redis Cluster protocol. Set to true when addr points to a
Redis Cluster discovery endpoint (e.g., GCP Memorystore Cluster, AWS ElastiCache
cluster mode enabled). Requires addr to be set.
DialTimeout is the timeout for establishing connections.
Format: Go duration string (e.g., "5s", "1m").
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
ReadTimeout is the timeout for socket reads.
Format: Go duration string (e.g., "3s", "1m").
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
SentinelConfig holds Redis Sentinel configuration.
Use for self-managed Redis with Sentinel-based HA. Mutually exclusive with addr.
DB is the Redis database number.
MasterName is the name of the Redis master monitored by Sentinel.
SentinelAddrs is a list of Sentinel host:port addresses.
Mutually exclusive with SentinelService.
SentinelService enables automatic discovery from a Kubernetes Service.
Mutually exclusive with SentinelAddrs.
Name of the Sentinel Service.
Namespace of the Sentinel Service (defaults to same namespace).
Port of the Sentinel service.
SentinelTLS configures TLS for connections to Sentinel instances.
Only applies when sentinelConfig is set. Presence of this field enables TLS.
CACertSecretRef references a Secret containing a PEM-encoded CA certificate
for verifying the server. When not specified, system root CAs are used.
Key is the key within the secret
Name is the name of the secret
InsecureSkipVerify skips TLS certificate verification.
Use when connecting to services with self-signed certificates.
TLS configures TLS for connections to the Redis/Valkey master or cluster nodes.
Presence of this field enables TLS. Omit to use plaintext.
CACertSecretRef references a Secret containing a PEM-encoded CA certificate
for verifying the server. When not specified, system root CAs are used.
Key is the key within the secret
Name is the name of the secret
InsecureSkipVerify skips TLS certificate verification.
Use when connecting to services with self-signed certificates.
WriteTimeout is the timeout for socket writes.
Format: Go duration string (e.g., "3s", "1m").
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
Type specifies the storage backend type.
Valid values: "memory" (default), "redis".
TokenLifespans configures the duration that various tokens are valid.
If not specified, defaults are applied (access: 1h, refresh: 7d, authCode: 10m).
AccessTokenLifespan is the duration that access tokens are valid.
Format: Go duration string (e.g., "1h", "30m", "24h").
If empty, defaults to 1 hour.
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
AuthCodeLifespan is the duration that authorization codes are valid.
Format: Go duration string (e.g., "10m", "5m").
If empty, defaults to 10 minutes.
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
RefreshTokenLifespan is the duration that refresh tokens are valid.
Format: Go duration string (e.g., "168h", "7d" as "168h").
If empty, defaults to 7 days (168h).
^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
UpstreamProviders configures connections to upstream Identity Providers.
The embedded auth server delegates authentication to these providers.
MCPServer and MCPRemoteProxy support a single upstream; VirtualMCPServer supports multiple.
Must contain a minimum of 1 items
UpstreamProviderConfig defines configuration for an upstream Identity Provider.
Exactly one of OIDCConfig or OAuth2Config must be set and must match the
declared Type: oidc-typed providers set OIDCConfig, oauth2-typed providers
set OAuth2Config. The CEL rule below enforces the pairing at admission; the
matching Go-level check in validateUpstreamProvider provides defense-in-depth
for stored objects.
The rule is structured as a chain of equality checks ending in an explicit
false, so adding a new UpstreamProviderType value without extending this
rule fails admission instead of silently demanding the OAuth2 shape. When
adding a new type, extend both this rule and validateUpstreamProvider.
Name uniquely identifies this upstream provider.
Used for routing decisions and session binding in multi-upstream scenarios.
Must be lowercase alphanumeric with hyphens (DNS-label-like).
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
Must be at least 1 characters long
Must be at most 63 characters long
OAuth2Config contains OAuth 2.0-specific configuration.
Required when Type is "oauth2", must be nil when Type is "oidc".
AdditionalAuthorizationParams are extra query parameters to include in
authorization requests sent to the upstream provider.
This is useful for providers that require custom parameters, such as
Google's accesstype=offline for obtaining refresh tokens.
Framework-managed parameters (responsetype, clientid, redirecturi,
scope, state, codechallenge, codechallenge_method, nonce) are not allowed.
Each additional property must conform to the following schema
Type: stringClientID is the OAuth 2.0 client identifier registered with the upstream IDP.
Mutually exclusive with DCRConfig: when DCRConfig is set, ClientID is obtained
at runtime via RFC 7591 Dynamic Client Registration and must be left empty.
ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret.
Key is the key within the secret
Name is the name of the secret
DCRConfig enables RFC 7591 Dynamic Client Registration against the upstream
authorization server. When set, the client credentials are obtained at
runtime rather than being pre-provisioned, and ClientID must be left empty.
Mutually exclusive with ClientID.
DiscoveryURL is the RFC 8414 / OIDC Discovery document URL. The resolver
issues a single GET against this URL (no well-known-path fallback) and
reads registrationendpoint, authorizationendpoint, tokenendpoint,
tokenendpointauthmethodssupported, and scopessupported from the
response.
Mutually exclusive with RegistrationEndpoint.
HTTPS is required because the registration endpoint resolved from this
document carries the initial access token and the issued client_secret
(RFC 7591 §3, RFC 8414 §3). MaxLength is a defensive size cap (etcd
object budget, regex evaluation cost) and matches the conventional URL
length cap.
^https://[^\s?#]+[^/\s?#]$
Must be at most 2048 characters long
InitialAccessTokenRef is an optional reference to a Kubernetes Secret
carrying an RFC 7591 §3 initial access token. When set, the resolver
presents the token value as a Bearer credential on the registration
request. Mirrors the ClientSecretRef pattern.
Key is the key within the secret
Name is the name of the secret
RegistrationEndpoint is the RFC 7591 registration endpoint URL used
directly, bypassing discovery. When using this field, the caller is
expected to also supply AuthorizationEndpoint, TokenEndpoint, and an
explicit Scopes list on the parent OAuth2UpstreamConfig.
Mutually exclusive with DiscoveryURL.
HTTPS is required because the registration endpoint carries the initial
access token and the issued client_secret (RFC 7591 §3, RFC 8414 §3).
MaxLength is a defensive size cap (etcd object budget, regex evaluation
cost) and matches the conventional URL length cap.
^https://[^\s?#]+[^/\s?#]$
Must be at most 2048 characters long
SoftwareID is the RFC 7591 "software_id" registration metadata value,
identifying the client software independent of any particular
registration instance. Typically a UUID or short identifier.
Must be at most 255 characters long
SoftwareStatement is the RFC 7591 "software_statement" JWT asserting
metadata about the client software, signed by a party the authorization
server trusts.
Stored inline on the CR. The JWT is signed but not encrypted, so its
contents are visible to anyone with get/list/watch on this resource and
appear in etcd backups in plaintext. Treat the value as non-confidential
(signed attestation, not a secret). Operators that rotate software
statements like bearer credentials should keep them at the authorization
server side and rely on the registration endpoint's initial access
token (see InitialAccessTokenRef) instead of placing them on the CR.
Bounded to 16384 characters as a defensive size cap (etcd object
budget, regex evaluation cost). Real-world signed statements with
embedded x5c certificate chains, JWKS keys, or OIDC-Federation
trust-framework metadata routinely exceed 4 KB.
Must be at most 16384 characters long
IdentityFromToken extracts user identity (subject, name, email) directly
from the OAuth2 token-endpoint response body using gjson dot-notation paths.
When set, the embedded auth server skips the userinfo HTTP call entirely
and resolves identity from the token response. See IdentityFromTokenConfig
for trust-model and uniqueness considerations.
EmailPath is the dot-notation path to the email address field in the token response.
If not specified or if the path does not resolve to a string, the email is omitted.
Omit the field entirely rather than setting it to an empty string.
Must be at least 1 characters long
Must be at most 256 characters long
NamePath is the dot-notation path to the display name field in the token response.
If not specified or if the path does not resolve to a string, the display name is omitted.
Omit the field entirely rather than setting it to an empty string.
Must be at least 1 characters long
Must be at most 256 characters long
SubjectPath is the dot-notation path to the subject (user ID) field in the token response.
Warning: claims read from the token response are trusted only via TLS, not
cryptographically verified; prefer OIDC ID tokens when verifiable claims are required.
Example: "autheduser.id" for Slack (top-level token-response field). For providers
whose token response embeds the access token as a JWT (e.g. Snowflake), use the
"@upstreamjwt" modifier to decode the payload, e.g. "accesstoken|@upstreamjwt|sub".
The "@upstreamjwt" modifier performs no signature verification either.
Must be at least 1 characters long
Must be at most 256 characters long
RedirectURI is the callback URL where the upstream IdP will redirect after authentication.
When not specified, defaults to {resourceUrl}/oauth/callback where resourceUrl is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config.
Scopes are the OAuth scopes to request from the upstream IdP.
No Additional ItemsTokenEndpoint is the URL for the OAuth token endpoint.
Must match regular expression:^https?://.*$
TokenResponseMapping configures custom field extraction from non-standard token responses.
Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
instead of returning them at the top level. When set, ToolHive performs the token
exchange HTTP call directly and extracts fields using the configured dot-notation paths.
If nil, standard OAuth 2.0 token response parsing is used.
For extracting user identity from the token response, see IdentityFromToken.
AccessTokenPath is the dot-notation path to the access token in the response.
Example: "autheduser.accesstoken"
Must be at least 1 characters long
ExpiresInPath is the dot-notation path to the expiresin value (in seconds).
If not specified, defaults to "expiresin".
RefreshTokenPath is the dot-notation path to the refresh token in the response.
If not specified, defaults to "refresh_token".
ScopePath is the dot-notation path to the scope string in the response.
If not specified, defaults to "scope".
UserInfo contains configuration for fetching user information from the upstream provider.
When omitted and IdentityFromToken is also unset, the embedded auth server runs in
synthesis mode for this upstream: a non-PII subject derived from the access token, no
Name/Email. Use this shape for upstreams with no userinfo surface and no identity in
the token response (e.g., MCP authorization servers per the MCP spec). When
IdentityFromToken is set instead, identity is resolved from the token response body
(e.g., Snowflake's "username" field, Slack's "authed_user.id"); the userinfo HTTP call
is skipped entirely.
AdditionalHeaders contains extra headers to include in the userinfo request.
Useful for providers that require specific headers (e.g., GitHub's Accept header).
Each additional property must conform to the following schema
Type: stringEndpointURL is the URL of the userinfo endpoint.
Must match regular expression:^https?://.*$
FieldMapping contains custom field mapping configuration for non-standard providers.
If nil, standard OIDC field names are used ("sub", "name", "email").
EmailFields is an ordered list of field names to try for the email address.
The first non-empty value found will be used.
Default: ["email"]
NameFields is an ordered list of field names to try for the display name.
The first non-empty value found will be used.
Default: ["name"]
SubjectFields is an ordered list of field names to try for the user ID.
The first non-empty value found will be used.
Default: ["sub"]
HTTPMethod is the HTTP method to use for the userinfo request.
If not specified, defaults to GET.
OIDCConfig contains OIDC-specific configuration.
Required when Type is "oidc", must be nil when Type is "oauth2".
AdditionalAuthorizationParams are extra query parameters to include in
authorization requests sent to the upstream provider.
This is useful for providers that require custom parameters, such as
Google's accesstype=offline for obtaining refresh tokens.
Note: when using accesstype=offline, also set explicit scopes to avoid
the default offlineaccess scope being sent alongside it.
Framework-managed parameters (responsetype, clientid, redirecturi,
scope, state, codechallenge, codechallenge_method, nonce) are not allowed.
Each additional property must conform to the following schema
Type: stringClientID is the OAuth 2.0 client identifier registered with the upstream IdP.
ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret.
Key is the key within the secret
Name is the name of the secret
IssuerURL is the OIDC issuer URL for automatic endpoint discovery.
Must be a valid HTTPS URL.
^https://.*$
RedirectURI is the callback URL where the upstream IdP will redirect after authentication.
When not specified, defaults to {resourceUrl}/oauth/callback where resourceUrl is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config.
Scopes are the OAuth scopes to request from the upstream IdP.
If not specified, defaults to ["openid", "offlineaccess"].
When using additionalAuthorizationParams with provider-specific refresh token
mechanisms (e.g., Google's accesstype=offline), set explicit scopes to avoid
sending both offline_access and the provider-specific parameter.
SubjectClaim names the validated ID-token claim to use as the upstream
subject. Defaults to "sub" when empty. Set it for IdPs where "sub" isn't
stable per user — e.g. Entra/Azure AD, whose "sub" rotates per application
and whose stable identifier is "oid".
The value is looked up verbatim as a top-level claim name, so it is
constrained to a claim-name shape: it must start with a letter or
underscore and contain only letters, digits, and underscores. This rejects
dotted, colon-namespaced, or whitespace-containing values at admission
rather than letting a typo silently miss the claim at login, and keeps the
field aligned with the directory service's per-issuer bindingClaim.
Changing this on a live deployment re-keys existing users (the value
resolves to the internal user ID), so treat it as immutable once users
exist.
Per-IdP notes:
- Entra/Azure AD: use "oid"; it is only emitted when the upstream scopes
include "profile". "oid" is unique within a single tenant — multi-tenant
apps need oid+tid, which this single-claim field cannot express.
- Okta: the org auth server already puts the stable id in "sub" (default
works). A custom auth server's "sub" is the mutable login/email and the
stable "uid" lives only in the access token, not the ID token — map a
custom ID-token claim and point subjectClaim at it.
The pattern matches the claim-name shape and allows empty (defaults to
"sub"). Using Pattern rather than a CEL XValidation rule keeps this off the
CRD's CEL cost budget — a single-field format check via CEL is rejected by
the apiserver as too expensive once multiplied across the upstreams list.
^([a-zA-Z_][a-zA-Z0-9_]*)?$
Must be at most 128 characters long
UserInfoOverride allows customizing UserInfo fetching behavior for OIDC providers.
By default, the UserInfo endpoint is discovered automatically via OIDC discovery.
Use this to override the endpoint URL, HTTP method, or field mappings for providers
that return non-standard claim names in their UserInfo response.
AdditionalHeaders contains extra headers to include in the userinfo request.
Useful for providers that require specific headers (e.g., GitHub's Accept header).
Each additional property must conform to the following schema
Type: stringEndpointURL is the URL of the userinfo endpoint.
Must match regular expression:^https?://.*$
FieldMapping contains custom field mapping configuration for non-standard providers.
If nil, standard OIDC field names are used ("sub", "name", "email").
EmailFields is an ordered list of field names to try for the email address.
The first non-empty value found will be used.
Default: ["email"]
NameFields is an ordered list of field names to try for the display name.
The first non-empty value found will be used.
Default: ["name"]
SubjectFields is an ordered list of field names to try for the user ID.
The first non-empty value found will be used.
Default: ["sub"]
HTTPMethod is the HTTP method to use for the userinfo request.
If not specified, defaults to GET.
Type specifies the provider type: "oidc" or "oauth2"
Config is the Virtual MCP server configuration.
The audit config from here is also supported, but not required.
EmbeddingServerRef references an existing EmbeddingServer resource by name.
When the optimizer is enabled, this field is required to point to a ready EmbeddingServer
that provides embedding capabilities.
The referenced EmbeddingServer must exist in the same namespace and be ready.
Name is the name of the EmbeddingServer resource
GroupRef references the MCPGroup that defines backend workloads.
The referenced MCPGroup must exist in the same namespace.
Name is the name of the MCPGroup resource in the same namespace
Must be at least 1 characters long
ImagePullSecrets allows specifying image pull secrets for the vMCP workload.
These are applied to both the vMCP Deployment's PodSpec.ImagePullSecrets
and to the operator-managed ServiceAccount the vMCP server runs as, so private
images are pullable through either path.
Merge semantics with PodTemplateSpec:
The deployed PodSpec.ImagePullSecrets is the Kubernetes-native strategic-merge
union of this field and spec.podTemplateSpec.spec.imagePullSecrets, merged by
the patchStrategy:"merge" / patchMergeKey:"name" tags on corev1.PodSpec.
- This field is rendered first as the controller-generated default.
- spec.podTemplateSpec.spec.imagePullSecrets is then strategic-merge-patched
on top, keyed by Name. Distinct names from the two sources are unioned in
the resulting list; entries with the same Name are deduplicated and the
PodTemplateSpec entry wins on overlap (user override).
- Order in the resulting list is not guaranteed and should not be relied on:
strategic merge by name is order-insensitive.
- The operator-managed ServiceAccount's imagePullSecrets list is populated
ONLY from this field. spec.podTemplateSpec.spec.imagePullSecrets does not
reach the ServiceAccount because PodTemplateSpec has no notion of a
ServiceAccount. To make a secret usable via the ServiceAccount path
(e.g. for sidecars or init containers that pull images independently),
list it here rather than under spec.podTemplateSpec.
Note on cross-CRD consistency:
MCPRegistry currently uses an atomic-replace strategy for its imagePullSecrets
(the user-provided value replaces the controller-generated list rather than
being merged on top). VirtualMCPServer follows the Kubernetes-native
strategic-merge-by-name behavior described above. Aligning the two is tracked
as a separate follow-up; until then, manifests that set imagePullSecrets on
both CRDs will see different override behavior between them.
LocalObjectReference contains enough information to let you locate the
referenced object inside the same namespace.
IncomingAuth configures authentication for clients connecting to the Virtual MCP server.
Must be explicitly set - use "anonymous" type when no authentication is required.
This field takes precedence over config.IncomingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config.
AuthzConfig defines authorization policy configuration.
Reuses MCPServer authz patterns.
AuthzConfig and AuthzConfigRef are mutually exclusive.
ConfigMap references a ConfigMap containing authorization configuration
Only used when Type is "configMap"
Key is the key in the ConfigMap that contains the authorization configuration
Name is the name of the ConfigMap
GroupClaimName is the JWT claim key that contains group membership for the
principal. When set, takes priority over the well-known defaults
("groups", "roles", "cognito:groups"). Use this for IDPs that place
groups under a URI-style claim (e.g. "https://example.com/groups"). When
Type is "configMap", a groupclaimname entry in the referenced ConfigMap
is overridden by this field if both are set.
Must be at most 253 characters long
GroupEntityType is the Cedar entity type name used for principal parent
UIDs synthesised from JWT group/role claims. Defaults to "THVGroup" when
empty. Must match the entity type used in the static entity store for
transitive in checks (e.g. ClaimGroup → PlatformRole) to resolve.
Namespaced names (Foo::Bar) are not yet supported. When Type is
"configMap", a groupentitytype entry in the referenced ConfigMap is
overridden by this field if both are set.
^[A-Za-z_][A-Za-z0-9_]*$
Must be at most 63 characters long
Inline contains direct authorization configuration
Only used when Type is "inline"
EntitiesJSON is a JSON string representing Cedar entities. Required when
transitive policies (e.g. ClaimGroup → PlatformRole) need a static
entity store; defaults to "[]".
Policies is a list of Cedar policy strings
Must contain a minimum of 1 items
PrimaryUpstreamProvider names the upstream IDP whose access token's
claims Cedar should evaluate.
Deprecated: on VirtualMCPServer this field has moved to
spec.authServerConfig.primaryUpstreamProvider. The old location is
still read for one release for backward compatibility; the
VirtualMCPServer controller emits an AuthzPrimaryUpstreamProviderDeprecated
Warning event whenever it is consumed, and removal is planned for the
release after the deprecation cycle.
On MCPServer and MCPRemoteProxy this field has always been a structural
no-op (those CRDs do not run an embedded auth server). Setting it
continues to surface the AuthzPrimaryUpstreamProviderIgnored advisory
condition; the deprecation does not change that behaviour.
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
Must be at least 1 characters long
Must be at most 63 characters long
RoleClaimName is the JWT claim key that contains role membership for the
principal. When set, the claim is extracted separately from GroupClaimName
and both are mapped to the configured GroupEntityType. When Type is
"configMap", a roleclaimname entry in the referenced ConfigMap is
overridden by this field if both are set.
Must be at most 253 characters long
Type is the type of authorization configuration
AuthzConfigRef references a shared MCPAuthzConfig resource for authorization.
The referenced MCPAuthzConfig must exist in the same namespace as this VirtualMCPServer.
Mutually exclusive with authzConfig.
Only cedarv1 MCPAuthzConfig resources are supported for VirtualMCPServer
today; referencing a non-Cedar config fails reconciliation with a clear
error because the vMCP runtime authz middleware is Cedar-only.
Name is the name of the MCPAuthzConfig resource in the same namespace.
Must be at least 1 characters long
OIDCConfigRef references a shared MCPOIDCConfig resource for OIDC authentication.
The referenced MCPOIDCConfig must exist in the same namespace as this VirtualMCPServer.
Per-server overrides (audience, scopes) are specified here; shared provider config
lives in the MCPOIDCConfig resource.
Audience is the expected audience for token validation.
This MUST be unique per server to prevent token replay attacks.
Must be at least 1 characters long
Name is the name of the MCPOIDCConfig resource
Must be at least 1 characters long
ResourceURL is the public URL for OAuth protected resource metadata (RFC 9728).
When the server is exposed via Ingress or gateway, set this to the external
URL that MCP clients connect to. If not specified, defaults to the internal
Kubernetes service URL.
Scopes is the list of OAuth scopes to advertise in the well-known endpoint (RFC 9728).
If empty, defaults to ["openid"].
Type defines the authentication type: anonymous or oidc
When no authentication is required, explicitly set this to "anonymous"
OutgoingAuth configures authentication from Virtual MCP to backend MCPServers.
This field takes precedence over config.OutgoingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config.
Backends defines per-backend authentication overrides
Works in all modes (discovered, inline)
Each additional property must conform to the following schema
Type: objectBackendAuthConfig defines authentication configuration for a backend MCPServer
ExternalAuthConfigRef references an MCPExternalAuthConfig resource
Only used when Type is "externalAuthConfigRef"
Name is the name of the MCPExternalAuthConfig resource
Type defines the authentication type
Default defines default behavior for backends without explicit auth config
ExternalAuthConfigRef references an MCPExternalAuthConfig resource
Only used when Type is "externalAuthConfigRef"
Name is the name of the MCPExternalAuthConfig resource
Type defines the authentication type
Source defines how backend authentication configurations are determined
- discovered: Automatically discover from backend's MCPServer.spec.externalAuthConfigRef
- inline: Explicit per-backend configuration in VirtualMCPServer
PassthroughHeaders is an allowlist of incoming client request header names
forwarded verbatim to all backends (e.g. an API key the backend resolves to
a user). Takes precedence over config.PassthroughHeaders. Names must not be
restricted headers (Host, hop-by-hop, X-Forwarded-*). Forwarded headers are
attacker-influenceable unless a trusted upstream sets them.
PodTemplateSpec defines the pod template to use for the Virtual MCP server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the Virtual MCP server runs in, you must specify
the 'vmcp' container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML.
Replicas is the desired number of vMCP pod replicas.
VirtualMCPServer creates a single Deployment for the vMCP aggregator process,
so there is only one replicas field (unlike MCPServer which has separate
Replicas and BackendReplicas for its two Deployments).
When nil, the operator does not set Deployment.Spec.Replicas, leaving replica
management to an HPA or other external controller.
Value must be greater or equal to 0
ServiceAccount is the name of an already existing service account to use by the Virtual MCP server.
If not specified, a ServiceAccount will be created automatically and used by the Virtual MCP server.
ServiceType specifies the Kubernetes service type for the Virtual MCP server
SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity.
SessionStorage configures session storage for stateful horizontal scaling.
When nil, no session storage is configured.
Address is the Redis server address (required when provider is redis)
Must be at least 1 characters long
DB is the Redis database number
Value must be greater or equal to 0
KeyPrefix is an optional prefix for all Redis keys used by ToolHive
PasswordRef is a reference to a Secret key containing the Redis password
Key is the key within the secret
Name is the name of the secret
Provider is the session storage backend type
TelemetryConfigRef references an MCPTelemetryConfig resource for shared telemetry configuration.
The referenced MCPTelemetryConfig must exist in the same namespace as this VirtualMCPServer.
Cross-namespace references are not supported for security and isolation reasons.
Name is the name of the MCPTelemetryConfig resource
Must be at least 1 characters long
ServiceName overrides the telemetry service name for this specific server.
This MUST be unique per server for proper observability (e.g., distinguishing
traces and metrics from different servers sharing the same collector).
If empty, defaults to the server name with "thv-" prefix at runtime.
VirtualMCPServerStatus defines the observed state of VirtualMCPServer
AuthzConfigHash is the hash of the referenced MCPAuthzConfig spec for change detection.
Only populated when IncomingAuth.AuthzConfigRef is set.
BackendCount is the number of routable backends (ready + unauthenticated).
Excludes unavailable, degraded, and unknown backends.
Conditions represent the latest available observations of the VirtualMCPServer's state
No Additional ItemsCondition contains details for one aspect of the current state of this API Resource.
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
message is a human readable message indicating details about the transition.
This may be an empty string.
Must be at most 32768 characters long
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
Value must be greater or equal to 0
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
Must be at least 1 characters long
Must be at most 1024 characters long
status of the condition, one of True, False, Unknown.
type of condition in CamelCase or in foo.example.com/CamelCase.
Must match regular expression:^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
Must be at most 316 characters long
DiscoveredBackends lists discovered backend configurations from the MCPGroup
No Additional ItemsDiscoveredBackend represents a backend server discovered by vMCP runtime.
This type is shared with the Kubernetes operator CRD (VirtualMCPServer.Status.DiscoveredBackends).
AuthConfigRef is the name of the discovered MCPExternalAuthConfig (if any)
AuthType is the type of authentication configured
CircuitBreakerState is the current circuit breaker state (closed, open, half-open).
Empty when circuit breaker is disabled or not configured.
CircuitLastChanged is the timestamp when the circuit breaker state last changed.
Empty when circuit breaker is disabled or has never changed state.
ConsecutiveFailures is the current count of consecutive health check failures.
Resets to 0 when the backend becomes healthy again.
LastHealthCheck is the timestamp of the last health check
MCPRevision is the backend's negotiated MCP protocol revision
("2026-07-28" or "2025-11-25"). Empty when the backend has not been probed.
Message provides additional information about the backend status
Name is the name of the backend MCPServer
Status is the current status of the backend (ready, degraded, unavailable, unauthenticated, unknown).
Use BackendHealthStatus.ToCRDStatus() to populate this field.
URL is the URL of the backend MCPServer
Message provides additional information about the current phase
ObservedGeneration is the most recent generation observed for this VirtualMCPServer
OIDCConfigHash is the hash of the referenced MCPOIDCConfig spec for change detection.
Only populated when IncomingAuth.OIDCConfigRef is set.
Phase is the current phase of the VirtualMCPServer
TelemetryConfigHash is the hash of the referenced MCPTelemetryConfig spec for change detection.
Only populated when TelemetryConfigRef is set.
URL is the URL where the Virtual MCP server can be accessed
Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.
remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is estimating the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.
String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.