Last generated September 30, 2026

MCPServer

MCPServer is the Schema for the mcpservers API

Full name:
mcpservers.muster.giantswarm.io
Group:
muster.giantswarm.io
Singular name:
mcpserver
Plural name:
mcpservers
Scope:
Namespaced
Versions:
v1alpha1

Version v1alpha1

Example CR

apiVersion: muster.giantswarm.io/v1alpha1
kind: MCPServer
metadata:
  name: git-tools
  namespace: default
spec:
  type: stdio
  autoStart: true
  command: npx
  args: ["@modelcontextprotocol/server-git"]
  env:
    GIT_ROOT: "/workspace"
    LOG_LEVEL: "info"
  description: "Git tools MCP server for repository operations"

Properties

.apiVersion

string

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

string

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

.metadata

object

.spec

object

MCPServerSpec defines the desired state of MCPServer

.spec.args

array

Args specifies the command line arguments for stdio type servers. This field is only available when Type is “stdio”.

.spec.args[*]

string

.spec.auth

object

Auth configures authentication behavior for this MCP server. This is only relevant for remote servers (streamable-http or sse).

.spec.auth.authorizationServer

object

AuthorizationServer is an opt-out for backends that don’t publish RFC 9728 Protected Resource Metadata. When set, muster’s per-server OAuth login flow (core_auth_login) skips PRM probing and uses these values directly. muster logs each override use at INFO so non-compliance is observable.

This override does NOT bypass mcp-go’s connect-time PRM probe; backends without RFC 9728 metadata still reconcile to “Auth Required” on first connect, then transition to “Connected” after muster auth login.

Setting AuthorizationServer does NOT change the RFC 8707 resource parameter — that remains driven by the MCP server URL.

AuthorizationServer is mutually exclusive with ForwardToken: true and TokenExchange.Enabled: true. The CRD admission rules above reject any CR that combines them. Only valid when Type is “oauth”. A server that needs the session’s ID token next to the pinned grant sets ForwardIdentity instead.

Use case: Atlassian Remote MCP and similar backends that publish RFC 8414 metadata at their resource origin instead of via RFC 9728.

.spec.auth.authorizationServer.authorizationEndpoint

string

