diff --git a/.github/upstream-projects.yaml b/.github/upstream-projects.yaml index a925b9f0..c541ab03 100644 --- a/.github/upstream-projects.yaml +++ b/.github/upstream-projects.yaml @@ -35,7 +35,7 @@ projects: - id: toolhive repo: stacklok/toolhive - version: v0.28.3 + version: v0.29.0 # toolhive is a monorepo covering the CLI, the Kubernetes # operator, and the vMCP gateway. It also introduces cross- # cutting features that land in concepts/, integrations/, diff --git a/docs/toolhive/concepts/embedded-auth-server.mdx b/docs/toolhive/concepts/embedded-auth-server.mdx index a7da9740..6b4d17df 100644 --- a/docs/toolhive/concepts/embedded-auth-server.mdx +++ b/docs/toolhive/concepts/embedded-auth-server.mdx @@ -166,6 +166,36 @@ DCR-registered client gains the ability to request these scopes, including public clients like Claude Code, Cursor, and VS Code, so privileged scopes do not belong in the baseline. +## Client ID Metadata Document (CIMD) + +DCR requires every client to register before its first authorization request. +Some MCP clients, including recent VS Code builds, can instead present an HTTPS +URL that hosts a Client ID Metadata Document (CIMD), letting the authorization +server resolve client metadata on demand without a prior registration step. + +To allow CIMD-style client IDs, enable CIMD on the embedded authorization +server. When enabled, the server accepts HTTPS URLs as `client_id` values, +fetches the document from the URL, and caches the result. When disabled (the +default), only DCR-registered `client_id` values are accepted. + +```yaml +spec: + embeddedAuthServer: + cimd: + enabled: true + cacheMaxSize: 256 + cacheFallbackTtl: '5m' +``` + +`cacheMaxSize` sets the LRU cache capacity (default `256`), and +`cacheFallbackTtl` sets the TTL applied to every cached entry as a Go duration +string (default `5m`). The CIMD fetcher does not yet honor `Cache-Control` +headers; every cached document uses the fallback TTL. + +If you also set `baselineClientScopes`, those scopes apply to CIMD-resolved +clients too. Because CIMD clients can be resolved from arbitrary HTTPS URLs, +keep the baseline narrow. + ## Session storage By default, session storage is in-memory. Upstream tokens are lost when pods diff --git a/docs/toolhive/guides-cli/manage-mcp-servers.mdx b/docs/toolhive/guides-cli/manage-mcp-servers.mdx index 96059848..8a0358f8 100644 --- a/docs/toolhive/guides-cli/manage-mcp-servers.mdx +++ b/docs/toolhive/guides-cli/manage-mcp-servers.mdx @@ -161,6 +161,92 @@ thv start This will always prompt for re-authentication, even if valid tokens exist. +## Check for and apply upgrades + +For MCP servers you ran from a registry entry (`thv run `), +ToolHive can compare each workload's image and configuration against the +registry's current entry and report when a newer version is available. This only +works for registry-sourced workloads on the local runtime; remote servers and +workloads run directly from an image reference report as `not-registry-sourced`. + +:::note + +Upgrade detection is not yet available for the Kubernetes operator. + +::: + +### Check for available upgrades + +The `thv upgrade check` command is an offline metadata comparison: it inspects +each workload's image tag and configuration against the registry and never pulls +images. + +To see a summary table for every workload (including stopped ones): + +```bash +thv upgrade check +``` + +Each row shows the workload's current image, the candidate image the registry +reports, the count of new environment variables the candidate declares, and a +posture marker if the candidate's transport or permission profile differs from +the workload's current configuration. Possible statuses are `up-to-date`, +`upgrade-available`, `not-registry-sourced`, `server-not-found`, and `unknown`. + +For a detailed report on a single workload, pass its name: + +```bash +thv upgrade check +``` + +The detailed report adds the registry server name, any new environment variables +(with their descriptions and whether they're required), and the specific +transport or permission profile changes between the running workload and the +candidate. + +You can also add an upgrade column to the `thv list` output with an opt-in flag: + +```bash +thv list --check-upgrades +``` + +The default `thv list` output is unchanged and performs no registry lookup, so +it stays offline-friendly. + +### Apply an upgrade + +To upgrade a workload to the candidate image while preserving its configuration +(environment variables, secrets, permission profile, transport, volumes, +middleware), use `thv upgrade apply`: + +```bash +thv upgrade apply +``` + +ToolHive resolves, verifies, and pulls the candidate image **before** touching +the running workload, then stops the existing workload and starts a new one on +the candidate image. There is no automatic rollback: if recreation fails after +the existing workload is stopped, recovery is a forward operation. For an +interactive session, the command prints what would change and prompts for +confirmation before applying. + +If the candidate declares new environment variables or secrets that the workload +does not yet supply, pass them with `--env` or `--secret`: + +```bash +thv upgrade apply --env NEW_FLAG=true --secret api-key,target=API_KEY +``` + +To preview the planned changes without applying them: + +```bash +thv upgrade apply --dry-run +``` + +To skip the confirmation prompt, pass `--yes`. When stdin is not a TTY (for +example, in a script), the prompt is skipped automatically. In either case, the +command fails if a required value is missing. + ## Next steps - [Organize servers into groups](./group-management.mdx) to manage related @@ -177,3 +263,5 @@ This will always prompt for re-authentication, even if valid tokens exist. - [`thv stop` command reference](../reference/cli/thv_stop.md) - [`thv start` command reference](../reference/cli/thv_start.md) - [`thv rm` command reference](../reference/cli/thv_rm.md) +- [`thv upgrade check` command reference](../reference/cli/thv_upgrade_check.md) +- [`thv upgrade apply` command reference](../reference/cli/thv_upgrade_apply.md) diff --git a/docs/toolhive/guides-cli/test-mcp-servers.mdx b/docs/toolhive/guides-cli/test-mcp-servers.mdx index c0f4f33f..87468ade 100644 --- a/docs/toolhive/guides-cli/test-mcp-servers.mdx +++ b/docs/toolhive/guides-cli/test-mcp-servers.mdx @@ -51,9 +51,9 @@ you can interactively test the MCP server's tools, prompts, and resources. ## `thv mcp` commands -ToolHive also includes several -[`thv mcp` commands](../reference/cli/thv_mcp_list.md) to test and validate MCP -servers. You can list the MCP server's tools, prompts, and resources: +ToolHive also includes several [`thv mcp` commands](../reference/cli/thv_mcp.md) +to test and validate MCP servers. You can list the MCP server's tools, prompts, +and resources: ```bash thv mcp list tools --server @@ -78,6 +78,37 @@ directly, like `thv mcp list tools --server http://localhost:12345/mcp`. ::: +### Invoke a tool + +`thv mcp list` shows _what_ a server exposes, but not how it behaves. To invoke +a tool directly from the CLI and inspect its result without opening the +Inspector, use `thv mcp call`: + +```bash +thv mcp call --server +``` + +Pass arguments as a JSON object via `--args` (inline) or `--args-file` (file +path; use `-` to read from stdin). The two flags are mutually exclusive, and the +parsed value must be a JSON object. If you supply neither flag, the tool is +called with no arguments. + +For example, to call the `fetch` tool on a `fetch` MCP server: + +```bash +thv mcp call fetch --server fetch --args '{"url":"https://example.com"}' +``` + +By default, the command exits non-zero when the tool reports an error +(`CallToolResult.IsError=true`); pass `--ignore-tool-error` to exit zero in that +case. Transport and protocol failures always exit non-zero. + +Add `--format json` to print the full `CallToolResult` instead of the default +text rendering. This is useful for piping output into other tools or capturing +structured content in scripts. + +The `--transport` and `--timeout` flags work the same way as for `thv mcp list`. + ## ToolHive playground While the MCP Inspector and `thv mcp` commands are great for basic functional @@ -103,3 +134,4 @@ about the playground's features and how to get started. - [`thv inspector` command reference](../reference/cli/thv_inspector.md) - [`thv mcp list` command reference](../reference/cli/thv_mcp_list.md) +- [`thv mcp call` command reference](../reference/cli/thv_mcp_call.md) diff --git a/docs/toolhive/guides-k8s/auth-k8s.mdx b/docs/toolhive/guides-k8s/auth-k8s.mdx index 5a819fcb..2a05570f 100644 --- a/docs/toolhive/guides-k8s/auth-k8s.mdx +++ b/docs/toolhive/guides-k8s/auth-k8s.mdx @@ -544,14 +544,15 @@ kubectl apply -f embedded-auth-config.yaml **Configuration reference:** -| Field | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. | -| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. | -| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. | -| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). | -| `upstreamProviders` | Configuration for upstream identity providers. MCPServer and MCPRemoteProxy support one provider; VirtualMCPServer supports multiple providers for sequential authentication. | -| `baselineClientScopes` | Optional list of OAuth 2.0 scopes merged into every DCR-registered client's scope set. Use this when MCP clients register with a narrowed `scope` field but then request wider scopes at `/oauth/authorize`. See [Baseline scopes for DCR clients](../concepts/embedded-auth-server.mdx#baseline-scopes-for-dcr-clients). | +| Field | Description | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. | +| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. | +| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. | +| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). | +| `upstreamProviders` | Configuration for upstream identity providers. MCPServer and MCPRemoteProxy support one provider; VirtualMCPServer supports multiple providers for sequential authentication. | +| `baselineClientScopes` | Optional list of OAuth 2.0 scopes merged into every DCR-registered client's scope set. Use this when MCP clients register with a narrowed `scope` field but then request wider scopes at `/oauth/authorize`. See [Baseline scopes for DCR clients](../concepts/embedded-auth-server.mdx#baseline-scopes-for-dcr-clients). | +| `cimd` | Optional Client ID Metadata Document (CIMD) configuration. When `cimd.enabled` is `true`, the auth server accepts HTTPS URLs as `client_id` values and resolves them via CIMD, letting clients (for example, VS Code) authenticate without prior Dynamic Client Registration. See [Client ID Metadata Document (CIMD)](../concepts/embedded-auth-server.mdx#client-id-metadata-document-cimd). | **Step 5: Create the MCPOIDCConfig and MCPServer resources** diff --git a/docs/toolhive/reference/cli/thv.md b/docs/toolhive/reference/cli/thv.md index 2749ca76..74824dfa 100644 --- a/docs/toolhive/reference/cli/thv.md +++ b/docs/toolhive/reference/cli/thv.md @@ -58,6 +58,7 @@ thv [flags] * [thv status](thv_status.md) - Show detailed status of an MCP server * [thv stop](thv_stop.md) - Stop one or more MCP servers * [thv tui](thv_tui.md) - Open the interactive TUI dashboard (experimental) +* [thv upgrade](thv_upgrade.md) - Manage upgrades for MCP server workloads * [thv version](thv_version.md) - Show the version of ToolHive * [thv vmcp](thv_vmcp.md) - Run and manage a Virtual MCP Server locally diff --git a/docs/toolhive/reference/cli/thv_client_register.md b/docs/toolhive/reference/cli/thv_client_register.md index fda1e600..cb5e076c 100644 --- a/docs/toolhive/reference/cli/thv_client_register.md +++ b/docs/toolhive/reference/cli/thv_client_register.md @@ -38,7 +38,7 @@ Valid clients: - lm-studio: LM Studio application - mistral-vibe: Mistral Vibe IDE - opencode: OpenCode editor - - roo-code: VS Code Roo Code extension + - roo-code: VS Code Roo Code extension (deprecated) - trae: Trae IDE - vscode: Visual Studio Code - vscode-insider: Visual Studio Code Insiders diff --git a/docs/toolhive/reference/cli/thv_client_remove.md b/docs/toolhive/reference/cli/thv_client_remove.md index f32115fc..57480144 100644 --- a/docs/toolhive/reference/cli/thv_client_remove.md +++ b/docs/toolhive/reference/cli/thv_client_remove.md @@ -38,7 +38,7 @@ Valid clients: - lm-studio: LM Studio application - mistral-vibe: Mistral Vibe IDE - opencode: OpenCode editor - - roo-code: VS Code Roo Code extension + - roo-code: VS Code Roo Code extension (deprecated) - trae: Trae IDE - vscode: Visual Studio Code - vscode-insider: Visual Studio Code Insiders diff --git a/docs/toolhive/reference/cli/thv_list.md b/docs/toolhive/reference/cli/thv_list.md index c0a3acfc..4b769921 100644 --- a/docs/toolhive/reference/cli/thv_list.md +++ b/docs/toolhive/reference/cli/thv_list.md @@ -41,6 +41,7 @@ thv list [flags] ``` -a, --all Show all workloads (default shows just running) + --check-upgrades Check each workload for available upgrades against its source registry (performs a registry lookup) --format string Output format (json, text, mcpservers) (default "text") --group string Filter by group -h, --help help for list diff --git a/docs/toolhive/reference/cli/thv_mcp.md b/docs/toolhive/reference/cli/thv_mcp.md index ca6131c3..386dd269 100644 --- a/docs/toolhive/reference/cli/thv_mcp.md +++ b/docs/toolhive/reference/cli/thv_mcp.md @@ -32,6 +32,7 @@ The mcp command provides subcommands to interact with MCP (Model Context Protoco ### SEE ALSO * [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +* [thv mcp call](thv_mcp_call.md) - Invoke a tool on an MCP server * [thv mcp list](thv_mcp_list.md) - List MCP server capabilities * [thv mcp serve](thv_mcp_serve.md) - ๐Ÿงช EXPERIMENTAL: Start an MCP server to control ToolHive diff --git a/docs/toolhive/reference/cli/thv_mcp_call.md b/docs/toolhive/reference/cli/thv_mcp_call.md new file mode 100644 index 00000000..0973192f --- /dev/null +++ b/docs/toolhive/reference/cli/thv_mcp_call.md @@ -0,0 +1,54 @@ +--- +title: thv mcp call +hide_title: true +description: Reference for ToolHive CLI command `thv mcp call` +last_update: + author: autogenerated +slug: thv_mcp_call +mdx: + format: md +--- + +## thv mcp call + +Invoke a tool on an MCP server + +### Synopsis + +Invoke a tool on an MCP server. The server is connected, initialized, +the tool is called with the supplied arguments, and the result is printed. + +Arguments are supplied as a JSON object via --args or --args-file. If neither +flag is set, the tool is called with an empty argument object. + +By default, the command exits with a non-zero status when the tool reports an +error (CallToolResult.IsError=true). Use --ignore-tool-error to exit zero in +that case; transport and protocol failures always exit non-zero. + +``` +thv mcp call [flags] +``` + +### Options + +``` + --args string Tool arguments as a JSON object literal + --args-file string Path to a file containing a JSON object of tool arguments (use '-' to read from stdin) + --format string Output format (json, text) (default "text") + -h, --help help for call + --ignore-tool-error Exit zero even when the tool reports an error (default is non-zero) + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +* [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging + diff --git a/docs/toolhive/reference/cli/thv_upgrade.md b/docs/toolhive/reference/cli/thv_upgrade.md new file mode 100644 index 00000000..03998668 --- /dev/null +++ b/docs/toolhive/reference/cli/thv_upgrade.md @@ -0,0 +1,41 @@ +--- +title: thv upgrade +hide_title: true +description: Reference for ToolHive CLI command `thv upgrade` +last_update: + author: autogenerated +slug: thv_upgrade +mdx: + format: md +--- + +## thv upgrade + +Manage upgrades for MCP server workloads + +### Synopsis + +Inspect and apply upgrades for registry-sourced MCP server workloads. + +Upgrade checks compare each workload's current image and configuration against +the metadata reported by its source registry. Checks are an offline metadata +comparison and never pull images. + +### Options + +``` + -h, --help help for upgrade +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +* [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +* [thv upgrade apply](thv_upgrade_apply.md) - Apply an available upgrade to a workload +* [thv upgrade check](thv_upgrade_check.md) - Check workloads for available upgrades + diff --git a/docs/toolhive/reference/cli/thv_upgrade_apply.md b/docs/toolhive/reference/cli/thv_upgrade_apply.md new file mode 100644 index 00000000..da7d73d2 --- /dev/null +++ b/docs/toolhive/reference/cli/thv_upgrade_apply.md @@ -0,0 +1,67 @@ +--- +title: thv upgrade apply +hide_title: true +description: Reference for ToolHive CLI command `thv upgrade apply` +last_update: + author: autogenerated +slug: thv_upgrade_apply +mdx: + format: md +--- + +## thv upgrade apply + +Apply an available upgrade to a workload + +### Synopsis + +Apply the upgrade the registry reports for a registry-sourced workload. + +The candidate image is resolved, verified, and pulled BEFORE the existing +workload is touched. The existing workload is then stopped and replaced with one +running the candidate image; the rest of the workload's configuration (env vars, +secrets, posture, middleware) is preserved. There is no automatic rollback: if +recreation fails the previous workload is not restored, so recovery is a forward +operation. + +New environment variables the candidate declares can be supplied with --env and +--secret. When run interactively, missing required values are prompted for; with +--yes (or in a non-interactive shell) the command runs non-interactively and +fails if a required value is missing. + +Examples: + # Apply the available upgrade, prompting for confirmation + thv upgrade apply my-server + + # Apply non-interactively, supplying a new env var + thv upgrade apply my-server --yes --env NEW_FLAG=true + + # Preview what an upgrade would change without applying it + thv upgrade apply my-server --dry-run + +``` +thv upgrade apply [flags] +``` + +### Options + +``` + --ca-cert string Path to a custom CA certificate file to use when resolving the candidate image + --dry-run Print what the upgrade would change without applying it + -e, --env stringArray Environment variables to set on the upgraded workload (format: KEY=VALUE, repeatable) + -h, --help help for apply + --image-verification string Set image verification mode (warn, enabled, disabled) (default "warn") + --secret stringArray Secrets to set on the upgraded workload (format: NAME,target=TARGET, repeatable) + -y, --yes Skip the confirmation prompt and run non-interactively (fail if required values are missing) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +* [thv upgrade](thv_upgrade.md) - Manage upgrades for MCP server workloads + diff --git a/docs/toolhive/reference/cli/thv_upgrade_check.md b/docs/toolhive/reference/cli/thv_upgrade_check.md new file mode 100644 index 00000000..be1f501d --- /dev/null +++ b/docs/toolhive/reference/cli/thv_upgrade_check.md @@ -0,0 +1,55 @@ +--- +title: thv upgrade check +hide_title: true +description: Reference for ToolHive CLI command `thv upgrade check` +last_update: + author: autogenerated +slug: thv_upgrade_check +mdx: + format: md +--- + +## thv upgrade check + +Check workloads for available upgrades + +### Synopsis + +Check whether registry-sourced workloads have a newer image available. + +With no arguments, all workloads are checked (including stopped ones) and a +summary table is printed. When a workload name is given, a detailed report for +that single workload is printed, including any new environment variables the +candidate image declares and any configuration (posture) drift. + +Examples: + # Check all workloads + thv upgrade check + + # Check a single workload with detailed output + thv upgrade check my-server + + # Check all workloads in JSON format + thv upgrade check --format json + +``` +thv upgrade check [workload-name] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for check +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +* [thv upgrade](thv_upgrade.md) - Manage upgrades for MCP server workloads + diff --git a/static/api-specs/crds/mcpexternalauthconfigs.schema.json b/static/api-specs/crds/mcpexternalauthconfigs.schema.json index 775ccea3..e9179049 100644 --- a/static/api-specs/crds/mcpexternalauthconfigs.schema.json +++ b/static/api-specs/crds/mcpexternalauthconfigs.schema.json @@ -133,7 +133,7 @@ "type": "string" }, "baselineClientScopes": { - "description": "BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be\nincluded in every client registration. The embedded auth server unions these\nscopes into the registered set returned by RFC 7591 Dynamic Client\nRegistration, so a client that narrows the `scope` field at /oauth/register\ncan still request the baseline scopes at /oauth/authorize. All values must\nbe present in the upstream-derived scopesSupported set; the auth server\nfails to start if any value is missing.\n\nSecurity: every client registered via /oauth/register will gain the\nability to request these scopes at /oauth/authorize, regardless of what\nthe client itself requested. Keep the baseline narrow (typically\n\"openid\" and \"offline_access\"). Adding a privileged scope here โ€” e.g.\n\"admin:read\" โ€” would grant it to every DCR-registered client, including\npublic clients like Claude Code, Cursor, and VS Code.", + "description": "BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be\nincluded in every client registration. The embedded auth server unions these\nscopes into the registered set returned by RFC 7591 Dynamic Client\nRegistration, so a client that narrows the `scope` field at /oauth/register\ncan still request the baseline scopes at /oauth/authorize. All values must\nbe present in the upstream-derived scopesSupported set; the auth server\nfails to start if any value is missing.\n\nSecurity: every client registered via /oauth/register will gain the\nability to request these scopes at /oauth/authorize, regardless of what\nthe client itself requested. Keep the baseline narrow (typically\n\"openid\" and \"offline_access\"). Adding a privileged scope here โ€” e.g.\n\"admin:read\" โ€” would grant it to every DCR-registered client, including\npublic clients like Claude Code, Cursor, and VS Code.\nWhen cimd.enabled is true, every dynamically resolved CIMD client will\nalso gain the ability to request these scopes, including third-party\nclients resolved from arbitrary HTTPS URLs.", "items": { "minLength": 1, "pattern": "^[\\x21\\x23-\\x5B\\x5D-\\x7E]+$", @@ -143,6 +143,30 @@ "type": "array", "x-kubernetes-list-type": "atomic" }, + "cimd": { + "description": "CIMD configures Client ID Metadata Document support. When omitted, CIMD is disabled.", + "properties": { + "cacheFallbackTtl": { + "description": "CacheFallbackTTL is the fixed TTL applied to every cached CIMD document.\nCache-Control header parsing is not yet implemented; all entries use this value.\nFormat: Go duration string (e.g. \"5m\", \"10m\", \"1h\").\nDefaults to 5 minutes when Enabled is true and this field is omitted.", + "pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|ยตs|ms|s|m|h))+$", + "type": "string" + }, + "cacheMaxSize": { + "description": "CacheMaxSize is the maximum number of CIMD documents held in the LRU cache.\nDefaults to 256 when Enabled is true and this field is omitted.", + "minimum": 1, + "type": "integer" + }, + "enabled": { + "default": false, + "description": "Enabled activates CIMD client lookup. When false (the default), the AS only\naccepts client_id values that were registered via DCR.", + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, "hmacSecretRefs": { "description": "HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing\nauthorization codes and refresh tokens (opaque tokens).\nCurrent secret must be at least 32 bytes and cryptographically random.\nSupports secret rotation via multiple entries (first is current, rest are for verification).\nIf not specified, an ephemeral secret will be auto-generated (development only -\nauth codes and refresh tokens will be invalid after restart).", "items": { @@ -866,6 +890,10 @@ ], "type": "object" }, + "obo": { + "description": "OBO configures On-Behalf-Of (OBO) authentication.\nOnly used when Type is \"obo\". The inner schema is intentionally empty in\nthis revision; sub-fields land in a follow-up. Setting this field on an\nupstream-only build will cause the MCPExternalAuthConfig to transition to\nstatus.conditions[Valid] = False with Reason: EnterpriseRequired.", + "type": "object" + }, "tokenExchange": { "description": "TokenExchange configures RFC-8693 OAuth 2.0 Token Exchange\nOnly used when Type is \"tokenExchange\"", "properties": { @@ -928,7 +956,7 @@ "type": "object" }, "type": { - "description": "Type is the type of external authentication to configure", + "description": "Type is the type of external authentication to configure.\nWhen set to \"obo\", the cluster must run a build that has registered an\nOBO handler via controllerutil.RegisterOBOHandler; upstream-only builds\nsurface status.conditions[Valid] = False with Reason: EnterpriseRequired\nfor obo-typed configs.", "enum": [ "tokenExchange", "headerInjection", @@ -936,7 +964,8 @@ "unauthenticated", "embeddedAuthServer", "awsSts", - "upstreamInject" + "upstreamInject", + "obo" ], "type": "string" }, @@ -984,9 +1013,13 @@ "message": "upstreamInject configuration must be set if and only if type is 'upstreamInject'", "rule": "self.type == 'upstreamInject' ? has(self.upstreamInject) : !has(self.upstreamInject)" }, + { + "message": "obo configuration must be set if and only if type is 'obo'", + "rule": "self.type == 'obo' ? has(self.obo) : !has(self.obo)" + }, { "message": "no configuration must be set when type is 'unauthenticated'", - "rule": "self.type == 'unauthenticated' ? (!has(self.tokenExchange) && !has(self.headerInjection) && !has(self.bearerToken) && !has(self.embeddedAuthServer) && !has(self.awsSts) && !has(self.upstreamInject)) : true" + "rule": "self.type == 'unauthenticated' ? (!has(self.tokenExchange) && !has(self.headerInjection) && !has(self.bearerToken) && !has(self.embeddedAuthServer) && !has(self.awsSts) && !has(self.upstreamInject) && !has(self.obo)) : true" } ] }, diff --git a/static/api-specs/crds/virtualmcpservers.schema.json b/static/api-specs/crds/virtualmcpservers.schema.json index 6b2b7af2..8a70f97f 100644 --- a/static/api-specs/crds/virtualmcpservers.schema.json +++ b/static/api-specs/crds/virtualmcpservers.schema.json @@ -24,7 +24,7 @@ "type": "string" }, "baselineClientScopes": { - "description": "BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be\nincluded in every client registration. The embedded auth server unions these\nscopes into the registered set returned by RFC 7591 Dynamic Client\nRegistration, so a client that narrows the `scope` field at /oauth/register\ncan still request the baseline scopes at /oauth/authorize. All values must\nbe present in the upstream-derived scopesSupported set; the auth server\nfails to start if any value is missing.\n\nSecurity: every client registered via /oauth/register will gain the\nability to request these scopes at /oauth/authorize, regardless of what\nthe client itself requested. Keep the baseline narrow (typically\n\"openid\" and \"offline_access\"). Adding a privileged scope here โ€” e.g.\n\"admin:read\" โ€” would grant it to every DCR-registered client, including\npublic clients like Claude Code, Cursor, and VS Code.", + "description": "BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be\nincluded in every client registration. The embedded auth server unions these\nscopes into the registered set returned by RFC 7591 Dynamic Client\nRegistration, so a client that narrows the `scope` field at /oauth/register\ncan still request the baseline scopes at /oauth/authorize. All values must\nbe present in the upstream-derived scopesSupported set; the auth server\nfails to start if any value is missing.\n\nSecurity: every client registered via /oauth/register will gain the\nability to request these scopes at /oauth/authorize, regardless of what\nthe client itself requested. Keep the baseline narrow (typically\n\"openid\" and \"offline_access\"). Adding a privileged scope here โ€” e.g.\n\"admin:read\" โ€” would grant it to every DCR-registered client, including\npublic clients like Claude Code, Cursor, and VS Code.\nWhen cimd.enabled is true, every dynamically resolved CIMD client will\nalso gain the ability to request these scopes, including third-party\nclients resolved from arbitrary HTTPS URLs.", "items": { "minLength": 1, "pattern": "^[\\x21\\x23-\\x5B\\x5D-\\x7E]+$", @@ -34,6 +34,30 @@ "type": "array", "x-kubernetes-list-type": "atomic" }, + "cimd": { + "description": "CIMD configures Client ID Metadata Document support. When omitted, CIMD is disabled.", + "properties": { + "cacheFallbackTtl": { + "description": "CacheFallbackTTL is the fixed TTL applied to every cached CIMD document.\nCache-Control header parsing is not yet implemented; all entries use this value.\nFormat: Go duration string (e.g. \"5m\", \"10m\", \"1h\").\nDefaults to 5 minutes when Enabled is true and this field is omitted.", + "pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|ยตs|ms|s|m|h))+$", + "type": "string" + }, + "cacheMaxSize": { + "description": "CacheMaxSize is the maximum number of CIMD documents held in the LRU cache.\nDefaults to 256 when Enabled is true and this field is omitted.", + "minimum": 1, + "type": "integer" + }, + "enabled": { + "default": false, + "description": "Enabled activates CIMD client lookup. When false (the default), the AS only\naccepts client_id values that were registered via DCR.", + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, "hmacSecretRefs": { "description": "HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing\nauthorization codes and refresh tokens (opaque tokens).\nCurrent secret must be at least 32 bytes and cryptographically random.\nSupports secret rotation via multiple entries (first is current, rest are for verification).\nIf not specified, an ephemeral secret will be auto-generated (development only -\nauth codes and refresh tokens will be invalid after restart).", "items": { diff --git a/static/api-specs/toolhive-api.yaml b/static/api-specs/toolhive-api.yaml index 6e9d446e..e0b26c2f 100644 --- a/static/api-specs/toolhive-api.yaml +++ b/static/api-specs/toolhive-api.yaml @@ -346,6 +346,29 @@ components: This is required and must match a configured upstream provider name. type: string type: object + github_com_stacklok_toolhive_pkg_authserver.CIMDRunConfig: + description: |- + CIMD controls client_id metadata document support. When enabled, the + embedded authorization server accepts HTTPS URLs as client_id values + and resolves them via the CIMD protocol instead of requiring DCR. + properties: + cache_fallback_ttl: + description: |- + 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. + example: 5m + type: string + cache_max_size: + description: |- + CacheMaxSize is the maximum number of CIMD documents held in the LRU cache. + Defaults to 256 when Enabled is true and this field is zero. + type: integer + enabled: + description: Enabled activates CIMD client lookup when true. + type: boolean + type: object github_com_stacklok_toolhive_pkg_authserver.DCRUpstreamConfig: description: |- DCRConfig enables RFC 7591 Dynamic Client Registration against the @@ -573,6 +596,8 @@ components: type: string type: array uniqueItems: false + cimd: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_authserver.CIMDRunConfig' hmac_secret_files: description: |- HMACSecretFiles contains file paths to HMAC secrets for signing authorization codes @@ -1897,6 +1922,118 @@ components: WARNING: This should only be used for development/testing. type: boolean type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.CheckResult: + description: |- + Result is the upgrade-check outcome for the workload. It carries only + metadata (status, image references, drift) and never secret values. + properties: + candidate_image: + description: CandidateImage is the image reference the registry currently + reports. + type: string + config_drift: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.ConfigDrift' + current_image: + description: CurrentImage is the image reference the workload is currently + running. + type: string + env_var_drift: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.EnvVarDrift' + reason: + description: Reason provides additional context, primarily for StatusUnknown. + type: string + registry_server: + description: |- + RegistryServer is the registry entry name the workload was sourced from. + Empty when the workload is not registry-sourced. + type: string + status: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.UpgradeStatus' + workload_name: + description: WorkloadName is the name of the workload that was checked. + type: string + type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.ConfigDrift: + description: |- + ConfigDrift describes posture differences (transport, permission profile) + between the workload and the candidate registry entry. + properties: + permission_profile: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.StringChange' + transport: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.StringChange' + type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.EnvVarDrift: + description: |- + EnvVarDrift describes environment variables the candidate registry entry + declares that differ from the workload's current configuration. + properties: + added: + description: |- + Added lists environment variables the candidate declares that the + workload does not currently supply (via plain env vars or secrets). + items: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.EnvVarInfo' + type: array + uniqueItems: false + removed: + description: |- + Removed lists environment variables the workload supplies that the + candidate no longer declares. Populated on a best-effort basis; may be + empty even when removals exist (forward-compatible field). + items: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.EnvVarInfo' + type: array + uniqueItems: false + type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.EnvVarInfo: + properties: + default: + description: |- + Default is the candidate's default value. It is cleared (left empty) + whenever Secret is true: a secret env var's default could carry sensitive + data, and surfacing it in a drift report (which may be logged or returned + over the API) would leak it. Non-secret defaults are safe to display. + type: string + description: + description: Description is the human-readable purpose of the variable. + type: string + name: + description: Name is the environment variable name. + type: string + required: + description: Required indicates whether the candidate marks the variable + as required. + type: boolean + secret: + description: Secret indicates whether the variable holds sensitive data. + type: boolean + type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.StringChange: + description: |- + PermissionProfile is set when the candidate's permission profile differs + from the workload's current profile. + properties: + from: + type: string + to: + type: string + type: object + github_com_stacklok_toolhive_pkg_workloads_upgrade.UpgradeStatus: + description: Status is the upgrade status for the workload. + enum: + - up-to-date + - upgrade-available + - not-registry-sourced + - server-not-found + - unknown + type: string + x-enum-varnames: + - StatusUpToDate + - StatusUpgradeAvailable + - StatusNotRegistrySourced + - StatusServerNotFound + - StatusUnknown model.Argument: properties: choices: @@ -2973,6 +3110,47 @@ components: description: Success message type: string type: object + pkg_api_v1.upgradeCheckBulkResponse: + description: Results of checking multiple workloads for available upgrades + properties: + results: + description: |- + Results holds one upgrade-check outcome per scoped workload, in the order + the workloads were enumerated. Each entry carries only metadata and never + secret values. + items: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.CheckResult' + type: array + uniqueItems: false + type: object + pkg_api_v1.upgradeCheckResponse: + description: Result of checking a single workload for an available upgrade + properties: + result: + $ref: '#/components/schemas/github_com_stacklok_toolhive_pkg_workloads_upgrade.CheckResult' + type: object + pkg_api_v1.upgradeRequest: + description: Request to apply an available upgrade to a workload. All fields + are optional; an empty body applies the upgrade preserving the workload's + existing configuration. + properties: + env: + additionalProperties: + type: string + description: |- + Env holds additional or overriding environment variables to merge into the + upgraded workload's configuration. + type: object + secrets: + description: |- + Secrets holds additional secret parameters (`,target=`) to merge + into the upgraded workload's configuration. Only references are accepted; + no secret values are transmitted in the request. + items: + type: string + type: array + uniqueItems: false + type: object pkg_api_v1.validateSkillRequest: description: Request to validate a skill definition properties: @@ -5258,6 +5436,101 @@ paths: summary: Stop a workload tags: - workloads + /api/v1beta/workloads/{name}/upgrade: + post: + description: |- + Apply a registry-sourced upgrade to a single workload. This + re-resolves and verifies the candidate image, pulls it, and only + then recreates the workload with the new image, preserving the + existing configuration. If the workload is already up to date or + is not registry-sourced, the current check result is returned + unchanged (no-op). Secret values are never accepted or returned. + parameters: + - description: Workload name + in: path + name: name + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + oneOf: + - type: object + - $ref: '#/components/schemas/pkg_api_v1.upgradeRequest' + description: Upgrade options + summary: request + description: Upgrade options + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/pkg_api_v1.upgradeCheckResponse' + description: OK + "400": + content: + application/json: + schema: + type: string + description: Bad Request + "404": + content: + application/json: + schema: + type: string + description: Not Found + "422": + content: + application/json: + schema: + type: string + description: Unprocessable Entity + "500": + content: + application/json: + schema: + type: string + description: Internal Server Error + summary: Apply an available upgrade to a workload + tags: + - workloads + /api/v1beta/workloads/{name}/upgrade-check: + get: + description: |- + Check whether a single workload has a newer image available in + its source registry. This is an offline metadata comparison; it + does not pull images. Secret values are never returned. + parameters: + - description: Workload name + in: path + name: name + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/pkg_api_v1.upgradeCheckResponse' + description: OK + "400": + content: + application/json: + schema: + type: string + description: Bad Request + "404": + content: + application/json: + schema: + type: string + description: Not Found + summary: Check a workload for an available upgrade + tags: + - workloads /api/v1beta/workloads/delete: post: description: |- @@ -5350,6 +5623,46 @@ paths: summary: Stop workloads in bulk tags: - workloads + /api/v1beta/workloads/upgrade-check: + get: + description: |- + Check all workloads (optionally filtered by group) for newer + images available in their source registries. This is an offline + metadata comparison; it does not pull images. Secret values are + never returned. + parameters: + - description: Include stopped workloads + in: query + name: all + schema: + type: boolean + - description: Filter workloads by group name + in: query + name: group + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/pkg_api_v1.upgradeCheckBulkResponse' + description: OK + "400": + content: + application/json: + schema: + type: string + description: Bad Request + "404": + content: + application/json: + schema: + type: string + description: Group not found + summary: Check workloads for available upgrades + tags: + - workloads /health: get: description: Check if the API is healthy