AI Agents

  • Changed

    • Outbound OAuth flows now send the RFC 8707 resource parameter on both the authorization request and the token request, for backends and for the muster agent login. The value is the resource field of the target’s RFC 9728 metadata, sent exactly as declared; when that metadata omits the field, or spec.auth.authorizationServer opts out of discovery, it is derived from the configured URL by dropping the query and the fragment and changing nothing else, which is the value mcp-go derives for the refresh request. A token muster obtains for one backend is no longer accepted at another backend that trusts the same issuer, provided the authorization server honors the parameter. An authorization server that rejects an unknown resource will fail the login.
    • Authorization server metadata is now rejected when its issuer does not identify the server the document was fetched from (RFC 8414 §3.3, trailing slash ignored), and when it carries no issuer at all. Deployments whose authorization server reports an issuer other than the URL muster fetches it from must correct the configured issuer.
    • RFC 9728 protected resource metadata advertised by a backend is now bound to that backend. A resource_metadata= pointer in a WWW-Authenticate header is followed only when it is on the backend’s own scheme and host, and a resource the document declares is used only when it is on that same origin (the path may differ, so a backend serving at <base>/mcp may declare <base>). A pointer to another origin is ignored and discovery falls through to the well-known path; a foreign declared resource is dropped and the indicator is derived from the backend URL. Without these checks a backend could name a document that binds muster’s token to a resource the backend does not own.
    • Breaking (Go API): pkg/oauth.Client.BuildAuthorizationURL takes an AuthorizationRequest struct instead of six positional arguments, and pkg/oauth.Client.ExchangeCode takes a trailing resource argument. Callers pass the same values through the new shapes; an empty Resource omits the parameter.
    • OAuth callbacks are now validated per RFC 9207 before anything else on the response is acted on. A present iss must equal the issuer the authorization server publishes in its own metadata (simple string comparison, no normalization), and an absent iss is refused when that server advertises authorization_response_iss_parameter_supported. When the metadata is unreachable the comparison falls back to the issuer recorded with the flow and then ignores a trailing slash on either side, because that value is operator-configured rather than published by the server. A response that fails the check is rejected whole: its error and error_description are neither acted on nor displayed. An error response that carries no valid state now renders “missing required parameters” instead of the OAuth denial page.
  • Fixed

    • AWS SigV4 request signing for MCPServers. spec.auth.type: sigv4 with spec.auth.sigv4 (region, optional service, optional roleArn) makes muster sign every request to a streamable-http backend with AWS Signature Version 4, as its own machine identity rather than the caller’s, using the default AWS credential chain so IRSA needs no extra configuration; spec.family plus one roleArn per MCPServer puts many AWS accounts behind a single tool set with an account selector. Every signed request carries X-Amz-Content-Sha256 inside its SignedHeaders, so a backend that requires the payload-hash header (S3 and the services sharing its request model, reachable because auth.sigv4.service is user-settable) accepts the signature.
    • spec.meta for remote MCPServers. The map is merged into the params._meta object of every outbound JSON-RPC request that carries params, for a backend that reads call-scoped configuration from MCP metadata rather than tool arguments — the AWS-hosted server takes the region it operates in from params._meta.AWS_REGION, which is a different value from the SigV4 signing region, and omitting it does not fail but answers about the wrong region. Injection happens in an HTTP round tripper shared by every remote client, so it applies to streamable-http and sse alike, with any auth type and with none, and it survives the switch to the per-session connection that an OAuth server’s tool calls run on; for a SigV4 server the round tripper sits in front of the signer, because the body has to be rewritten before it is signed. type: stdio rejects the field (CEL rule plus the same check in filesystem mode): a stdio server speaks over a pipe, so no HTTP transport can inject the entries, and a dropped entry fails nowhere.
  • Added

    • kyvernoPolicies.policyExceptionNamespace (policy-exceptions), kyvernoPolicies.seccompPolicyName (restrict-seccomp-strict) and kyvernoPolicies.seccompRuleNames, the cluster-owned names the seccomp PolicyException targets. They default to the Giant Swarm cluster policy names; a cluster that names its policies differently must override them, or the exception matches nothing.

    Changed

    • kyvernoPolicies.seccompRuleNames must hold at least one non-empty name, and the connectivity chart fails the render when agentSandbox.podSecurity.enabled is on without kyvernoPolicies.enabled. Both configurations used to render an object that silently matched nothing.
    • The agent-sandbox pod-security ClusterPolicy no longer carries helm.sh/resource-policy: keep. Helm prunes it now whenever it stops rendering (kyvernoPolicies.enabled: false, agentSandbox.podSecurity.enabled: false, or the planned v0.5.0 removal of the template), instead of leaving an orphan ClusterPolicy that has to be deleted by hand.
    • The agent-sandbox component dependsOn the agent-platform-connectivity release, so Kyverno holds the mutate policy that carries the controller Deployment’s securityContext before the Deployment applies. The agent-sandbox release no longer fails its first install on a restricted-PSS cluster and then converges on a retry. The window is narrowed, not closed: dependsOn waits for the ClusterPolicy to apply, not for the Kyverno webhook to serve it, so the release retries still cover the rest. The agent-sandbox release now also waits on the whole connectivity release, so a connectivity failure holds it back. Flux only: the argo engine has no dependsOn, and its sync waves do not express this order yet.
  • Fixed

    • (oauth) Complete the callback flow despite duplicate delivery and client disconnect in #1090 by @teemow

    Full Changelog: https://github.com/giantswarm/muster/compare/v5.3.0...v5.3.1

  • Added

    • (mcpserver) Add core_mcpserver_detect for transport auto-detection in #1087 by @teemow

    Full Changelog: https://github.com/giantswarm/muster/compare/v5.2.1...v5.3.0

  • Fixed

    • (oauth) Omit scope from RFC 7591 registration requests in #1086 by @teemow

    Full Changelog: https://github.com/giantswarm/muster/compare/v5.2.0...v5.2.1

  • Fixed

    • Test framework: expectations are now evaluated by a single implementation for both step kinds, so an assertion means the same thing whether it is written on an MCP tool step or a test_* step. The two paths each carried their own copy of the checks and had drifted three times — json_path (#1036), not_contains and wait_for_state (#1078) were each implemented on one path and silently ignored on the other, so a scenario declaring one on the wrong kind of step passed regardless of the response. Each path now only adapts its own response shape and shares the checks. Two behaviours are unified in the process: error_contains is evaluated whenever it is declared (the test-tool path previously skipped it unless success: false), and a success: false payload now fails an MCP step that expected success (previously checked only for test_* steps). All 186 scenarios are unaffected by both. A new test asserts that every field of TestExpectation is enforced on both paths, and fails the build when an expectation kind is added without being wired up — the class of bug behind #1038.
    • Test framework: status_code in a step’s expected block is now rejected at load time, with a message pointing at json_path. It was accepted by the schema and printed by the reporter but never evaluated by either path, so a scenario asserting an HTTP status asserted nothing. No scenario in the repository used it.
  • Changed

    • components.muster.versionRange default changed from ">=4.1.0 <5.0.0" to "5.x", so the meta-package resolves the current muster line (v5.1.0). The >=4.1.0 floor is subsumed by the major: it existed because the muster values here set observability.metrics.prometheus.prometheusRule, and every 5.x accepts that key. No installation needs to act. muster v5.0.0 (muster#1078) is breaking only for muster instances running without their own OAuth: with no bearer there was no token family, so the transport injected the default-user placeholder and every concurrent MCP client collapsed onto one session — one client’s authenticated backend became visible and callable in every other client’s session, on the first client’s token. Such sessions are now keyed by MCP transport session, which means a session ends with the connection and a reconnecting client re-runs core_auth_login. Every installation of this umbrella runs muster.muster.oauth.server.enabled: true (the default here, overridden in no *-configs installation patch across the 14 deployments), so a real token-family session ID still wins and the crossing is a no-op for the fleet; stdio is unaffected either way. muster v5.1.0 (muster#1079) additionally fails the render on an explicitly emptied rbac.mcpServerEditor.subjects / rbac.workflowEditor.subjects; both are bound to the Giant Swarm admin groups by this chart (since #232) and overridden nowhere, so the guard is satisfied. The crossing also picks up muster#1077 (the chart values-schema fix), which the never-published v4.1.1 chart left unreachable.
  • Fixed

    • The kagent-pg CNPG Cluster now sets inheritedMetadata.labels with observability.giantswarm.io/tenant: giantswarm, which the operator copies onto the PodMonitor it generates. Without it the PodMonitor carried only CNPG’s stock labels and matched neither of alloy-metrics’ discovery selectors, so it was created and silently never scraped (zero cnpg_* series in Mimir). The previous podMonitorRelabelings attempt is removed: it rewrote scraped series, not the PodMonitor object’s labels, so it could never make the monitor discoverable.
  • Changed

    Full Changelog: https://github.com/giantswarm/agent-platform/compare/v2.11.0...v2.11.1