AuthorizationEndpoint is the authorization server’s authorization endpoint. Set it together with TokenEndpoint for an authorization server that publishes no RFC 8414 / OIDC discovery document (GitHub: https://github.com/login/oauth/authorize). muster then performs no discovery for this issuer and assumes S256 PKCE support, which the operator asserts by pinning the endpoints.

.spec.auth.authorizationServer.clientCredentialsSecretRef

object

ClientCredentialsSecretRef references a Kubernetes Secret holding a client registered with this authorization server out of band (a GitHub App or OAuth App). When set, muster identifies itself with these credentials instead of its Client ID Metadata Document or a dynamic registration – the only option against an authorization server that supports neither. The secret carries client-id and client-secret (key names configurable in the reference).

.spec.auth.authorizationServer.clientCredentialsSecretRef.clientIdKey

string

ClientIDKey is the key in the secret data that contains the client ID. Defaults to “client-id” if not specified.

.spec.auth.authorizationServer.clientCredentialsSecretRef.clientSecretKey

string

ClientSecretKey is the key in the secret data that contains the client secret. Defaults to “client-secret” if not specified.

.spec.auth.authorizationServer.clientCredentialsSecretRef.name

string Required

Name is the name of the Kubernetes Secret. Required.

.spec.auth.authorizationServer.clientCredentialsSecretRef.namespace

string

Namespace is the Kubernetes namespace where the secret is located. If not specified, defaults to the MCPServer’s namespace.

.spec.auth.authorizationServer.expectedIssuer

string

ExpectedIssuer is the issuer identifier the authorization server itself publishes and puts in the RFC 9207 iss parameter of its authorization responses, when that differs from Issuer. Issuer stays the identity the grants are filed under; ExpectedIssuer only decides which value a present iss on the callback has to equal. Set it when several MCPServers pin one authorization server under different identities so their grants stay apart – two GitHub Apps, say: each is pinned under its own identity (https://github.com/apps/) with ExpectedIssuer https://github.com/login/oauth, the issuer GitHub sends. Needs AuthorizationEndpoint and TokenEndpoint. Default: Issuer.

.spec.auth.authorizationServer.grantScope

string

GrantScope decides whom a token obtained from this authorization server belongs to. session (the default) keeps today’s behavior: the token is bound to the login session that completed the browser flow, so every new session signs in again. subject files the token under the user’s identity as well, so any later session of the same person – another client, a re-login, a front-end whose bearer rotates – reuses the grant without a new consent, until the person signs out of this server (core_auth_logout) or everywhere. Use subject for external accounts the person owns (GitHub), never for tokens that carry session-specific authority.

.spec.auth.authorizationServer.issuer

string Required

Issuer is the OAuth 2.0 / OIDC issuer URL of the authorization server the sign-in runs against, and the identity the session’s grants are filed under. It takes precedence over the authorization server the endpoint’s own RFC 9728 metadata names: the browser flow, the token store, the connection and core_auth_logout all use this issuer, so an endpoint that accepts tokens from an authorization server other than the one it advertises is pinned by naming the accepting one here. It may be muster’s own identity provider; muster keeps the session’s login token apart from the grant. A change to this field takes effect on the next core_auth_login, without a restart. muster fetches AS metadata via the existing OAuth client, which performs RFC 8414 / OIDC discovery against this issuer – unless AuthorizationEndpoint and TokenEndpoint are set, in which case no discovery happens and the issuer is only that identity. When the authorization server publishes a different issuer identifier than the identity pinned here, set ExpectedIssuer.

.spec.auth.authorizationServer.scopes

string

Scopes is the OAuth scope parameter value (RFC 6749 §3.3 wire format: space-separated scope tokens). Matches existing TokenExchangeConfig.Scopes.

.spec.auth.authorizationServer.tokenEndpoint

string

TokenEndpoint is the authorization server’s token endpoint (GitHub: https://github.com/login/oauth/access_token). See AuthorizationEndpoint.

.spec.auth.forwardIdentity

boolean

ForwardIdentity sends the session’s upstream ID token – the token ForwardToken would put in Authorization – in the X-Muster-Id-Token header on every request to this server, next to the grant of its pinned authorization server, which Authorization keeps carrying unchanged. It is for a server that acts for the person on two systems: one through the pinned grant (a GitHub App user token, say) and one through calls back with the person’s own identity (their Kubernetes access). The token is read on every request, so a refreshed one is sent once the session holds it; a session without an upstream ID token sends no header, and a server without this field never receives it.

Only valid with Type “oauth” and AuthorizationServer set. RequiredAudiences of such a server are requested at login as for a ForwardToken server, so the forwarded ID token carries them.

The forwarded ID token is not audience-scoped to this server, as with ForwardToken: the same token is accepted by every backend that trusts its issuer and audiences, so a ForwardIdentity server must be trusted as much as a ForwardToken backend.

.spec.auth.forwardToken

boolean

ForwardToken enables ID token forwarding for SSO. When true, muster forwards the session’s upstream dex ID token (or, for sessions established by a trusted-issuer bearer, that IdP-issued bearer) to this server byte-identical, instead of triggering a separate OAuth flow. Every forwarded token is issued by the IdP (dex); muster is not an identity provider, never signs tokens, and no backend is ever configured to trust a muster JWKS. The downstream server must trust the IdP’s issuer/JWKS (e.g. muster’s client ID in its TrustedAudiences for forwarded dex ID tokens).

The forwarded token is not audience-scoped to this server: the same token is accepted by every forwardToken backend, so all forwardToken backends must be equally trusted. A token’s nested act claim (minted by the IdP, e.g. via exchange at dex) carries the delegation chain for backend authorization decisions.

.spec.auth.requiredAudiences

array

RequiredAudiences specifies additional audience(s) that the forwarded ID token should contain. When ForwardToken is true, muster will request these audiences from the upstream IdP (e.g., Dex) using cross-client scopes.

This is used when the downstream server requires tokens with specific audiences, for example when forwarding tokens to Kubernetes for OIDC authentication: requiredAudiences: - “dex-k8s-authenticator”

At user authentication, muster collects all requiredAudiences from MCPServers with forwardToken: true or forwardIdentity: true and requests them all from the IdP.

.spec.auth.requiredAudiences[*]

string

.spec.auth.sigv4

object

SigV4 configures AWS Signature Version 4 request signing. Required when Type is “sigv4”, and rejected otherwise.

Unlike the OAuth mechanisms above, this is a machine identity: every request signs as muster itself, not as the calling user, so it never touches the session-scoped client machinery. Only valid together with spec.type “streamable-http”.

ForwardToken and TokenExchange are rejected alongside it by the rule above. AuthorizationServer needs no rule of its own: it already requires Type “oauth”, which the sigv4 pairing rule excludes.

.spec.auth.sigv4.region

string Required

Region is the SigV4 signing region. It must match the region in URL, because the signature’s credential scope is checked against the endpoint. This is not the region the backend operates in — that is MCPServerSpec.Meta.

.spec.auth.sigv4.roleArn

string

RoleARN, when set, is assumed from the pod’s base credentials before signing. Leave empty to sign as the pod’s own identity.

.spec.auth.sigv4.service

string

Service is the SigV4 signing service name. It defaults to the first hostname label of URL, which is how AWS’s own clients derive it: aws-mcp.eu-central-1.api.aws signs as service “aws-mcp”.

.spec.auth.tokenExchange

object

TokenExchange enables SSO via RFC 8693 Token Exchange for cross-cluster SSO. When configured, muster exchanges its local token for a token valid on the remote cluster’s Identity Provider (e.g., Dex).

Use TokenExchange when: - The remote cluster has its own Dex instance - The remote Dex is configured with an OIDC connector for muster’s Dex - You need a token issued by the remote cluster’s IdP (not just forwarded)

Token exchange takes precedence over ForwardToken if both are configured.

.spec.auth.tokenExchange.clientCredentialsSecretRef

object

ClientCredentialsSecretRef references a Kubernetes Secret containing client credentials for authenticating with the remote Dex’s token endpoint. This is required when the remote Dex requires client authentication for token exchange (RFC 8693).

The secret should contain: - client-id: The OAuth client ID registered on the remote Dex - client-secret: The OAuth client secret for authentication

Example secret:

apiVersion: v1
kind: Secret
metadata:
  name: grizzly-token-exchange-credentials
  namespace: muster
type: Opaque
stringData:
  client-id: muster-token-exchange
  client-secret: <secret-value>

.spec.auth.tokenExchange.clientCredentialsSecretRef.clientIdKey

string

ClientIDKey is the key in the secret data that contains the client ID. Defaults to “client-id” if not specified.

.spec.auth.tokenExchange.clientCredentialsSecretRef.clientSecretKey

string

ClientSecretKey is the key in the secret data that contains the client secret. Defaults to “client-secret” if not specified.

.spec.auth.tokenExchange.clientCredentialsSecretRef.name

string Required

Name is the name of the Kubernetes Secret. Required.

.spec.auth.tokenExchange.clientCredentialsSecretRef.namespace

string

Namespace is the Kubernetes namespace where the secret is located. If not specified, defaults to the MCPServer’s namespace.

.spec.auth.tokenExchange.connectorId

string

ConnectorID is the ID of the OIDC connector on the remote Dex that trusts the local cluster’s Dex. Required when Enabled is true. Example: “cluster-a-dex”

.spec.auth.tokenExchange.dexTokenEndpoint

string

DexTokenEndpoint is the URL used to connect to the remote cluster’s Dex token endpoint. This may differ from the issuer URL when access goes through a proxy. Required when Enabled is true. Example: https://dex.cluster-b.example.com/token (direct) Example: https://dex-cluster.proxy.example.com/token (via proxy)

.spec.auth.tokenExchange.enabled

boolean

Enabled determines whether token exchange should be attempted.

.spec.auth.tokenExchange.expectedIssuer

string

ExpectedIssuer is the expected issuer URL in the exchanged token’s “iss” claim. This should match the remote Dex’s configured issuer URL. When access goes through a proxy, this differs from DexTokenEndpoint. If not specified, the issuer is derived from DexTokenEndpoint (backward compatible). Example: https://dex.cluster-b.example.com

.spec.auth.tokenExchange.scopes

string

Scopes are the scopes to request for the exchanged token.

.spec.auth.type

string

Type specifies the authentication type. Supported values: - “oauth”: OAuth 2.0/OIDC authentication - “none”: No authentication - “sigv4”: AWS Signature Version 4, signed with muster’s own machine identity. See SigV4.

.spec.autoStart

boolean

AutoStart determines whether this MCP server should be automatically started when the muster system initializes or when dependencies become available.

.spec.command

string

Command specifies the executable path for stdio type servers. This field is required when Type is “stdio”.

.spec.description

string

Description provides a human-readable description of this MCP server’s purpose.

.spec.env

object

Env contains environment variables to set for the MCP server. For stdio servers, these are passed to the process when it is started. For remote servers, these can be used for authentication or configuration.

.spec.family

object

Family declares that this MCP server is an instance of a family of equivalent servers (for example, multiple kubernetes MCP servers pointed at different clusters). When set, the aggregator exposes tools from all servers in the same family under a single name ({musterPrefix}{family.name}{toolName}) with a required parameter (named by family.instanceArg) that selects which instance handles the call. The parameter is always required even for single-instance families so skills written against the family name remain stable as instances are added or removed. When unset, the legacy per-server prefixing applies ({musterPrefix}{toolPrefix-or-name}{toolName}).

.spec.family.instanceArg

string Required

InstanceArg names the required parameter callers use to select which family member handles the tool call (for example “management_cluster”, “country”, “model”). All servers declaring the same family.name must agree on InstanceArg; if they diverge, the aggregator falls back to per-server prefixing for the entire family and logs a warning.

.spec.family.name

string Required

Name is the family identifier. Servers sharing the same Name expose their tools as {musterPrefix}{Name}{toolName}.

.spec.headers

object

Headers contains HTTP headers to send with requests to remote MCP servers. This field is only relevant when Type is “streamable-http” or “sse”.

.spec.meta

object

Meta contains entries merged into the params._meta object of every outbound JSON-RPC request that carries params. It carries call-scoped configuration to a backend that reads it from the MCP metadata field instead of from tool arguments.

An entry already present in a request’s _meta wins, so a caller can still override per call. Requests without params are left untouched.

The injection happens in an HTTP round tripper shared by every remote client, so it applies to streamable-http and sse alike, with or without auth. For a SigV4 server the round tripper sits in front of the signer, because the body has to be rewritten before it is signed.

A stdio server has no HTTP transport, so the CEL rule above rejects the field there rather than accepting and dropping it.

The AWS-hosted MCP server is the motivating case: it takes the region it operates in from params._meta.AWS_REGION, and its tools declare no region argument, so without this a call fails with NoRegionError. That operating region is a different value from Auth.SigV4.Region, which is the signing region — the two default to the same string but resolve independently, endpoint-first for signing and config-first for operating.

.spec.restartRequestedAt

string

RestartRequestedAt requests a one-shot restart of this server’s service. Restart is an action, not a steady state, so it cannot be a boolean: the caller writes a timestamp, which doubles as the audit record of when the restart was requested. The reconciler restarts the service only when this differs from status.lastRestartedAt, then mirrors the processed value into status — repeated reconciles of an already-processed request are no-ops (issue #1055).

.spec.suspended

boolean

Suspended declares the desired lifecycle state of this server’s service. When true, the reconciler stops the service (and refuses to start it) until the field is set back to false. This is the CR-driven replacement for the imperative start/stop tools: writing it is authorized and audited by the apiserver like any other spec mutation (issue #1055).

.spec.timeout

integer

Timeout specifies the connection timeout for remote operations (in seconds)

.spec.toolPrefix

string

ToolPrefix is an optional prefix that will be prepended to all tool names provided by this MCP server. This helps avoid naming conflicts when multiple servers provide tools with similar names.

.spec.type

string Required

Type specifies how this MCP server should be executed. Supported values: “stdio” for local processes, “streamable-http” for HTTP-based servers, “sse” for Server-Sent Events.

“stdio” is CLI-only. A stdio server is started as a subprocess of the muster process, so a muster running in Kubernetes mode rejects it: the tool handlers fail the call, and a CR applied directly through the API server is reconciled to state Failed instead of spawning a process in the muster pod. Run the MCP server as its own workload and use “streamable-http” or “sse” instead.

.spec.url

string

URL is the endpoint where the remote MCP server can be reached This field is required when Type is “streamable-http” or “sse”. Examples: http://mcp-server:8080/mcp, https://api.example.com/mcp

.status

object

MCPServerStatus defines the observed state of MCPServer.

This status reflects server-side observable state including auth requirements. It captures infrastructure connectivity as well as whether the server demands authentication (e.g. “Auth Required”). Per-user session state (which specific user is authenticated, token expiry, etc.) is tracked separately in the Session Registry (internal/aggregator/session_registry.go).

Server-Side State (CRD): - State: Running/Connected/Starting/Connecting/Stopped/Disconnected/Auth Required/Awaiting Session/Failed - Conditions: Standard K8s conditions for detailed status

Per-User Session State (Session Registry): - ConnectionStatus: Connected, PendingAuth, Failed (per-user) - AuthStatus: Authenticated, AuthRequired, TokenExpired (per-user) - AvailableTools: Tools visible to this specific user

.status.conditions

array

Conditions represent the latest available observations of the MCPServer’s current state. Standard condition types: - Ready: True while the server is reachable (Running, Connected, Auth Required, Awaiting Session), False otherwise. The reason is the state in one word (Connected, AwaitingSession, AuthRequired, Connecting, Disconnected, Suspended, Failed) or, for a server Failed by its token exchange, the class of the failure (TokenExchangeCredentials, TokenExchangeEndpoint, TokenExchangeConnector). The message explains the state: for Awaiting Session the per-session mechanism, the token endpoint and connector, and when the last exchange succeeded; for Failed the error.

.status.conditions[*]

object

Condition contains details for one aspect of the current state of this API Resource.

.status.conditions[*].lastTransitionTime

string Required

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.

.status.conditions[*].message

string Required

message is a human readable message indicating details about the transition. This may be an empty string.

.status.conditions[*].observedGeneration

integer

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.

.status.conditions[*].reason

string Required

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.

.status.conditions[*].status

string Required

status of the condition, one of True, False, Unknown.

.status.conditions[*].type

string Required

type of condition in CamelCase or in foo.example.com/CamelCase.

.status.consecutiveFailures

integer

ConsecutiveFailures tracks the number of consecutive connection failures. This is used for exponential backoff and to identify unreachable servers. Reset to 0 when a connection succeeds.

.status.lastAttempt

string

LastAttempt indicates when the last connection attempt was made. Used with ConsecutiveFailures to implement exponential backoff.

.status.lastConnected

string

LastConnected indicates when the server was last successfully connected

.status.lastError

string

LastError contains any error message from the most recent server operation. Note: Per-user authentication errors are tracked in the Session Registry, not here. This field only contains infrastructure-level errors.

.status.lastFailureHTTPStatus

integer

LastFailureHTTPStatus is the HTTP status code the endpoint answered the most recent failed connection attempt with, for example 504 from a gateway in front of the server or 404 from a backend whose route is not served yet. Absent when the attempt got no HTTP response at all (connection refused, DNS failure, timeout) or the last attempt succeeded. Together with LastError it tells an upstream outage from an endpoint nothing listens on.

.status.lastRestartedAt

string

LastRestartedAt mirrors the spec.restartRequestedAt value most recently processed by the reconciler. A restart is performed only when the two differ (spec-is-desired / status-is-observed idiom, issue #1055).

.status.nextRetryAfter

string

NextRetryAfter indicates the earliest time when the next retry should be attempted. The wait doubles with every consecutive failure, starting at 30 seconds, and is capped (2 minutes by default) so a recovered upstream is noticed within the cap plus the retry tick, however long the outage lasted. Absent while no retry is scheduled.

.status.restartCount

integer

RestartCount tracks how many times this server has been restarted (stdio only)

.status.state

string

State represents the high-level infrastructure state of the MCP server. This is independent of user session state (authentication, connection status).

For stdio servers: Running, Starting, Stopped, Failed For remote servers: Connected, Auth Required, Awaiting Session, Connecting, Disconnected, Failed


This documentation page shows information based on muster v5.32.10.