{ "components": { "schemas": { "audit.Config": { "description": "DEPRECATED: Middleware configuration.\nAuditConfig contains the audit logging configuration", "properties": { "component": { "description": "Component is the component name to use in audit events.\n+optional", "type": "string" }, "detectApplicationErrors": { "description": "DetectApplicationErrors controls whether the audit middleware inspects\nJSON-RPC response bodies for application-level errors when the HTTP\nstatus code indicates success (2xx). When enabled, a small prefix of\nthe response body is buffered to detect JSON-RPC error fields,\nindependent of the IncludeResponseData setting.\n+kubebuilder:default=true\n+optional", "type": "boolean" }, "enabled": { "description": "Enabled controls whether audit logging is enabled.\nWhen true, enables audit logging with the configured options.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "eventTypes": { "description": "EventTypes specifies which event types to audit. If empty, all events are audited.\n+optional", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "excludeEventTypes": { "description": "ExcludeEventTypes specifies which event types to exclude from auditing.\nThis takes precedence over EventTypes.\n+optional", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "includeRequestData": { "description": "IncludeRequestData determines whether to include request data in audit logs.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "includeResponseData": { "description": "IncludeResponseData determines whether to include response data in audit logs.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "logFile": { "description": "LogFile specifies the file path for audit logs. If empty, logs to stdout.\n+optional", "type": "string" }, "maxDataSize": { "description": "MaxDataSize limits the size of request/response data included in audit logs (in bytes).\n+kubebuilder:default=1024\n+optional", "type": "integer" }, "maxDelegationDepth": { "description": "MaxDelegationDepth caps how many nested RFC 8693 \"act\" entries are\nrecorded in an audit event's delegation chain. Deeper chains are\ntruncated (marked with truncated=true). Defaults to 10 when unset.\n+kubebuilder:validation:Minimum=1\n+kubebuilder:default=10\n+optional", "type": "integer" } }, "type": "object" }, "auth.TokenValidatorConfig": { "description": "DEPRECATED: Middleware configuration.\nOIDCConfig contains OIDC configuration", "properties": { "allowPrivateIP": { "description": "AllowPrivateIP allows JWKS/OIDC endpoints on private IP addresses", "type": "boolean" }, "audience": { "description": "Audience is the expected audience for the token", "type": "string" }, "authTokenFile": { "description": "AuthTokenFile is the path to file containing bearer token for authentication", "type": "string" }, "cacertPath": { "description": "CACertPath is the path to the CA certificate bundle for HTTPS requests", "type": "string" }, "clientID": { "description": "ClientID is the OIDC client ID", "type": "string" }, "clientSecret": { "description": "ClientSecret is the optional OIDC client secret for introspection", "type": "string" }, "insecureAllowHTTP": { "description": "InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing\nWARNING: This is insecure and should NEVER be used in production", "type": "boolean" }, "introspectionURL": { "description": "IntrospectionURL is the optional introspection endpoint for validating tokens", "type": "string" }, "issuer": { "description": "Issuer is the OIDC issuer URL (e.g., https://accounts.google.com)", "type": "string" }, "jwksurl": { "description": "JWKSURL is the URL to fetch the JWKS from", "type": "string" }, "resourceURL": { "description": "ResourceURL is the explicit resource URL for OAuth discovery (RFC 9728)", "type": "string" }, "scopes": { "description": "Scopes is the list of OAuth scopes to advertise in the well-known endpoint (RFC 9728)\nIf empty, defaults to [\"openid\"]", "items": { "type": "string" }, "type": "array" } }, "type": "object" }, "authserver.CIMDRunConfig": { "description": "CIMD controls client_id metadata document support. When enabled, the\nembedded authorization server accepts HTTPS URLs as client_id values\nand 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.\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.", "example": "5m", "type": "string" }, "cache_max_size": { "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 zero.", "type": "integer" }, "enabled": { "description": "Enabled activates CIMD client lookup when true.", "type": "boolean" } }, "type": "object" }, "authserver.DCRUpstreamConfig": { "description": "DCRConfig enables RFC 7591 Dynamic Client Registration against the\nupstream authorization server. When set, the client credentials are\nobtained at runtime rather than being pre-provisioned via ClientID /\nClientSecretFile / ClientSecretEnvVar, and ClientID must be left empty.\nMutually exclusive with ClientID.", "properties": { "discovery_url": { "description": "DiscoveryURL is the exact RFC 8414 / OIDC Discovery document URL to\nfetch at runtime. The resolver issues a single GET against this URL\n(no well-known-path fallback) and reads registration_endpoint,\nauthorization_endpoint, token_endpoint,\ntoken_endpoint_auth_methods_supported, and scopes_supported from the\nresponse. Per RFC 8414 §3.3, the document's \"issuer\" field must\nexactly match the upstream issuer configured on the parent\nrun-config.\n\nUse this field when the upstream publishes discovery metadata at a\npath that differs from the issuer-derived well-known paths — for\nexample a multi-tenant IdP whose metadata lives at\nhttps://idp.example.com/tenants/acme/.well-known/openid-configuration.\n\nMutually exclusive with RegistrationEndpoint.", "type": "string" }, "initial_access_token_env_var": { "description": "InitialAccessTokenEnvVar is the name of an environment variable\ncontaining the RFC 7591 initial access token. Mutually exclusive with\nInitialAccessTokenFile.", "type": "string" }, "initial_access_token_file": { "description": "InitialAccessTokenFile is the path to a file containing the RFC 7591\ninitial access token presented to the registration endpoint. Mutually\nexclusive with InitialAccessTokenEnvVar. Both may be omitted for open\nregistration endpoints.", "type": "string" }, "registration_endpoint": { "description": "RegistrationEndpoint is the RFC 7591 registration endpoint URL used\ndirectly, bypassing discovery. Because no discovery is performed,\nserver-capability fields (token_endpoint_auth_methods_supported,\nscopes_supported) are unavailable on this code path; the caller is\nexpected to also supply AuthorizationEndpoint, TokenEndpoint, and an\nexplicit Scopes list on the parent OAuth2UpstreamRunConfig. Auth\nmethod falls back to the resolver's default (client_secret_basic).\n\nMutually exclusive with DiscoveryURL.", "type": "string" }, "software_id": { "description": "SoftwareID is the RFC 7591 \"software_id\" registration metadata value,\nidentifying the client software independent of any particular\nregistration instance.", "type": "string" }, "software_statement": { "description": "SoftwareStatement is the RFC 7591 \"software_statement\" JWT asserting\nmetadata about the client software, signed by a party the authorization\nserver trusts.", "type": "string" } }, "type": "object" }, "authserver.DelegateClientRunConfig": { "properties": { "audiences": { "description": "Audiences are the RFC 8707 resource values this client may request a\ntoken for. Required, and must be a subset of RunConfig.AllowedAudiences:\na declared client must not receive every allowed audience just because\nthis was left empty.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "client_id": { "description": "ClientID is the OAuth client_id this client presents at the token endpoint.", "type": "string" }, "client_secret_env_var": { "description": "ClientSecretEnvVar is the name of an environment variable containing\nthe client secret. One of ClientSecretFile or ClientSecretEnvVar is\nrequired.", "type": "string" }, "client_secret_file": { "description": "ClientSecretFile is the path to a file containing the client secret.\nIf both this and ClientSecretEnvVar are set, the file takes precedence.", "type": "string" }, "scopes": { "description": "Scopes are the OAuth scopes this client may request. Required, and\nmust be a subset of RunConfig.ScopesSupported: a declared client must\nnot receive every supported scope just because this was left empty.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "authserver.IdentityFromTokenRunConfig": { "description": "IdentityFromToken extracts user identity (subject, name, email) directly from the\nOAuth2 token-endpoint response body using gjson dot-notation paths. When set, the\nembedded auth server skips the userinfo HTTP call entirely. Mirrors the CRD type\n(cmd/thv-operator/api/v1beta1.IdentityFromTokenConfig) — the authoritative\ntrust-model and uniqueness documentation lives there.", "properties": { "email_path": { "description": "EmailPath is the dot-notation path to the email address field.", "type": "string" }, "name_path": { "description": "NamePath is the dot-notation path to the display name field.", "type": "string" }, "subject_path": { "description": "SubjectPath is the dot-notation path to the subject (user ID) field.\nRequired when IdentityFromToken is set.", "type": "string" } }, "type": "object" }, "authserver.OAuth2UpstreamRunConfig": { "description": "OAuth2Config contains OAuth 2.0-specific configuration.\nRequired when Type is \"oauth2\", must be nil when Type is \"oidc\".", "properties": { "additional_authorization_params": { "additionalProperties": { "type": "string" }, "description": "AdditionalAuthorizationParams are extra query parameters to include in\nauthorization requests. Useful for provider-specific parameters like\nGoogle's access_type=offline.", "type": "object" }, "allow_private_ips": { "description": "AllowPrivateIPs permits the upstream provider's HTTP client to connect to\nprivate IP ranges (RFC-1918, link-local). When DCRConfig is set, this\nalso gates the DCR discovery and registration calls made on this\nupstream's behalf (see pkg/authserver/runner/dcr_adapter.go), so a\nsingle flag covers the whole upstream rather than needing a separate\nDCR-specific setting. Use only when the upstream is hosted inside the\nsame cluster and has no public endpoint. HTTP-scheme restrictions are\nunchanged — HTTPS is still required for non-localhost hosts. Defaults\nto false.", "type": "boolean" }, "authorization_endpoint": { "description": "AuthorizationEndpoint is the URL for the OAuth authorization endpoint.", "type": "string" }, "ca_file_path": { "description": "CAFilePath is the path to a PEM CA bundle added to the system roots.", "type": "string" }, "client_id": { "description": "ClientID is the OAuth 2.0 client identifier registered with the upstream IDP.\nMutually exclusive with DCRConfig: when DCRConfig is set, ClientID is obtained\nat runtime via RFC 7591 Dynamic Client Registration and must be left empty.", "type": "string" }, "client_secret_env_var": { "description": "ClientSecretEnvVar is the name of an environment variable containing the client secret.\nMutually exclusive with ClientSecretFile. Optional for public clients using PKCE.", "type": "string" }, "client_secret_file": { "description": "ClientSecretFile is the path to a file containing the OAuth 2.0 client secret.\nMutually exclusive with ClientSecretEnvVar. Optional for public clients using PKCE.", "type": "string" }, "dcr_config": { "$ref": "#/components/schemas/authserver.DCRUpstreamConfig" }, "identity_from_token": { "$ref": "#/components/schemas/authserver.IdentityFromTokenRunConfig" }, "insecure_allow_http": { "description": "InsecureAllowHTTP permits plain-HTTP authorization and token endpoint URLs\nfor this upstream. Only for in-cluster development environments (e.g. an\nOAuth2 provider served over HTTP in a kind cluster) where TLS is not\navailable. Never set this in production.", "type": "boolean" }, "redirect_uri": { "description": "RedirectURI is the callback URL where the upstream IDP will redirect after authentication.\nWhen not specified, defaults to `{issuer}/oauth/callback`.", "type": "string" }, "scopes": { "description": "Scopes are the OAuth scopes to request from the upstream IDP.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "token_endpoint": { "description": "TokenEndpoint is the URL for the OAuth token endpoint.", "type": "string" }, "token_response_mapping": { "$ref": "#/components/schemas/authserver.TokenResponseMappingRunConfig" }, "userinfo": { "$ref": "#/components/schemas/authserver.UserInfoRunConfig" } }, "type": "object" }, "authserver.OIDCUpstreamRunConfig": { "description": "OIDCConfig contains OIDC-specific configuration.\nRequired when Type is \"oidc\", must be nil when Type is \"oauth2\".", "properties": { "additional_authorization_params": { "additionalProperties": { "type": "string" }, "description": "AdditionalAuthorizationParams are extra query parameters to include in\nauthorization requests. Useful for provider-specific parameters like\nGoogle's access_type=offline.", "type": "object" }, "allow_private_ips": { "description": "AllowPrivateIPs permits the OIDC discovery and token HTTP clients to\nconnect to private IP ranges (RFC-1918, link-local). Use only when the\nupstream is hosted inside the same cluster and has no public endpoint.\nHTTP-scheme restrictions are unchanged — HTTPS is still required for\nnon-localhost hosts. Defaults to false.", "type": "boolean" }, "ca_file_path": { "description": "CAFilePath is the path to a PEM CA bundle added to the system roots.", "type": "string" }, "client_id": { "description": "ClientID is the OAuth 2.0 client identifier registered with the upstream IDP.", "type": "string" }, "client_secret_env_var": { "description": "ClientSecretEnvVar is the name of an environment variable containing the client secret.\nMutually exclusive with ClientSecretFile. Optional for public clients using PKCE.", "type": "string" }, "client_secret_file": { "description": "ClientSecretFile is the path to a file containing the OAuth 2.0 client secret.\nMutually exclusive with ClientSecretEnvVar. Optional for public clients using PKCE.", "type": "string" }, "insecure_allow_http": { "description": "InsecureAllowHTTP permits a plain-HTTP issuer URL and HTTP discovery\nendpoints for this upstream. Only for in-cluster development environments\n(e.g. Dex served over HTTP in a kind cluster) where TLS is not available.\nNever set this in production.", "type": "boolean" }, "issuer_url": { "description": "IssuerURL is the OIDC issuer URL for automatic endpoint discovery.\nMust be a valid HTTPS URL.", "type": "string" }, "redirect_uri": { "description": "RedirectURI is the callback URL where the upstream IDP will redirect after authentication.\nWhen not specified, defaults to `{issuer}/oauth/callback`.", "type": "string" }, "scopes": { "description": "Scopes are the OAuth scopes to request from the upstream IDP.\nIf not specified, defaults to [\"openid\", \"offline_access\"].\nWhen using AdditionalAuthorizationParams with provider-specific refresh\ntoken mechanisms (e.g., Google's access_type=offline), set explicit scopes\nto avoid sending both offline_access and the provider-specific parameter.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "subject_claim": { "description": "SubjectClaim names the validated ID-token claim to use as the upstream\nsubject. Defaults to \"sub\" when empty. Set for IdPs where \"sub\" isn't\nstable per user (e.g. Entra/Azure AD's \"oid\"). See upstream.OIDCConfig.", "type": "string" }, "userinfo_override": { "$ref": "#/components/schemas/authserver.UserInfoRunConfig" } }, "type": "object" }, "authserver.RunConfig": { "description": "EmbeddedAuthServerConfig contains configuration for the embedded OAuth2/OIDC authorization server.\nWhen set, the proxy runner will start an embedded auth server that delegates to upstream IDPs.\nThis is the serializable RunConfig; secrets are referenced by file paths or env var names.", "properties": { "allow_confidential_client_registration": { "description": "AllowConfidentialClientRegistration permits Dynamic Client Registration\nof confidential clients: when true, /oauth/register accepts\ntoken_endpoint_auth_method values client_secret_basic and\nclient_secret_post in addition to \"none\" (still the default on\nomission) and mints a client_secret returned exactly once. Confidential\nclients are restricted to https non-loopback redirect URIs, and\nregistrations idle for more than DefaultDCRClientTTL (30 days) are\nevicted and must re-register. This gates registration only: disabling\nit does not revoke or reject already-minted secrets at the token\nendpoint.\n\nSecurity: /oauth/register is unauthenticated, so this issues client\nsecrets to any caller. Combining it with InsecureAllowHTTP is rejected\nby Validate.", "type": "boolean" }, "allow_private_key_jwt_registration": { "description": "AllowPrivateKeyJWTRegistration permits Dynamic Client Registration of\nclients using private_key_jwt authentication. This is independent of\nAllowConfidentialClientRegistration and defaults to false. Registration\nbehavior is controlled independently by the DCR handler and discovery\nmetadata.\n\nSecurity: /oauth/register is unauthenticated. Unlike\nAllowConfidentialClientRegistration, this is NOT rejected when combined\nwith InsecureAllowHTTP: registration never returns a secret for a\nprivate_key_jwt client, so there is nothing for cleartext HTTP to\nexpose.", "type": "boolean" }, "allowed_audiences": { "description": "AllowedAudiences is the list of valid resource URIs that tokens can be issued for.\nPer RFC 8707, the \"resource\" parameter in authorization and token requests is\nvalidated against this list. Required for MCP compliance.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "authorization_endpoint_base_url": { "description": "AuthorizationEndpointBaseURL overrides the base URL used for the authorization_endpoint\nin the OAuth discovery document. When set, the discovery document will advertise\n`{authorization_endpoint_base_url}/oauth/authorize` instead of `{issuer}/oauth/authorize`.\nAll other endpoints remain derived from the issuer.", "type": "string" }, "baseline_client_scopes": { "description": "BaselineClientScopes is a baseline set of OAuth 2.0 scopes unioned into every\nDCR registration. All values must appear in ScopesSupported; the auth server\nrejects this RunConfig at startup otherwise. Empty means current behavior is\npreserved (registered scope = client-requested, or DefaultScopes if empty).\nWhen ScopesSupported is empty, the subset check uses registration.DefaultScopes\n(the same set applyDefaults would substitute at startup) — so\nBaselineClientScopes containing standard OIDC scopes works without enumerating\nScopesSupported explicitly.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "cimd": { "$ref": "#/components/schemas/authserver.CIMDRunConfig" }, "delegate_clients": { "description": "DelegateClients declares confidential OAuth clients to register at\nauthorization-server startup, including clients intended for RFC 8693\ntoken exchange.\n\nIndependent of AllowConfidentialClientRegistration: declaring a client\nhere does not require or enable self-service confidential DCR, and\nsetting that flag does not declare or enable any client here. They\ngovern different endpoints — this field is static configuration the\noperator controls directly, while the flag is admission policy for the\nunauthenticated /oauth/register endpoint.\n\nSee DelegateClientRunConfig for the per-client field reference.", "items": { "$ref": "#/components/schemas/authserver.DelegateClientRunConfig" }, "type": "array", "uniqueItems": false }, "delegation_token_lifespan": { "description": "DelegationTokenLifespan is the maximum lifetime for delegated tokens issued\nvia RFC 8693 token exchange. Specified as a Go duration string (e.g., \"15m\").\nIf empty, defaults to 15 minutes.", "type": "string" }, "disable_upstream_token_injection": { "description": "DisableUpstreamTokenInjection prevents the upstream swap middleware from being added.\nWhen true, the embedded auth server handles OAuth flows for clients, but instead of\ninjecting upstream IdP tokens the proxy strips the client's credential headers\n(Authorization, Cookie, Proxy-Authorization) after the JWT is validated — the\nbackend receives an unauthenticated request. Incompatible with token exchange\nand AWS STS, which would re-add credentials after the strip.", "type": "boolean" }, "force_confidential_redirect_uris": { "description": "ForceConfidentialRedirectURIs lists redirect URIs that must be registered\nas confidential clients regardless of the token_endpoint_auth_method the\nDCR request declares. A registration whose redirect_uris contains an\nEXACT match for one of these entries is issued a real client_secret and\nreported back as token_endpoint_auth_method \"client_secret_post\", even\nif the request said \"none\" or omitted the field.\n\nThis exists for MCP clients (Perplexity is the known case) that declare\nthemselves public (token_endpoint_auth_method: \"none\") per RFC 7591 but\nthen refuse to proceed because the response carries no client_secret —\na self-contradictory request no conformant server can satisfy as\nwritten. RFC 7591 §3.2.1 permits the server to substitute metadata, so\nthis takes such a client at its word that it wants a secret.\n\nExact matching is deliberate: it is not a way to obtain a usable\ncredential for another client. An attacker who registers with someone\nelse's callback URI is issued a secret for a client whose authorization\ncodes are delivered to that someone else's redirect endpoint, not to\nthe attacker — the secret is useless without also controlling the\ncallback.\n\nRequires AllowConfidentialClientRegistration; every entry must be a\nvalid https non-loopback URI (Validate rejects loopback entries — the\nsame restriction AllowConfidentialClientRegistration itself enforces\nexists so secrets do not land in distributed native apps, and this\noverride must not bypass it). Remove an entry once the client is fixed\nto handle \"none\" registrations correctly.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "hmac_secret_files": { "description": "HMACSecretFiles contains file paths to HMAC secrets for signing authorization codes\nand refresh tokens (opaque tokens).\nFirst file is the current secret (must be at least 32 bytes), subsequent files\nare for rotation/verification of existing tokens.\nIf empty, an ephemeral secret will be auto-generated (development only).", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "insecure_allow_confidential_over_loopback_http": { "description": "InsecureAllowConfidentialOverLoopbackHTTP opts in to confidential clients\nwhen Issuer is a plain-HTTP loopback URL. Without this flag, that\ncombination is rejected: a loopback http:// issuer is normally fine for\nlocal development (the traffic never leaves the machine), but client\nsecrets would otherwise travel over cleartext. Defaults to false. Has no\neffect when there are no confidential clients or Issuer is https.\n\nApplies identically to delegate clients and DCR-registered clients. The\nKubernetes CRD requires the explicit opt-in for a delegate client with an\nHTTP issuer; the shared transport validator enforces that its host is\nloopback — see EmbeddedAuthServerConfig's doc comment.\n\nprivate_key_jwt registration has no equivalent flag or transport\nrestriction: unlike confidential registration, it never returns a\nclient_secret (or any other secret) in the DCR response, so there is\nnothing here for cleartext HTTP to expose.", "type": "boolean" }, "insecure_allow_http": { "description": "InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.\nOnly set this for in-cluster Kubernetes deployments on a trusted network.\nProduction deployments reachable outside the cluster MUST use https://.", "type": "boolean" }, "issuer": { "description": "Issuer is the issuer identifier for this authorization server.\nThis will be included in the \"iss\" claim of issued tokens.\nMust be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.", "type": "string" }, "schema_version": { "description": "SchemaVersion is the version of the RunConfig schema.", "type": "string" }, "scopes_supported": { "description": "ScopesSupported lists the OAuth 2.0 scope values advertised in discovery documents.\nIf empty, defaults to registration.DefaultScopes ([\"openid\", \"profile\", \"email\", \"offline_access\"]).", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "signing_key_config": { "$ref": "#/components/schemas/authserver.SigningKeyRunConfig" }, "storage": { "$ref": "#/components/schemas/storage.RunConfig" }, "token_lifespans": { "$ref": "#/components/schemas/authserver.TokenLifespanRunConfig" }, "trusted_issuers": { "description": "TrustedIssuers lists external OIDC issuers whose tokens are accepted as\nRFC 8693 subject tokens or RFC 7523 JWT-bearer assertions. Issuers with\njwtBearerGrant enabled may be used for the JWT-bearer grant without an\nRFC 8693 delegation policy. Empty (the default) means only self-issued\nsubject tokens are accepted.\n\nSee tokenexchange.TrustedIssuer for the per-issuer field reference, and\ndocs/arch/17-token-exchange-delegation.md for the trust model, consent\nsignals, and operator-facing constraints (audience/scope bounding,\nsubject namespace qualification, required client binding) that aren't\nvisible from the config shape alone.", "items": { "$ref": "#/components/schemas/tokenexchange.TrustedIssuer" }, "type": "array", "uniqueItems": false }, "upstreams": { "description": "Upstreams configures connections to upstream Identity Providers.\nAt least one upstream is required - the server delegates authentication to these providers.\nMultiple upstreams are supported for sequential authorization chains.", "items": { "$ref": "#/components/schemas/authserver.UpstreamRunConfig" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "authserver.SigningKeyRunConfig": { "description": "SigningKeyConfig configures the signing key provider for JWT operations.\nIf nil or empty, an ephemeral signing key will be auto-generated (development only).", "properties": { "fallback_key_files": { "description": "FallbackKeyFiles are filenames of additional keys for verification (relative to KeyDir).\nThese keys are included in the JWKS endpoint for token verification but are NOT\nused for signing new tokens. Useful for key rotation.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "key_dir": { "description": "KeyDir is the directory containing PEM-encoded private key files.\nAll key filenames are relative to this directory.\nIn Kubernetes, this is typically a mounted Secret volume.", "type": "string" }, "signing_key_file": { "description": "SigningKeyFile is the filename of the primary signing key (relative to KeyDir).\nThis key is used for signing new tokens.", "type": "string" } }, "type": "object" }, "authserver.TokenLifespanRunConfig": { "description": "TokenLifespans configures the duration that various tokens are valid.\nIf nil, defaults are applied (access: 1h, refresh: 7d, authCode: 10m).", "properties": { "access_token_lifespan": { "description": "AccessTokenLifespan is the duration that access tokens are valid.\nIf empty, defaults to 1 hour.", "type": "string" }, "auth_code_lifespan": { "description": "AuthCodeLifespan is the duration that authorization codes are valid.\nIf empty, defaults to 10 minutes.", "type": "string" }, "refresh_token_lifespan": { "description": "RefreshTokenLifespan is the duration that refresh tokens are valid.\nIf empty, defaults to 7 days (168h).", "type": "string" } }, "type": "object" }, "authserver.TokenResponseMappingRunConfig": { "description": "TokenResponseMapping configures custom field extraction from non-standard token responses.\nWhen set, the token exchange bypasses golang.org/x/oauth2 and extracts fields using\nthe configured dot-notation paths.", "properties": { "access_token_path": { "description": "AccessTokenPath is the dot-notation path to the access token (required).", "type": "string" }, "expires_in_path": { "description": "ExpiresInPath is the dot-notation path to the expires_in value. Defaults to \"expires_in\".", "type": "string" }, "refresh_token_path": { "description": "RefreshTokenPath is the dot-notation path to the refresh token. Defaults to \"refresh_token\".", "type": "string" }, "scope_path": { "description": "ScopePath is the dot-notation path to the scope. Defaults to \"scope\".", "type": "string" } }, "type": "object" }, "authserver.UpstreamRunConfig": { "properties": { "name": { "description": "Name uniquely identifies this upstream.\nUsed for routing decisions and session binding in multi-upstream scenarios.\nIf empty when only one upstream is configured, defaults to \"default\".", "type": "string" }, "oauth2_config": { "$ref": "#/components/schemas/authserver.OAuth2UpstreamRunConfig" }, "oidc_config": { "$ref": "#/components/schemas/authserver.OIDCUpstreamRunConfig" }, "type": { "description": "Type specifies the provider type: \"oidc\" or \"oauth2\".", "type": "string" } }, "type": "object" }, "authserver.UserInfoFieldMappingRunConfig": { "description": "FieldMapping contains custom field mapping configuration for non-standard providers.\nIf nil, standard OIDC field names are used (\"sub\", \"name\", \"email\").", "properties": { "email_fields": { "description": "EmailFields is an ordered list of field names to try for the email address.\nThe first non-empty value found will be used.\nDefault: [\"email\"]", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "name_fields": { "description": "NameFields is an ordered list of field names to try for the display name.\nThe first non-empty value found will be used.\nDefault: [\"name\"]", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "subject_fields": { "description": "SubjectFields is an ordered list of field names to try for the user ID.\nThe first non-empty value found will be used.\nDefault: [\"sub\"]", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "authserver.UserInfoRunConfig": { "description": "UserInfo contains configuration for fetching user information.\nOptional: when nil, the upstream OAuth2 provider derives a deterministic\nsubject by SHA-256-hashing the access token (with a \"tk-\" prefix) instead\nof calling a userinfo endpoint. OIDC providers always derive Subject from\nthe ID token and are unaffected.", "properties": { "additional_headers": { "additionalProperties": { "type": "string" }, "description": "AdditionalHeaders contains extra headers to include in the userinfo request.\nUseful for providers that require specific headers (e.g., GitHub's Accept header).", "type": "object" }, "endpoint_url": { "description": "EndpointURL is the URL of the userinfo endpoint.", "type": "string" }, "field_mapping": { "$ref": "#/components/schemas/authserver.UserInfoFieldMappingRunConfig" }, "http_method": { "description": "HTTPMethod is the HTTP method to use for the userinfo request.\nIf not specified, defaults to GET.", "type": "string" } }, "type": "object" }, "core.Workload": { "properties": { "created_at": { "description": "CreatedAt is the timestamp when the workload was created.", "type": "string" }, "group": { "description": "Group is the name of the group this workload belongs to, if any.", "type": "string" }, "labels": { "additionalProperties": { "type": "string" }, "description": "Labels are the container labels (excluding standard ToolHive labels)", "type": "object" }, "name": { "description": "Name is the name of the workload.\nIt is used as a unique identifier.", "type": "string" }, "package": { "description": "Package specifies the Workload Package used to create this Workload.", "type": "string" }, "port": { "description": "Port is the port on which the workload is exposed.\nThis is embedded in the URL.", "type": "integer" }, "proxy_mode": { "description": "ProxyMode is the proxy mode that clients should use to connect.\nFor stdio transports, this will be the proxy mode (sse or streamable-http).\nFor direct transports (sse/streamable-http), this will be the same as TransportType.", "type": "string" }, "remote": { "description": "Remote indicates whether this is a remote workload (true) or a container workload (false).", "type": "boolean" }, "started_at": { "description": "StartedAt is when the container was last started (changes on restart)", "type": "string" }, "status": { "description": "Status is the current status of the workload.", "enum": [ "running", "stopped", "error", "starting", "stopping", "unhealthy", "removing", "unknown", "unauthenticated", "auth_retrying", "policy_stopped" ], "type": "string" }, "status_context": { "description": "StatusContext provides additional context about the workload's status.\nThe exact meaning is determined by the status and the underlying runtime.", "type": "string" }, "tools": { "description": "ToolsFilter is the filter on tools applied to the workload.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "transport_type": { "description": "TransportType is the type of transport used for this workload.", "enum": ["stdio", "sse", "streamable-http", "inspector"], "type": "string" }, "url": { "description": "URL is the URL of the workload exposed by the ToolHive proxy.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_auth_awssts.Config": { "description": "AWSStsConfig contains AWS STS token exchange configuration for accessing AWS services", "properties": { "fallback_role_arn": { "description": "FallbackRoleArn is the IAM role ARN to assume when no role mapping matches.", "type": "string" }, "region": { "description": "Region is the AWS region for STS and SigV4 signing.", "type": "string" }, "role_claim": { "description": "RoleClaim is the JWT claim to use for role mapping (default: \"groups\").", "type": "string" }, "role_mappings": { "description": "RoleMappings maps JWT claim values to IAM roles with priority.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_auth_awssts.RoleMapping" }, "type": "array", "uniqueItems": false }, "service": { "description": "Service is the AWS service name for SigV4 signing (default: \"aws-mcp\").", "type": "string" }, "session_duration": { "description": "SessionDuration is the duration in seconds for assumed role credentials (default: 3600).", "type": "integer" }, "session_name_claim": { "description": "SessionNameClaim is the JWT claim to use for role session name (default: \"sub\").", "type": "string" }, "subject_provider_name": { "description": "SubjectProviderName identifies which upstream provider's access token to use\nfor STS AssumeRoleWithWebIdentity. Used by vMCP only. When empty, the bearer\ntoken from the incoming HTTP request is used.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_auth_awssts.RoleMapping": { "properties": { "claim": { "description": "Claim is the simple claim value to match (e.g., group name).\nInternally compiles to a CEL expression: \"\u003cclaim_value\u003e\" in claims[\"\u003crole_claim\u003e\"]\nMutually exclusive with Matcher.", "type": "string" }, "matcher": { "description": "Matcher is a CEL expression for complex matching against JWT claims.\nThe expression has access to a \"claims\" variable containing all JWT claims.\nExamples:\n - \"admins\" in claims[\"groups\"]\n - claims[\"sub\"] == \"user123\" \u0026\u0026 !(\"act\" in claims)\nMutually exclusive with Claim.", "type": "string" }, "priority": { "description": "Priority determines selection order (lower number = higher priority).\nWhen multiple mappings match, the one with the lowest priority is selected.\nWhen nil (omitted), the mapping has the lowest possible priority, and\nconfiguration order acts as tie-breaker via stable sort.", "type": "integer" }, "role_arn": { "description": "RoleArn is the IAM role ARN to assume when this mapping matches.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_auth_upstreamswap.Config": { "description": "UpstreamSwapConfig contains configuration for upstream token swap middleware.\nWhen set along with EmbeddedAuthServerConfig, this middleware exchanges ToolHive JWTs\nfor upstream IdP tokens before forwarding requests to the MCP server.", "properties": { "custom_header_name": { "description": "CustomHeaderName is the header name when HeaderStrategy is \"custom\".", "type": "string" }, "header_strategy": { "description": "HeaderStrategy determines how to inject the token: \"replace\" (default) or \"custom\".", "type": "string" }, "provider_name": { "description": "ProviderName identifies which upstream provider's tokens to retrieve for injection.\nThis is required and must match a configured upstream provider name.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_authz.Config": { "description": "DEPRECATED: Middleware configuration.\nAuthzConfig contains the authorization configuration", "properties": { "type": { "description": "Type is the type of authorization configuration (e.g., \"cedarv1\").", "type": "string" }, "version": { "description": "Version is the version of the configuration format.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_client.ClientApp": { "description": "ClientType is the type of MCP client", "enum": [ "roo-code", "cline", "cursor", "vscode-insider", "vscode", "claude-code", "windsurf", "windsurf-jetbrains", "amp-cli", "lm-studio", "goose", "trae", "continue", "opencode", "kiro", "antigravity", "zed", "gemini-cli", "vscode-server", "mistral-vibe", "codex", "kimi-cli", "factory", "copilot-cli", "qoder" ], "type": "string", "x-enum-varnames": [ "RooCode", "Cline", "Cursor", "VSCodeInsider", "VSCode", "ClaudeCode", "Windsurf", "WindsurfJetBrains", "AmpCli", "LMStudio", "Goose", "Trae", "Continue", "OpenCode", "Kiro", "Antigravity", "Zed", "GeminiCli", "VSCodeServer", "MistralVibe", "Codex", "KimiCli", "Factory", "CopilotCli", "Qoder" ] }, "github_com_stacklok_toolhive_pkg_client.ClientAppStatus": { "properties": { "client_type": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientApp" }, "installed": { "description": "Installed indicates whether the client is installed on the system", "type": "boolean" }, "registered": { "description": "Registered indicates whether the client is registered in the ToolHive configuration", "type": "boolean" }, "supports_plugins": { "description": "SupportsPlugins indicates whether ToolHive can install plugins for this client", "type": "boolean" }, "supports_skills": { "description": "SupportsSkills indicates whether ToolHive can install skills for this client", "type": "boolean" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_client.RegisteredClient": { "properties": { "groups": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "name": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientApp" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_groups.Group": { "properties": { "name": { "type": "string" }, "plugins": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "registered_clients": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "skills": { "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.BuildResult": { "properties": { "reference": { "description": "Reference is the OCI reference of the built skill artifact.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.ComponentInventory": { "additionalProperties": { "type": "integer" }, "description": "Components is the inventory of component types declared by the plugin\n(e.g. {\"commands\": 3, \"skills\": 2}). Extracted from the OCI artifact.", "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.ComponentType": { "enum": [ "commands", "agents", "skills", "hooks", "mcpServers", "lspServers" ], "type": "string", "x-enum-varnames": [ "ComponentCommands", "ComponentAgents", "ComponentSkills", "ComponentHooks", "ComponentMCP", "ComponentLSP" ] }, "github_com_stacklok_toolhive_pkg_plugins.Dependency": { "properties": { "digest": { "description": "Digest is the OCI digest for upgrade detection.", "type": "string" }, "name": { "description": "Name is the dependency name.", "type": "string" }, "reference": { "description": "Reference is the OCI reference for the dependency.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.InstallStatus": { "description": "Status is the current installation status.", "enum": ["installed", "pending", "failed"], "type": "string", "x-enum-varnames": [ "InstallStatusInstalled", "InstallStatusPending", "InstallStatusFailed" ] }, "github_com_stacklok_toolhive_pkg_plugins.InstalledPlugin": { "description": "InstalledPlugin contains the full installation record.", "properties": { "clients": { "description": "Clients is the list of client identifiers the plugin is installed for.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "components": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.ComponentInventory" }, "dependencies": { "description": "Dependencies is the list of external plugin dependencies.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.Dependency" }, "type": "array", "uniqueItems": false }, "digest": { "description": "Digest is the OCI digest (sha256:...) for upgrade detection.", "type": "string" }, "installed_at": { "description": "InstalledAt is the timestamp when the plugin was installed.", "type": "string" }, "managed": { "description": "Managed indicates this install is tracked in the project's\ntoolhive.lock.yaml plugins: key. Only ever true for project-scoped\ninstalls. No omitempty: false is an observable state (unmanaged),\nnot an absence.", "type": "boolean" }, "metadata": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.PluginMetadata" }, "project_root": { "description": "ProjectRoot is the project root path for project-scoped plugins. Empty for user-scoped.", "type": "string" }, "reference": { "description": "Reference is the full OCI reference (e.g. ghcr.io/org/plugin:v1).", "type": "string" }, "scope": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.Scope" }, "signature": { "description": "Signature is the optional signing signature for the plugin artifact.", "type": "string" }, "status": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.InstallStatus" }, "tag": { "description": "Tag is the OCI tag (e.g. v1.0.0).", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.LocalBuild": { "properties": { "description": { "description": "Description is the skill description extracted from the artifact metadata, if available.", "type": "string" }, "digest": { "description": "Digest is the OCI digest of the artifact (sha256:...).", "type": "string" }, "name": { "description": "Name is the skill name extracted from the artifact metadata, if available.", "type": "string" }, "tag": { "description": "Tag is the OCI tag or name used to reference the artifact.", "type": "string" }, "version": { "description": "Version is the skill version extracted from the artifact metadata, if available.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.PluginContent": { "properties": { "description": { "description": "Description is the plugin description from the OCI config labels.", "type": "string" }, "files": { "description": "Files is the list of all files in the artifact with their sizes.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.PluginFileEntry" }, "type": "array", "uniqueItems": false }, "license": { "description": "License is the SPDX license identifier from the OCI config labels.", "type": "string" }, "manifest": { "description": "Manifest is the raw .claude-plugin/plugin.json body.", "type": "string" }, "name": { "description": "Name is the plugin name from the OCI config labels.", "type": "string" }, "version": { "description": "Version is the plugin version from the OCI config labels.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.PluginFileEntry": { "properties": { "path": { "description": "Path is the file path within the artifact.", "type": "string" }, "size": { "description": "Size is the uncompressed file size in bytes.", "type": "integer" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.PluginInfo": { "properties": { "installed_plugin": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.InstalledPlugin" }, "metadata": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.PluginMetadata" }, "project_scope_degraded_clients": { "description": "ProjectScopeDegradedClients lists the client types for which a\nproject-scoped install degraded (the adapter could only materialize at\nuser scope — e.g. Codex always writes to the user-scoped config.toml).\nPopulated by Info; empty for user-scoped installs. Recomputed at read\ntime from the stored scope + each adapter's capability, mirroring the\nUnmaterializedComponents pattern (no persistence needed — the degradation\nis deterministic from scope + client type).", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "unmaterialized_components": { "additionalProperties": { "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.ComponentType" }, "type": "array" }, "description": "UnmaterializedComponents lists, per client type, the component types the\nplugin declares that the installed client adapter does NOT load. Populated\nby Info by diffing InstalledPlugin.Components against each installed\nclient adapter's SupportedComponents.", "type": "object" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.PluginMetadata": { "description": "Metadata contains the plugin's metadata.", "properties": { "author": { "description": "Author is the plugin author or maintainer.", "type": "string" }, "description": { "description": "Description is a human-readable description of the plugin.", "type": "string" }, "keywords": { "description": "Keywords is a list of keywords for categorization/search.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "license": { "description": "License is the SPDX license identifier for the plugin.", "type": "string" }, "name": { "description": "Name is the unique name of the plugin (kebab-case).", "type": "string" }, "version": { "description": "Version is the semantic version of the plugin.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.Scope": { "description": "Scope for the installation", "enum": ["user", "project"], "type": "string", "x-enum-varnames": ["ScopeUser", "ScopeProject"] }, "github_com_stacklok_toolhive_pkg_plugins.SyncResult": { "properties": { "already_current": { "description": "AlreadyCurrent lists skills that already matched the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "drifted": { "description": "Drifted lists skills whose on-disk contentDigest differed from the lock\nfile. Normally these are reinstalled to match it; when Check is set,\nnothing is written and this field reports the drift only.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "failed": { "description": "Failed lists skills that could not be synced, with the reason for each.\nDrift alone is never reported here — see Drifted.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SyncFailure" }, "type": "array", "uniqueItems": false }, "installed": { "description": "Installed lists skills that were installed or reinstalled to match the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "missing": { "description": "Missing lists lock entries with no corresponding install record at all\n— the fresh-clone state. Normally these are installed at their pinned\nreference; when Check is set, nothing is written and this field\nreports the gap only.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "never_managed": { "description": "NeverManaged lists project-scoped skills never recorded as lock-managed.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "pruned": { "description": "Pruned lists removed-from-lock skills that were uninstalled because Prune was set.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "removed_from_lock": { "description": "RemovedFromLock lists previously managed skills absent from the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.UpgradeResult": { "properties": { "outcomes": { "description": "Outcomes contains one entry per skill considered for upgrade.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.UpgradeOutcome" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_plugins.ValidationResult": { "properties": { "errors": { "description": "Errors is a list of validation errors, if any.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "valid": { "description": "Valid indicates whether the skill definition is valid.", "type": "boolean" }, "warnings": { "description": "Warnings is a list of non-blocking validation warnings, if any.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_registry.OAuthPublicConfig": { "description": "AuthConfig contains the non-secret OAuth configuration when auth is configured.\nNil when auth_status is \"none\".", "properties": { "audience": { "type": "string" }, "client_id": { "type": "string" }, "issuer": { "type": "string" }, "scopes": { "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_runner.HeaderForwardConfig": { "description": "HeaderForward contains configuration for injecting headers into requests to remote servers.", "properties": { "add_headers_from_secret": { "additionalProperties": { "type": "string" }, "description": "AddHeadersFromSecret is a map of header names to secret names.\nThe key is the header name, the value is the secret name in ToolHive's secrets manager.\nResolved at runtime via WithSecrets() into resolvedHeaders.\nThe actual secret value is only held in memory, never persisted.", "type": "object" }, "add_plaintext_headers": { "additionalProperties": { "type": "string" }, "description": "AddPlaintextHeaders is a map of header names to literal values to inject into requests.\nWARNING: These values are stored in plaintext in the configuration.\nFor sensitive values (API keys, tokens), use AddHeadersFromSecret instead.", "type": "object" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_runner.RunConfig": { "properties": { "additional_middleware_configs": { "description": "AdditionalMiddlewareConfigs carries pre-built middleware configs injected by\nexternal-auth handlers (reached via *[]RunConfigBuilderOption) rather than\nderived from typed RunConfig fields. PopulateMiddlewareConfigs splices these\ninto the chain in the backend-egress group — after auth and before recovery —\ninstead of discarding them. Upstream carries these configs verbatim and never\ninspects their parameters; the middleware type identity (e.g. an enterprise\nauth type) is supplied by the caller via types.MiddlewareConfig.Type.\n\nEach entry's Type is expected to be a NEW egress middleware type (e.g. OBO),\nnot one already produced from a typed RunConfig field (auth, authz, audit,\ntokenExchange, awssts, …). Dispatch in the proxyrunner is purely by Type\nstring, so an injected Type that shadows a typed-field type would add a\nsecond instance of that middleware to the chain; the seam does not validate\nagainst this.", "items": { "$ref": "#/components/schemas/types.MiddlewareConfig" }, "type": "array", "uniqueItems": false }, "allow_docker_gateway": { "description": "AllowDockerGateway permits outbound connections to Docker gateway addresses\n(host.docker.internal, gateway.docker.internal, 172.17.0.1). These are\nblocked by default in the egress proxy even when InsecureAllowAll is set.\nOnly applicable to Docker deployments with network isolation enabled.\nGateway access is port-independent: it ignores the permission profile's\nallowed ports, so once enabled the gateway is reachable on any port.", "type": "boolean" }, "allowed_origins": { "description": "AllowedOrigins is the allowlist of values accepted on the HTTP Origin header,\nused for DNS-rebinding protection per MCP 2025-11-25 §\"Security Warning\".\nWhen empty and Host is loopback (127.0.0.1 / localhost / [::1]), a default\nloopback-only allowlist is derived at middleware-wiring time.\nWhen empty and Host is non-loopback, the middleware is disabled — operators\nexposing the proxy publicly must configure an explicit allowlist.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "audit_config": { "$ref": "#/components/schemas/audit.Config" }, "audit_config_path": { "description": "DEPRECATED: Middleware configuration.\nAuditConfigPath is the path to the audit configuration file", "type": "string" }, "authz_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_authz.Config" }, "authz_config_path": { "description": "DEPRECATED: Middleware configuration.\nAuthzConfigPath is the path to the authorization configuration file", "type": "string" }, "aws_sts_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_auth_awssts.Config" }, "base_name": { "description": "BaseName is the base name used for the container (without prefixes)", "type": "string" }, "cmd_args": { "description": "CmdArgs are the arguments to pass to the container", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "container_labels": { "additionalProperties": { "type": "string" }, "description": "ContainerLabels are the labels to apply to the container", "type": "object" }, "container_name": { "description": "ContainerName is the name of the container", "type": "string" }, "debug": { "description": "Debug indicates whether debug mode is enabled", "type": "boolean" }, "embedded_auth_server_config": { "$ref": "#/components/schemas/authserver.RunConfig" }, "endpoint_prefix": { "description": "EndpointPrefix is an explicit prefix to prepend to SSE endpoint URLs.\nThis is used to handle path-based ingress routing scenarios.", "type": "string" }, "env_file_dir": { "description": "DEPRECATED: No longer appears to be used.\nEnvFileDir is the directory path to load environment files from", "type": "string" }, "env_vars": { "additionalProperties": { "type": "string" }, "description": "EnvVars are the parsed environment variables as key-value pairs", "type": "object" }, "group": { "description": "Group is the name of the group this workload belongs to, if any", "type": "string" }, "header_forward": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_runner.HeaderForwardConfig" }, "host": { "description": "Host is the host for the HTTP proxy", "type": "string" }, "ignore_config": { "$ref": "#/components/schemas/ignore.Config" }, "image": { "description": "Image is the Docker image to run", "type": "string" }, "isolate_network": { "description": "IsolateNetwork indicates whether to isolate the network for the container", "type": "boolean" }, "jwks_auth_token_file": { "description": "DEPRECATED: No longer appears to be used.\nJWKSAuthTokenFile is the path to file containing auth token for JWKS/OIDC requests", "type": "string" }, "k8s_pod_template_patch": { "description": "K8sPodTemplatePatch is a JSON string to patch the Kubernetes pod template\nOnly applicable when using Kubernetes runtime", "type": "string" }, "mcpserver_generation": { "description": "MCPServerGeneration is the K8s .metadata.generation of the MCPServer CR that rendered\nthis RunConfig. The Kubernetes runtime uses it as a monotonic version to prevent stale\nrolling-update pods from overwriting a newer RunConfig's StatefulSet apply. Zero value\nmeans unversioned (backward-compat with older operators, or non-operator callers).", "type": "integer" }, "middleware_configs": { "description": "MiddlewareConfigs contains the list of middleware to apply to the transport\nand the configuration for each middleware.", "items": { "$ref": "#/components/schemas/types.MiddlewareConfig" }, "type": "array", "uniqueItems": false }, "mutating_webhooks": { "description": "MutatingWebhooks contains the configuration for mutating webhook middleware.\nMutating webhooks run before validating webhooks, per RFC THV-0017 ordering.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_webhook.Config" }, "type": "array", "uniqueItems": false }, "name": { "description": "Name is the name of the MCP server", "type": "string" }, "oidc_config": { "$ref": "#/components/schemas/auth.TokenValidatorConfig" }, "permission_profile_name_or_path": { "description": "PermissionProfileNameOrPath is the name or path of the permission profile", "type": "string" }, "port": { "description": "Port is the port for the HTTP proxy to listen on (host port)", "type": "integer" }, "proxy_mode": { "description": "ProxyMode is the effective HTTP protocol the proxy uses.\nFor stdio transports, this is the configured mode (sse or streamable-http).\nFor direct transports (sse/streamable-http), this matches the transport type.\nNote: \"sse\" is deprecated; use \"streamable-http\" instead.", "enum": ["sse", "streamable-http"], "type": "string" }, "publish": { "description": "Publish lists ports to publish to the host in format \"hostPort:containerPort\"", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "rate_limit_config": { "$ref": "#/components/schemas/v1beta1.RateLimitConfig" }, "rate_limit_namespace": { "description": "RateLimitNamespace is the Kubernetes namespace for Redis key derivation.", "type": "string" }, "registry_api_url": { "description": "RegistryAPIURL is the registry API URL that served this server's metadata.\nEmpty when the server was not discovered via registry lookup.", "type": "string" }, "registry_server_name": { "description": "RegistryServerName is the registry entry name used to look up this server's metadata.\nEmpty when the server was not discovered via registry lookup.", "type": "string" }, "registry_url": { "description": "RegistryURL is the registry URL that served this server's metadata.\nEmpty when the server was not discovered via registry lookup.", "type": "string" }, "remote_auth_config": { "$ref": "#/components/schemas/remote.Config" }, "remote_url": { "description": "RemoteURL is the URL of the remote MCP server (if running remotely)", "type": "string" }, "runtime_config": { "$ref": "#/components/schemas/templates.RuntimeConfig" }, "scaling_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_runner.ScalingConfig" }, "schema_version": { "description": "SchemaVersion is the version of the RunConfig schema", "type": "string" }, "secrets": { "description": "Secrets are the secret parameters to pass to the container\nFormat: \"\u003csecret name\u003e,target=\u003ctarget environment variable\u003e\"", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "session_ttl": { "description": "SessionTTL is the inactivity timeout for proxy sessions, expressed as a Go\nduration string (e.g. \"30m\", \"2h\", \"168h\"). Empty uses the transport\ndefault (2h). Negative durations and values that fail time.ParseDuration\nare rejected at runtime.\nString (not time.Duration) keeps the wire format unit-explicit: a\ntime.Duration field serializes as nanoseconds in JSON.", "example": "2h", "type": "string" }, "stateless": { "description": "Stateless indicates the server only supports POST (no SSE/GET).\nWhen true, the proxy returns 405 for incoming GET requests and uses a\nPOST-based health check instead of the default GET probe.\nApplies to both remote URLs and local container workloads.", "type": "boolean" }, "strict_protocol_validation": { "description": "StrictProtocolValidation enables strict MCP-Protocol-Version validation\non the streamable HTTP proxy: a request whose header names an unknown\nMCP revision is rejected with HTTP 400. Default false accepts any\nversion string (an absent header is always accepted in either mode).", "type": "boolean" }, "target_host": { "description": "TargetHost is the host to forward traffic to (only applicable to SSE transport)", "type": "string" }, "target_port": { "description": "TargetPort is the port for the container to expose (only applicable to SSE transport)", "type": "integer" }, "telemetry_config": { "$ref": "#/components/schemas/telemetry.Config" }, "thv_ca_bundle": { "description": "DEPRECATED: No longer appears to be used.\nThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations", "type": "string" }, "token_exchange_config": { "$ref": "#/components/schemas/tokenexchange.Config" }, "tools_filter": { "description": "DEPRECATED: Middleware configuration.\nToolsFilter is the list of tools to filter", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "tools_override": { "additionalProperties": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_runner.ToolOverride" }, "description": "DEPRECATED: Middleware configuration.\nToolsOverride is a map from an actual tool to its overridden name and/or description", "type": "object" }, "transport": { "description": "Transport is the transport mode (stdio, sse, or streamable-http)", "enum": ["stdio", "sse", "streamable-http", "inspector"], "type": "string" }, "trust_proxy_headers": { "description": "TrustProxyHeaders indicates whether to trust X-Forwarded-* headers from reverse proxies", "type": "boolean" }, "upstream_swap_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_auth_upstreamswap.Config" }, "validating_webhooks": { "description": "ValidatingWebhooks contains the configuration for validating webhook middleware.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_webhook.Config" }, "type": "array", "uniqueItems": false }, "volumes": { "description": "Volumes are the directory mounts to pass to the container\nFormat: \"host-path:container-path[:ro]\"", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_runner.ScalingConfig": { "description": "ScalingConfig contains configuration for horizontal scaling of the proxy runner.\nOnly applicable when running in Kubernetes with the ToolHive operator.\nWhen nil, no scaling configuration is applied (single-replica default behavior).", "properties": { "backend_replicas": { "description": "BackendReplicas is the desired StatefulSet replica count for the proxy runner backend.\nWhen nil, replicas are unmanaged (preserving HPA or manual kubectl control).\nWhen set (including 0), the value is an explicit replica count.", "type": "integer" }, "session_redis": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_runner.SessionRedisConfig" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_runner.SessionRedisConfig": { "description": "SessionRedis holds non-sensitive Redis connection parameters for distributed session storage.\nPopulated only when MCPServer.spec.sessionStorage.provider == \"redis\".\nThe Redis password is not included — it is injected as env var THV_SESSION_REDIS_PASSWORD.\n+optional", "properties": { "address": { "description": "Address is the Redis server address (host:port).", "type": "string" }, "db": { "description": "DB is the Redis database number.", "type": "integer" }, "key_prefix": { "description": "KeyPrefix is an optional prefix applied to all Redis keys used by ToolHive.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_runner.ToolOverride": { "properties": { "description": { "description": "Description is the redefined description of the tool", "type": "string" }, "name": { "description": "Name is the redefined name of the tool", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.BuildResult": { "properties": { "reference": { "description": "Reference is the OCI reference of the built skill artifact.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.Dependency": { "properties": { "digest": { "description": "Digest is the OCI digest for upgrade detection.", "type": "string" }, "name": { "description": "Name is the dependency name.", "type": "string" }, "reference": { "description": "Reference is the OCI reference for the dependency.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.FailureReason": { "description": "Reason is a typed failure reason when Status is UpgradeStatusFailed.", "enum": [ "registry-unreachable", "digest-missing", "validation-rejected", "lock-write-failed", "signature-invalid", "signer-mismatch", "provenance-field-mismatch", "unsigned-rejected", "unknown" ], "type": "string", "x-enum-varnames": [ "FailureReasonRegistryUnreachable", "FailureReasonDigestMissing", "FailureReasonValidationRejected", "FailureReasonLockWriteFailed", "FailureReasonSignatureInvalid", "FailureReasonSignerMismatch", "FailureReasonProvenanceFieldMismatch", "FailureReasonUnsignedRejected", "FailureReasonUnknown" ] }, "github_com_stacklok_toolhive_pkg_skills.InstallStatus": { "description": "Status is the current installation status.", "enum": ["installed", "pending", "failed"], "type": "string", "x-enum-varnames": [ "InstallStatusInstalled", "InstallStatusPending", "InstallStatusFailed" ] }, "github_com_stacklok_toolhive_pkg_skills.InstalledSkill": { "description": "InstalledSkill contains the full installation record.", "properties": { "clients": { "description": "Clients is the list of client identifiers the skill is installed for.\nTODO: Refactor client.ClientApp to a shared package so it can be used here instead of []string.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "dependencies": { "description": "Dependencies is the list of external skill dependencies.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.Dependency" }, "type": "array", "uniqueItems": false }, "digest": { "description": "Digest is the OCI digest (sha256:...) for upgrade detection.", "type": "string" }, "installed_at": { "description": "InstalledAt is the timestamp when the skill was installed.", "type": "string" }, "managed": { "description": "Managed indicates this install is tracked in the project's\ntoolhive.lock.yaml. Only ever true for project-scoped installs. No\nomitempty: false is an observable state (unmanaged), not an absence.", "type": "boolean" }, "metadata": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SkillMetadata" }, "project_root": { "description": "ProjectRoot is the project root path for project-scoped skills. Empty for user-scoped.", "type": "string" }, "reference": { "description": "Reference is the full OCI reference (e.g. ghcr.io/org/skill:v1).", "type": "string" }, "scope": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.Scope" }, "status": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.InstallStatus" }, "tag": { "description": "Tag is the OCI tag (e.g. v1.0.0).", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.LocalBuild": { "properties": { "description": { "description": "Description is the skill description extracted from the artifact metadata, if available.", "type": "string" }, "digest": { "description": "Digest is the OCI digest of the artifact (sha256:...).", "type": "string" }, "name": { "description": "Name is the skill name extracted from the artifact metadata, if available.", "type": "string" }, "tag": { "description": "Tag is the OCI tag or name used to reference the artifact.", "type": "string" }, "version": { "description": "Version is the skill version extracted from the artifact metadata, if available.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.ProvenanceInfo": { "description": "Provenance is the signer identity the project's lock file records\nfor this skill, when project-scoped and lock-managed.", "properties": { "cert_issuer": { "description": "CertIssuer is the OIDC issuer that authenticated the signer.", "type": "string" }, "provisional": { "description": "Provisional marks provenance with a documented verification gap\n(git signatures until transparency-log validation lands).", "type": "boolean" }, "repository_ref": { "description": "RepositoryRef is the git ref the signing workflow ran on, from Fulcio\ncertificate extension 1.3.6.1.4.1.57264.1.14. Empty means\nunconstrained, matching lock files written before the field existed.", "type": "string" }, "repository_uri": { "description": "RepositoryURI is the source repository from the certificate\nextensions, when present.", "type": "string" }, "runner_environment": { "description": "RunnerEnvironment is the runner class the signing workflow executed in\n(e.g. \"github-hosted\"), from Fulcio certificate extension\n1.3.6.1.4.1.57264.1.11. Empty means unconstrained.", "type": "string" }, "signer_identity": { "description": "SignerIdentity is the certificate subject identity (workflow path for\nGitHub Actions certificates, SAN verbatim otherwise).", "type": "string" }, "sigstore_url": { "description": "SigstoreURL is the Sigstore instance the signature chains to.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.Scope": { "description": "Scope for the installation", "enum": ["user", "project"], "type": "string", "x-enum-varnames": ["ScopeUser", "ScopeProject"] }, "github_com_stacklok_toolhive_pkg_skills.SkillContent": { "properties": { "body": { "description": "Body is the raw SKILL.md markdown content.", "type": "string" }, "description": { "description": "Description is the skill description from the OCI config labels.", "type": "string" }, "files": { "description": "Files is the list of all files in the artifact with their sizes.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SkillFileEntry" }, "type": "array", "uniqueItems": false }, "license": { "description": "License is the SPDX license identifier from the OCI config labels.", "type": "string" }, "name": { "description": "Name is the skill name from the OCI config labels.", "type": "string" }, "version": { "description": "Version is the skill version from the OCI config labels.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.SkillFileEntry": { "properties": { "path": { "description": "Path is the file path within the artifact.", "type": "string" }, "size": { "description": "Size is the uncompressed file size in bytes.", "type": "integer" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.SkillInfo": { "properties": { "installed_skill": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.InstalledSkill" }, "metadata": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SkillMetadata" }, "provenance": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.ProvenanceInfo" }, "unsigned": { "description": "Unsigned reports that the lock file records an explicit unsigned\nexception for this skill.", "type": "boolean" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.SkillMetadata": { "description": "Metadata contains the skill's metadata.", "properties": { "author": { "description": "Author is the skill author or maintainer.", "type": "string" }, "description": { "description": "Description is a human-readable description of the skill.", "type": "string" }, "name": { "description": "Name is the unique name of the skill.", "type": "string" }, "tags": { "description": "Tags is a list of tags for categorization.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "version": { "description": "Version is the semantic version of the skill.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.SyncFailure": { "properties": { "error": { "description": "Error is a human-readable description of the failure.", "type": "string" }, "name": { "description": "Name is the skill name that failed.", "type": "string" }, "reason": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.FailureReason" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.SyncResult": { "properties": { "already_current": { "description": "AlreadyCurrent lists skills that already matched the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "drifted": { "description": "Drifted lists skills whose on-disk contentDigest differed from the lock\nfile. Normally these are reinstalled to match it; when Check is set,\nnothing is written and this field reports the drift only.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "failed": { "description": "Failed lists skills that could not be synced, with the reason for each.\nDrift alone is never reported here — see Drifted.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SyncFailure" }, "type": "array", "uniqueItems": false }, "installed": { "description": "Installed lists skills that were installed or reinstalled to match the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "missing": { "description": "Missing lists lock entries with no corresponding install record at all\n— the fresh-clone state. Normally these are installed at their pinned\nreference; when Check is set, nothing is written and this field\nreports the gap only.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "never_managed": { "description": "NeverManaged lists project-scoped skills never recorded as lock-managed.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "pruned": { "description": "Pruned lists removed-from-lock skills that were uninstalled because Prune was set.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "removed_from_lock": { "description": "RemovedFromLock lists previously managed skills absent from the lock file.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.UpgradeOutcome": { "properties": { "error": { "description": "Error is a human-readable description of the failure, set only when Status is UpgradeStatusFailed.", "type": "string" }, "name": { "description": "Name is the skill name.", "type": "string" }, "new_digest": { "description": "NewDigest is the digest the source currently resolves to. Equal to\nOldDigest when Status is UpgradeStatusUpToDate.", "type": "string" }, "new_resolved_reference": { "description": "NewResolvedReference is the new resolvedReference when it changed.", "type": "string" }, "new_signer_identity": { "description": "NewSignerIdentity is the candidate's signer identity when it differs\nfrom the recorded one (empty when the candidate is unsigned).", "type": "string" }, "old_digest": { "description": "OldDigest is the digest pinned in the lock file before this operation.", "type": "string" }, "reason": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.FailureReason" }, "status": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.UpgradeStatus" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.UpgradeResult": { "properties": { "outcomes": { "description": "Outcomes contains one entry per skill considered for upgrade.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.UpgradeOutcome" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_skills.UpgradeStatus": { "description": "Status is the outcome of the upgrade attempt.", "enum": [ "upgraded", "up-to-date", "not-upgradable", "ref-change-blocked", "signer-change-blocked", "failed" ], "type": "string", "x-enum-varnames": [ "UpgradeStatusUpgraded", "UpgradeStatusUpToDate", "UpgradeStatusNotUpgradable", "UpgradeStatusRefChangeBlocked", "UpgradeStatusSignerChangeBlocked", "UpgradeStatusFailed" ] }, "github_com_stacklok_toolhive_pkg_skills.ValidationResult": { "properties": { "errors": { "description": "Errors is a list of validation errors, if any.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "valid": { "description": "Valid indicates whether the skill definition is valid.", "type": "boolean" }, "warnings": { "description": "Warnings is a list of non-blocking validation warnings, if any.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_webhook.Config": { "properties": { "failure_policy": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_webhook.FailurePolicy" }, "hmac_secret_ref": { "description": "HMACSecretRef is an optional reference to an HMAC secret for payload signing.", "type": "string" }, "name": { "description": "Name is a unique identifier for this webhook.", "type": "string" }, "timeout": { "description": "Timeout is the maximum time to wait for a webhook response.", "type": "integer" }, "tls_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_webhook.TLSConfig" }, "url": { "description": "URL is the HTTPS endpoint to call.", "type": "string" } }, "type": "object" }, "github_com_stacklok_toolhive_pkg_webhook.FailurePolicy": { "description": "FailurePolicy determines behavior when the webhook call fails.", "enum": ["fail", "ignore"], "type": "string", "x-enum-varnames": ["FailurePolicyFail", "FailurePolicyIgnore"] }, "github_com_stacklok_toolhive_pkg_webhook.TLSConfig": { "description": "TLSConfig holds optional TLS configuration (CA bundles, client certs).", "properties": { "ca_bundle_path": { "description": "CABundlePath is the path to a CA certificate bundle for server verification.", "type": "string" }, "client_cert_path": { "description": "ClientCertPath is the path to a client certificate for mTLS.", "type": "string" }, "client_key_path": { "description": "ClientKeyPath is the path to a client key for mTLS.", "type": "string" }, "insecure_skip_verify": { "description": "InsecureSkipVerify disables server certificate verification.\nWARNING: 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\nmetadata (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.\nEmpty 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)\nbetween 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\ndeclares that differ from the workload's current configuration.", "properties": { "added": { "description": "Added lists environment variables the candidate declares that the\nworkload 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\ncandidate no longer declares. Populated on a best-effort basis; may be\nempty 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)\nwhenever Secret is true: a secret env var's default could carry sensitive\ndata, and surfacing it in a drift report (which may be logged or returned\nover 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\nfrom 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" ] }, "ignore.Config": { "description": "IgnoreConfig contains configuration for ignore processing", "properties": { "loadGlobal": { "description": "Whether to load global ignore patterns", "type": "boolean" }, "printOverlays": { "description": "Whether to print resolved overlay paths for debugging", "type": "boolean" } }, "type": "object" }, "model.Argument": { "properties": { "choices": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "default": { "type": "string" }, "description": { "type": "string" }, "format": { "$ref": "#/components/schemas/model.Format" }, "isRepeated": { "type": "boolean" }, "isRequired": { "type": "boolean" }, "isSecret": { "type": "boolean" }, "name": { "example": "--port", "type": "string" }, "placeholder": { "type": "string" }, "type": { "$ref": "#/components/schemas/model.ArgumentType" }, "value": { "type": "string" }, "valueHint": { "example": "file_path", "type": "string" }, "variables": { "additionalProperties": { "$ref": "#/components/schemas/model.Input" }, "type": "object" } }, "type": "object" }, "model.ArgumentType": { "enum": ["positional", "named"], "example": "positional", "type": "string", "x-enum-varnames": ["ArgumentTypePositional", "ArgumentTypeNamed"] }, "model.Format": { "enum": ["string", "number", "boolean", "filepath"], "type": "string", "x-enum-varnames": [ "FormatString", "FormatNumber", "FormatBoolean", "FormatFilePath" ] }, "model.Icon": { "properties": { "mimeType": { "example": "image/png", "type": "string" }, "sizes": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "src": { "example": "https://example.com/icon.png", "format": "uri", "maxLength": 255, "type": "string" }, "theme": { "type": "string" } }, "type": "object" }, "model.Input": { "properties": { "choices": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "default": { "type": "string" }, "description": { "type": "string" }, "format": { "$ref": "#/components/schemas/model.Format" }, "isRequired": { "type": "boolean" }, "isSecret": { "type": "boolean" }, "placeholder": { "type": "string" }, "value": { "type": "string" } }, "type": "object" }, "model.KeyValueInput": { "properties": { "choices": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "default": { "type": "string" }, "description": { "type": "string" }, "format": { "$ref": "#/components/schemas/model.Format" }, "isRequired": { "type": "boolean" }, "isSecret": { "type": "boolean" }, "name": { "example": "SOME_VARIABLE", "type": "string" }, "placeholder": { "type": "string" }, "value": { "type": "string" }, "variables": { "additionalProperties": { "$ref": "#/components/schemas/model.Input" }, "type": "object" } }, "type": "object" }, "model.Package": { "properties": { "environmentVariables": { "description": "EnvironmentVariables are set when running the package", "items": { "$ref": "#/components/schemas/model.KeyValueInput" }, "type": "array", "uniqueItems": false }, "fileSha256": { "description": "FileSHA256 is the SHA-256 hash for integrity verification (required for mcpb, optional for others)", "example": "fe333e598595000ae021bd27117db32ec69af6987f507ba7a63c90638ff633ce", "pattern": "^[a-f0-9]{64}$", "type": "string" }, "identifier": { "description": "Identifier is the package identifier:\n - For NPM/PyPI/NuGet: package name or ID\n - For OCI: full image reference (e.g., \"ghcr.io/owner/repo:v1.0.0\")\n - For MCPB: direct download URL", "example": "@modelcontextprotocol/server-brave-search", "minLength": 1, "type": "string" }, "packageArguments": { "description": "PackageArguments are passed to the package's binary", "items": { "$ref": "#/components/schemas/model.Argument" }, "type": "array", "uniqueItems": false }, "registryBaseUrl": { "description": "RegistryBaseURL is the base URL of the package registry (used by npm, pypi, nuget, cargo; not used by oci, mcpb)", "example": "https://registry.npmjs.org", "format": "uri", "type": "string" }, "registryType": { "description": "RegistryType indicates how to download packages (e.g., \"npm\", \"pypi\", \"cargo\", \"oci\", \"nuget\", \"mcpb\")", "example": "npm", "minLength": 1, "type": "string" }, "runtimeArguments": { "description": "RuntimeArguments are passed to the package's runtime command (e.g., docker, npx)", "items": { "$ref": "#/components/schemas/model.Argument" }, "type": "array", "uniqueItems": false }, "runtimeHint": { "description": "RunTimeHint suggests the appropriate runtime for the package", "example": "npx", "type": "string" }, "transport": { "$ref": "#/components/schemas/model.Transport" }, "version": { "description": "Version is the package version (required for npm, pypi, nuget; optional for mcpb; not used by oci where version is in the identifier)", "example": "1.0.2", "maxLength": 255, "minLength": 1, "type": "string" } }, "type": "object" }, "model.Repository": { "properties": { "id": { "example": "b94b5f7e-c7c6-d760-2c78-a5e9b8a5b8c9", "type": "string" }, "source": { "example": "github", "type": "string" }, "subfolder": { "example": "src/everything", "type": "string" }, "url": { "example": "https://github.com/modelcontextprotocol/servers", "format": "uri", "type": "string" } }, "type": "object" }, "model.Transport": { "description": "Transport is required and specifies the transport protocol configuration", "properties": { "headers": { "items": { "$ref": "#/components/schemas/model.KeyValueInput" }, "type": "array", "uniqueItems": false }, "type": { "example": "stdio", "type": "string" }, "url": { "example": "https://api.example.com/mcp", "type": "string" }, "variables": { "additionalProperties": { "$ref": "#/components/schemas/model.Input" }, "type": "object" } }, "type": "object" }, "permissions.InboundNetworkPermissions": { "description": "Inbound defines inbound network permissions", "properties": { "allow_host": { "description": "AllowHost is a list of allowed hosts for inbound connections", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "permissions.NetworkPermissions": { "description": "Network defines network permissions", "properties": { "inbound": { "$ref": "#/components/schemas/permissions.InboundNetworkPermissions" }, "mode": { "description": "Mode specifies the network mode for the container (e.g., \"host\", \"bridge\", \"none\")\nWhen empty, the default container runtime network mode is used", "type": "string" }, "outbound": { "$ref": "#/components/schemas/permissions.OutboundNetworkPermissions" } }, "type": "object" }, "permissions.OutboundNetworkPermissions": { "description": "Outbound defines outbound network permissions", "properties": { "allow_host": { "description": "AllowHost is a list of allowed hosts", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "allow_port": { "description": "AllowPort is a list of allowed ports", "items": { "type": "integer" }, "type": "array", "uniqueItems": false }, "insecure_allow_all": { "description": "InsecureAllowAll allows all outbound network connections", "type": "boolean" } }, "type": "object" }, "permissions.Profile": { "description": "Permission profile to apply", "properties": { "name": { "description": "Name is the name of the profile", "type": "string" }, "network": { "$ref": "#/components/schemas/permissions.NetworkPermissions" }, "privileged": { "description": "Privileged indicates whether the container should run in privileged mode\nWhen true, the container has access to all host devices and capabilities\nUse with extreme caution as this removes most security isolation", "type": "boolean" }, "read": { "description": "Read is a list of mount declarations that the container can read from\nThese can be in the following formats:\n- A single path: The same path will be mounted from host to container\n- host-path:container-path: Different paths for host and container\n- resource-uri:container-path: Mount a resource identified by URI to a container path", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "write": { "description": "Write is a list of mount declarations that the container can write to\nThese follow the same format as Read mounts but with write permissions", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.RegistryType": { "description": "Type of registry (file, url, or default)", "enum": ["file", "url", "api", "default"], "type": "string", "x-enum-varnames": [ "RegistryTypeFile", "RegistryTypeURL", "RegistryTypeAPI", "RegistryTypeDefault" ] }, "pkg_api_v1.UpdateRegistryAuthRequest": { "description": "OAuth authentication configuration (optional)", "properties": { "audience": { "description": "OAuth audience (optional)", "type": "string" }, "client_id": { "description": "OAuth client ID", "type": "string" }, "issuer": { "description": "OIDC issuer URL", "type": "string" }, "scopes": { "description": "OAuth scopes (optional)", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.UpdateRegistryRequest": { "description": "Request containing registry configuration updates", "properties": { "allow_private_ip": { "description": "Allow private IP addresses for registry URL or API URL", "type": "boolean" }, "api_url": { "description": "MCP Registry API URL", "type": "string" }, "auth": { "$ref": "#/components/schemas/pkg_api_v1.UpdateRegistryAuthRequest" }, "local_path": { "description": "Local registry file path", "type": "string" }, "url": { "description": "Registry URL (for remote registries)", "type": "string" } }, "type": "object" }, "pkg_api_v1.UpdateRegistryResponse": { "description": "Response containing update result", "properties": { "type": { "description": "Registry type after update", "type": "string" } }, "type": "object" }, "pkg_api_v1.buildListResponse": { "description": "Response containing a list of locally-built OCI skill artifacts", "properties": { "builds": { "description": "List of locally-built OCI skill artifacts", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.LocalBuild" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.buildPluginRequest": { "description": "Request to build a plugin from a local directory", "properties": { "path": { "description": "Path to the plugin definition directory", "type": "string" }, "tag": { "description": "OCI tag for the built artifact", "type": "string" } }, "type": "object" }, "pkg_api_v1.buildSkillRequest": { "description": "Request to build a skill from a local directory", "properties": { "path": { "description": "Path to the skill definition directory", "type": "string" }, "tag": { "description": "OCI tag for the built artifact", "type": "string" } }, "type": "object" }, "pkg_api_v1.bulkClientRequest": { "properties": { "groups": { "description": "Groups is the list of groups configured on the client.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "names": { "description": "Names is the list of client names to operate on.", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientApp" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.bulkOperationRequest": { "properties": { "group": { "description": "Group name to operate on (mutually exclusive with names)", "type": "string" }, "names": { "description": "Names of the workloads to operate on", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.clientStatusResponse": { "properties": { "clients": { "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientAppStatus" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.createClientRequest": { "properties": { "groups": { "description": "Groups is the list of groups configured on the client.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "name": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientApp" } }, "type": "object" }, "pkg_api_v1.createClientResponse": { "properties": { "groups": { "description": "Groups is the list of groups configured on the client.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "name": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.ClientApp" } }, "type": "object" }, "pkg_api_v1.createGroupRequest": { "properties": { "name": { "description": "Name of the group to create", "type": "string" } }, "type": "object" }, "pkg_api_v1.createGroupResponse": { "properties": { "name": { "description": "Name of the created group", "type": "string" } }, "type": "object" }, "pkg_api_v1.createRequest": { "description": "Request to create a new workload", "properties": { "allow_docker_gateway": { "description": "Whether to permit outbound connections to Docker gateway addresses\n(host.docker.internal, gateway.docker.internal, 172.17.0.1). These are\nblocked by default in the egress proxy even when network isolation is on.\nOnly applicable to Docker deployments with network isolation enabled.", "type": "boolean" }, "authz_config": { "description": "Authorization configuration", "type": "string" }, "cmd_arguments": { "description": "Command arguments to pass to the container", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "env_vars": { "additionalProperties": { "type": "string" }, "description": "Environment variables to set in the container", "type": "object" }, "group": { "description": "Group name this workload belongs to", "type": "string" }, "header_forward": { "$ref": "#/components/schemas/pkg_api_v1.headerForwardConfig" }, "headers": { "items": { "$ref": "#/components/schemas/registry.Header" }, "type": "array", "uniqueItems": false }, "host": { "description": "Host to bind to", "type": "string" }, "image": { "description": "Docker image to use", "type": "string" }, "name": { "description": "Name of the workload", "type": "string" }, "network_isolation": { "description": "Whether network isolation is turned on. This applies the rules in the permission profile.\nPointer so that omitting the field defaults to network isolation ENABLED (matching the\n`thv run` CLI default); set it explicitly to false to disable network isolation.\nThis also applies on update: a request that omits this field enables isolation, so\nclients that build update requests from scratch should send it explicitly to avoid\nunintentionally turning isolation on for a workload that had it off.", "type": "boolean" }, "oauth_config": { "$ref": "#/components/schemas/pkg_api_v1.remoteOAuthConfig" }, "oidc": { "$ref": "#/components/schemas/pkg_api_v1.oidcOptions" }, "permission_profile": { "$ref": "#/components/schemas/permissions.Profile" }, "proxy_mode": { "description": "Proxy mode to use", "type": "string" }, "proxy_port": { "description": "Port for the HTTP proxy to listen on", "type": "integer" }, "registry": { "description": "Registry is the optional registry name to resolve the server from (e.g. \"default\").", "type": "string" }, "runtime_config": { "$ref": "#/components/schemas/templates.RuntimeConfig" }, "secrets": { "description": "Secret parameters to inject", "items": { "$ref": "#/components/schemas/secrets.SecretParameter" }, "type": "array", "uniqueItems": false }, "server": { "description": "Server is the optional server name in the registry (e.g. \"io.github.stacklok/fetch\").\nWhen both Registry and Server are set, thv resolves the server metadata\nserver-side, filling in image, transport, env vars, permissions, etc.\nUser-provided fields always override registry defaults.", "type": "string" }, "target_port": { "description": "Port to expose from the container", "type": "integer" }, "tools": { "description": "Tools filter", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "tools_override": { "additionalProperties": { "$ref": "#/components/schemas/pkg_api_v1.toolOverride" }, "description": "Tools override", "type": "object" }, "transport": { "description": "Transport configuration", "type": "string" }, "trust_proxy_headers": { "description": "Whether to trust X-Forwarded-* headers from reverse proxies", "type": "boolean" }, "url": { "description": "Remote server specific fields", "type": "string" }, "volumes": { "description": "Volume mounts", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.createSecretRequest": { "description": "Request to create a new secret", "properties": { "key": { "description": "Secret key name", "type": "string" }, "value": { "description": "Secret value", "type": "string" } }, "type": "object" }, "pkg_api_v1.createSecretResponse": { "description": "Response after creating a secret", "properties": { "key": { "description": "Secret key that was created", "type": "string" }, "message": { "description": "Success message", "type": "string" } }, "type": "object" }, "pkg_api_v1.createWorkloadResponse": { "description": "Response after successfully creating a workload", "properties": { "name": { "description": "Name of the created workload", "type": "string" }, "port": { "description": "Port the workload is listening on", "type": "integer" } }, "type": "object" }, "pkg_api_v1.getRegistryResponse": { "description": "Response containing registry details", "properties": { "auth_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_registry.OAuthPublicConfig" }, "auth_status": { "description": "AuthStatus is one of: \"none\", \"configured\", \"authenticated\".\nIntentionally omits omitempty — see registryInfo for rationale.", "type": "string" }, "auth_type": { "description": "AuthType is \"oauth\", \"bearer\" (future), or empty string when no auth.\nIntentionally omits omitempty — see registryInfo for rationale.", "type": "string" }, "last_updated": { "description": "Last updated timestamp", "type": "string" }, "name": { "description": "Name of the registry", "type": "string" }, "registry": { "$ref": "#/components/schemas/registry.Registry" }, "server_count": { "description": "Number of servers in the registry", "type": "integer" }, "source": { "description": "Source of the registry (URL, file path, or empty string for built-in)", "type": "string" }, "type": { "$ref": "#/components/schemas/pkg_api_v1.RegistryType" }, "version": { "description": "Version of the registry schema", "type": "string" } }, "type": "object" }, "pkg_api_v1.getSecretsProviderResponse": { "description": "Response containing secrets provider details", "properties": { "capabilities": { "$ref": "#/components/schemas/pkg_api_v1.providerCapabilitiesResponse" }, "name": { "description": "Name of the secrets provider", "type": "string" }, "provider_type": { "description": "Type of the secrets provider", "type": "string" } }, "type": "object" }, "pkg_api_v1.getServerResponse": { "description": "Response containing server details", "properties": { "is_remote": { "description": "Indicates if this is a remote server", "type": "boolean" }, "remote_server": { "$ref": "#/components/schemas/registry.RemoteServerMetadata" }, "server": { "$ref": "#/components/schemas/registry.ImageMetadata" } }, "type": "object" }, "pkg_api_v1.groupListResponse": { "properties": { "groups": { "description": "List of groups", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_groups.Group" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.headerForwardConfig": { "description": "HeaderForward configures headers to inject into requests to remote MCP servers.\nUse this to add custom headers like X-Tenant-ID or correlation IDs.", "properties": { "add_headers_from_secret": { "additionalProperties": { "type": "string" }, "description": "AddHeadersFromSecret maps header names to secret names in ToolHive's secrets manager.\nKey: HTTP header name, Value: secret name in the secrets manager", "type": "object" }, "add_plaintext_headers": { "additionalProperties": { "type": "string" }, "description": "AddPlaintextHeaders contains literal header values to inject.\nWARNING: These values are stored and transmitted in plaintext.\nUse AddHeadersFromSecret for sensitive data like API keys.", "type": "object" } }, "type": "object" }, "pkg_api_v1.installPluginRequest": { "description": "Request to install a plugin", "properties": { "allow_unsigned": { "description": "AllowUnsigned permits installing a project-scoped plugin without a\nverified signature; the exception is recorded in the project's lock\nfile.", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers (e.g., \"claude-code\"),\nor [\"all\"] to target every plugin-supporting client.\nOmitting this field installs to all available clients.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "force": { "description": "Force allows overwriting unmanaged plugin directories", "type": "boolean" }, "group": { "description": "Group is the group name to add the plugin to after installation", "type": "string" }, "name": { "description": "Name or OCI reference of the plugin to install", "type": "string" }, "project_root": { "description": "ProjectRoot is the project root path for project-scoped installs", "type": "string" }, "scope": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.Scope" }, "version": { "description": "Version to install (empty means latest)", "type": "string" } }, "type": "object" }, "pkg_api_v1.installPluginResponse": { "description": "Response after successfully installing a plugin", "properties": { "plugin": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.InstalledPlugin" } }, "type": "object" }, "pkg_api_v1.installSkillRequest": { "description": "Request to install a skill", "properties": { "allow_unsigned": { "description": "AllowUnsigned permits installing a project-scoped skill without a\nverified signature; the exception is recorded in the project's lock\nfile.", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers (e.g., \"claude-code\"),\nor [\"all\"] to target every skill-supporting client.\nOmitting this field installs to all available clients.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "force": { "description": "Force allows overwriting unmanaged skill directories", "type": "boolean" }, "group": { "description": "Group is the group name to add the skill to after installation", "type": "string" }, "name": { "description": "Name or OCI reference of the skill to install", "type": "string" }, "project_root": { "description": "ProjectRoot is the project root path for project-scoped installs", "type": "string" }, "scope": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.Scope" }, "version": { "description": "Version to install (empty means latest)", "type": "string" } }, "type": "object" }, "pkg_api_v1.installSkillResponse": { "description": "Response after successfully installing a skill", "properties": { "provenance": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.ProvenanceInfo" }, "skill": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.InstalledSkill" }, "unsigned": { "description": "Whether the install was recorded as an explicit unsigned exception.", "type": "boolean" } }, "type": "object" }, "pkg_api_v1.listSecretsResponse": { "description": "Response containing a list of secret keys", "properties": { "keys": { "description": "List of secret keys", "items": { "$ref": "#/components/schemas/pkg_api_v1.secretKeyResponse" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.listServersResponse": { "description": "Response containing a list of servers", "properties": { "remote_servers": { "description": "List of remote servers in the registry (if any)", "items": { "$ref": "#/components/schemas/registry.RemoteServerMetadata" }, "type": "array", "uniqueItems": false }, "servers": { "description": "List of container servers in the registry", "items": { "$ref": "#/components/schemas/registry.ImageMetadata" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.oidcOptions": { "description": "OIDC configuration options", "properties": { "audience": { "description": "Expected audience", "type": "string" }, "client_id": { "description": "OAuth2 client ID", "type": "string" }, "client_secret": { "description": "OAuth2 client secret", "type": "string" }, "introspection_url": { "description": "Token introspection URL for OIDC", "type": "string" }, "issuer": { "description": "OIDC issuer URL", "type": "string" }, "jwks_url": { "description": "JWKS URL for key verification", "type": "string" }, "scopes": { "description": "OAuth scopes to advertise in well-known endpoint (RFC 9728)", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.paginationV01Metadata": { "description": "Metadata contains pagination information", "properties": { "limit": { "description": "Limit is the maximum number of items per page", "type": "integer" }, "page": { "description": "Page is the current page number (1-based)", "type": "integer" }, "total": { "description": "Total is the total number of items matching the query", "type": "integer" } }, "type": "object" }, "pkg_api_v1.pluginBuildListResponse": { "description": "Response containing a list of locally-built OCI plugin artifacts", "properties": { "builds": { "description": "List of locally-built OCI plugin artifacts", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.LocalBuild" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.pluginListResponse": { "description": "Response containing a list of installed plugins", "properties": { "plugins": { "description": "List of installed plugins", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.InstalledPlugin" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.pluginsV01Response": { "description": "Paginated list of plugins from the registry", "properties": { "metadata": { "$ref": "#/components/schemas/pkg_api_v1.paginationV01Metadata" }, "plugins": { "description": "Plugins is the list of plugins on the current page", "items": { "$ref": "#/components/schemas/registry.Plugin" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.providerCapabilitiesResponse": { "description": "Capabilities of the secrets provider", "properties": { "can_cleanup": { "description": "Whether the provider can cleanup all secrets", "type": "boolean" }, "can_delete": { "description": "Whether the provider can delete secrets", "type": "boolean" }, "can_list": { "description": "Whether the provider can list secrets", "type": "boolean" }, "can_read": { "description": "Whether the provider can read secrets", "type": "boolean" }, "can_write": { "description": "Whether the provider can write secrets", "type": "boolean" } }, "type": "object" }, "pkg_api_v1.pushPluginRequest": { "description": "Request to push a built plugin artifact", "properties": { "reference": { "description": "OCI reference to push", "type": "string" } }, "type": "object" }, "pkg_api_v1.pushSkillRequest": { "description": "Request to push a built skill artifact", "properties": { "identity_token": { "description": "IdentityToken is a short-lived OIDC identity token used for keyless\nsigning, mutually exclusive with Key", "type": "string" }, "key": { "description": "Key is the path to a cosign private key used to sign the pushed\nartifact", "type": "string" }, "no_sign": { "description": "NoSign pushes without signing", "type": "boolean" }, "reference": { "description": "OCI reference to push", "type": "string" } }, "type": "object" }, "pkg_api_v1.registryErrorResponse": { "description": "Structured error response returned by registry endpoints", "properties": { "code": { "description": "Code is a machine-readable error code (e.g. \"not_found\", \"registry_auth_required\")", "type": "string" }, "message": { "description": "Message is a human-readable description of the error", "type": "string" } }, "type": "object" }, "pkg_api_v1.registryInfo": { "description": "Basic information about a registry", "properties": { "auth_config": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_registry.OAuthPublicConfig" }, "auth_status": { "description": "AuthStatus is one of: \"none\", \"configured\", \"authenticated\".\nIntentionally omits omitempty so clients always receive the field,\neven when the value is \"none\" (the zero-value equivalent).", "type": "string" }, "auth_type": { "description": "AuthType is \"oauth\", \"bearer\" (future), or empty string when no auth.\nIntentionally omits omitempty so clients can distinguish \"no auth\nconfigured\" (empty string) from \"field missing\" without extra logic.", "type": "string" }, "last_updated": { "description": "Last updated timestamp", "type": "string" }, "name": { "description": "Name of the registry", "type": "string" }, "server_count": { "description": "Number of servers in the registry", "type": "integer" }, "source": { "description": "Source of the registry (URL, file path, or empty string for built-in)", "type": "string" }, "type": { "$ref": "#/components/schemas/pkg_api_v1.RegistryType" }, "version": { "description": "Version of the registry schema", "type": "string" } }, "type": "object" }, "pkg_api_v1.registryListResponse": { "description": "Response containing a list of registries", "properties": { "registries": { "description": "List of registries", "items": { "$ref": "#/components/schemas/pkg_api_v1.registryInfo" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.remoteOAuthConfig": { "description": "OAuth configuration for remote server authentication", "properties": { "authorize_url": { "description": "OAuth authorization endpoint URL (alternative to issuer for non-OIDC OAuth)", "type": "string" }, "bearer_token": { "$ref": "#/components/schemas/secrets.SecretParameter" }, "callback_port": { "description": "Specific port for OAuth callback server", "type": "integer" }, "client_id": { "description": "OAuth client ID for authentication", "type": "string" }, "client_secret": { "$ref": "#/components/schemas/secrets.SecretParameter" }, "issuer": { "description": "OAuth/OIDC issuer URL (e.g., https://accounts.google.com)", "type": "string" }, "oauth_params": { "additionalProperties": { "type": "string" }, "description": "Additional OAuth parameters for server-specific customization", "type": "object" }, "resource": { "description": "OAuth 2.0 resource indicator (RFC 8707)", "type": "string" }, "scopes": { "description": "OAuth scopes to request", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "skip_browser": { "description": "Whether to skip opening browser for OAuth flow (defaults to false)", "type": "boolean" }, "token_url": { "description": "OAuth token endpoint URL (alternative to issuer for non-OIDC OAuth)", "type": "string" }, "use_pkce": { "description": "Whether to use PKCE for the OAuth flow", "type": "boolean" } }, "type": "object" }, "pkg_api_v1.secretKeyResponse": { "description": "Secret key information", "properties": { "description": { "description": "Optional description of the secret", "type": "string" }, "key": { "description": "Secret key name", "type": "string" } }, "type": "object" }, "pkg_api_v1.serversV01Response": { "description": "Paginated list of servers from the registry", "properties": { "metadata": { "$ref": "#/components/schemas/pkg_api_v1.paginationV01Metadata" }, "servers": { "description": "Servers is the list of servers on the current page", "items": { "$ref": "#/components/schemas/v0.ServerJSON" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.setupSecretsRequest": { "description": "Request to setup a secrets provider", "properties": { "password": { "description": "Password for encrypted provider (optional, can be set via environment variable)\nTODO Review environment variable for this", "type": "string" }, "provider_type": { "description": "Type of the secrets provider (encrypted, 1password, environment)", "type": "string" } }, "type": "object" }, "pkg_api_v1.setupSecretsResponse": { "description": "Response after initializing a secrets provider", "properties": { "message": { "description": "Success message", "type": "string" }, "provider_type": { "description": "Type of the secrets provider that was setup", "type": "string" } }, "type": "object" }, "pkg_api_v1.skillListResponse": { "description": "Response containing a list of installed skills", "properties": { "skills": { "description": "List of installed skills", "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.InstalledSkill" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.skillsV01Response": { "description": "Paginated list of skills from the registry", "properties": { "metadata": { "$ref": "#/components/schemas/pkg_api_v1.paginationV01Metadata" }, "skills": { "description": "Skills is the list of skills on the current page", "items": { "$ref": "#/components/schemas/registry.Skill" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.syncPluginsRequest": { "description": "Request to restore a project's installed plugins to match its lock file", "properties": { "adopt": { "description": "Adopt writes lock entries for existing unmanaged project-scope installs", "type": "boolean" }, "allow_unsigned": { "description": "AllowUnsigned permits adopting plugins whose signature state cannot be\nestablished, recording them as unsigned", "type": "boolean" }, "check": { "description": "Check verifies on-disk content against the lock file without installing or writing anything", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers. Empty means every\nplugin-supporting client detected on this host.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "project_root": { "description": "ProjectRoot is the project root path whose lock file should be synced", "type": "string" }, "prune": { "description": "Prune removes project-scoped plugins installed but not present in the lock file", "type": "boolean" } }, "type": "object" }, "pkg_api_v1.syncSkillsRequest": { "description": "Request to restore a project's installed skills to match its lock file", "properties": { "adopt": { "description": "Adopt writes lock entries for existing unmanaged project-scope installs", "type": "boolean" }, "allow_unsigned": { "description": "AllowUnsigned permits adopting skills whose signature state cannot be\nestablished, recording them as unsigned", "type": "boolean" }, "check": { "description": "Check verifies on-disk content against the lock file without installing or writing anything", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers. Empty means every\nskill-supporting client detected on this host.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "project_root": { "description": "ProjectRoot is the project root path whose lock file should be synced", "type": "string" }, "prune": { "description": "Prune removes project-scoped skills installed but not present in the lock file", "type": "boolean" } }, "type": "object" }, "pkg_api_v1.toolOverride": { "description": "Tool override", "properties": { "description": { "description": "Description of the tool", "type": "string" }, "name": { "description": "Name of the tool", "type": "string" } }, "type": "object" }, "pkg_api_v1.updateRequest": { "description": "Request to update an existing workload (name cannot be changed)", "properties": { "allow_docker_gateway": { "description": "Whether to permit outbound connections to Docker gateway addresses\n(host.docker.internal, gateway.docker.internal, 172.17.0.1). These are\nblocked by default in the egress proxy even when network isolation is on.\nOnly applicable to Docker deployments with network isolation enabled.", "type": "boolean" }, "authz_config": { "description": "Authorization configuration", "type": "string" }, "cmd_arguments": { "description": "Command arguments to pass to the container", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "env_vars": { "additionalProperties": { "type": "string" }, "description": "Environment variables to set in the container", "type": "object" }, "group": { "description": "Group name this workload belongs to", "type": "string" }, "header_forward": { "$ref": "#/components/schemas/pkg_api_v1.headerForwardConfig" }, "headers": { "items": { "$ref": "#/components/schemas/registry.Header" }, "type": "array", "uniqueItems": false }, "host": { "description": "Host to bind to", "type": "string" }, "image": { "description": "Docker image to use", "type": "string" }, "network_isolation": { "description": "Whether network isolation is turned on. This applies the rules in the permission profile.\nPointer so that omitting the field defaults to network isolation ENABLED (matching the\n`thv run` CLI default); set it explicitly to false to disable network isolation.\nThis also applies on update: a request that omits this field enables isolation, so\nclients that build update requests from scratch should send it explicitly to avoid\nunintentionally turning isolation on for a workload that had it off.", "type": "boolean" }, "oauth_config": { "$ref": "#/components/schemas/pkg_api_v1.remoteOAuthConfig" }, "oidc": { "$ref": "#/components/schemas/pkg_api_v1.oidcOptions" }, "permission_profile": { "$ref": "#/components/schemas/permissions.Profile" }, "proxy_mode": { "description": "Proxy mode to use", "type": "string" }, "proxy_port": { "description": "Port for the HTTP proxy to listen on", "type": "integer" }, "runtime_config": { "$ref": "#/components/schemas/templates.RuntimeConfig" }, "secrets": { "description": "Secret parameters to inject", "items": { "$ref": "#/components/schemas/secrets.SecretParameter" }, "type": "array", "uniqueItems": false }, "target_port": { "description": "Port to expose from the container", "type": "integer" }, "tools": { "description": "Tools filter", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "tools_override": { "additionalProperties": { "$ref": "#/components/schemas/pkg_api_v1.toolOverride" }, "description": "Tools override", "type": "object" }, "transport": { "description": "Transport configuration", "type": "string" }, "trust_proxy_headers": { "description": "Whether to trust X-Forwarded-* headers from reverse proxies", "type": "boolean" }, "url": { "description": "Remote server specific fields", "type": "string" }, "volumes": { "description": "Volume mounts", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.updateSecretRequest": { "description": "Request to update an existing secret", "properties": { "value": { "description": "New secret value", "type": "string" } }, "type": "object" }, "pkg_api_v1.updateSecretResponse": { "description": "Response after updating a secret", "properties": { "key": { "description": "Secret key that was updated", "type": "string" }, "message": { "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\nthe workloads were enumerated. Each entry carries only metadata and never\nsecret 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.upgradePluginsRequest": { "description": "Request to re-resolve a project's lock entries and install newer content", "properties": { "allow_ref_change": { "description": "AllowRefChange permits resolvedReference changes during upgrade", "type": "boolean" }, "allow_signer_change": { "description": "AllowSignerChange permits upgrading to an artifact signed by a\ndifferent identity than the recorded one", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers. Empty means every\nplugin-supporting client detected on this host.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "fail_on_changes": { "description": "FailOnChanges exits with an error when any mutable source would upgrade", "type": "boolean" }, "names": { "description": "Names restricts the upgrade to specific plugin names. Empty means every entry.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "preview": { "description": "Preview reports what would change without installing (still fetches to compare digests)", "type": "boolean" }, "project_root": { "description": "ProjectRoot is the project root path whose lock file should be upgraded", "type": "string" } }, "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\nupgraded workload's configuration.", "type": "object" }, "secrets": { "description": "Secrets holds additional secret parameters (`\u003cname\u003e,target=\u003cenv\u003e`) to merge\ninto the upgraded workload's configuration. Only references are accepted;\nno secret values are transmitted in the request.", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.upgradeSkillsRequest": { "description": "Request to re-resolve a project's lock entries and install newer content", "properties": { "allow_ref_change": { "description": "AllowRefChange permits resolvedReference changes during upgrade", "type": "boolean" }, "allow_signer_change": { "description": "AllowSignerChange permits upgrading to an artifact signed by a\ndifferent identity than the recorded one", "type": "boolean" }, "clients": { "description": "Clients lists target client identifiers. Empty means every\nskill-supporting client detected on this host.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "fail_on_changes": { "description": "FailOnChanges exits with an error when any mutable source would upgrade", "type": "boolean" }, "names": { "description": "Names restricts the upgrade to specific skill names. Empty means every entry.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "preview": { "description": "Preview reports what would change without installing (still fetches to compare digests)", "type": "boolean" }, "project_root": { "description": "ProjectRoot is the project root path whose lock file should be upgraded", "type": "string" } }, "type": "object" }, "pkg_api_v1.validatePluginRequest": { "description": "Request to validate a plugin definition", "properties": { "path": { "description": "Path to the plugin definition directory", "type": "string" } }, "type": "object" }, "pkg_api_v1.validateSkillRequest": { "description": "Request to validate a skill definition", "properties": { "path": { "description": "Path to the skill definition directory", "type": "string" } }, "type": "object" }, "pkg_api_v1.versionResponse": { "properties": { "version": { "type": "string" } }, "type": "object" }, "pkg_api_v1.workloadListResponse": { "description": "Response containing a list of workloads", "properties": { "workloads": { "description": "List of container information for each workload", "items": { "$ref": "#/components/schemas/core.Workload" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "pkg_api_v1.workloadStatusResponse": { "description": "Response containing workload status information", "properties": { "status": { "description": "Current status of the workload", "enum": [ "running", "stopped", "error", "starting", "stopping", "unhealthy", "removing", "unknown", "unauthenticated", "auth_retrying", "policy_stopped" ], "type": "string" } }, "type": "object" }, "registry.EnvVar": { "properties": { "default": { "description": "Default is the value to use if the environment variable is not explicitly provided\nOnly used for non-required variables", "type": "string" }, "description": { "description": "Description is a human-readable explanation of the variable's purpose", "type": "string" }, "name": { "description": "Name is the environment variable name (e.g., API_KEY)", "type": "string" }, "required": { "description": "Required indicates whether this environment variable must be provided\nIf true and not provided via command line or secrets, the user will be prompted for a value", "type": "boolean" }, "secret": { "description": "Secret indicates whether this environment variable contains sensitive information\nIf true, the value will be stored as a secret rather than as a plain environment variable", "type": "boolean" } }, "type": "object" }, "registry.Group": { "properties": { "description": { "description": "Description is a human-readable description of the group's purpose and functionality", "type": "string" }, "name": { "description": "Name is the identifier for the group, used when referencing the group in commands", "type": "string" }, "remote_servers": { "additionalProperties": { "$ref": "#/components/schemas/registry.RemoteServerMetadata" }, "description": "RemoteServers is a map of server names to their corresponding remote server definitions within this group", "type": "object" }, "servers": { "additionalProperties": { "$ref": "#/components/schemas/registry.ImageMetadata" }, "description": "Servers is a map of server names to their corresponding server definitions within this group", "type": "object" } }, "type": "object" }, "registry.Header": { "properties": { "choices": { "description": "Choices provides a list of valid values for the header (optional)", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "default": { "description": "Default is the value to use if the header is not explicitly provided\nOnly used for non-required headers", "type": "string" }, "description": { "description": "Description is a human-readable explanation of the header's purpose", "type": "string" }, "name": { "description": "Name is the header name (e.g., X-API-Key, Authorization)", "type": "string" }, "required": { "description": "Required indicates whether this header must be provided\nIf true and not provided via command line or secrets, the user will be prompted for a value", "type": "boolean" }, "secret": { "description": "Secret indicates whether this header contains sensitive information\nIf true, the value will be stored as a secret rather than as plain text", "type": "boolean" } }, "type": "object" }, "registry.ImageMetadata": { "description": "Container server details (if it's a container server)", "properties": { "args": { "description": "Args are the default command-line arguments to pass to the MCP server container.\nThese arguments will be used only if no command-line arguments are provided by the user.\nIf the user provides arguments, they will override these defaults.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "custom_metadata": { "additionalProperties": {}, "description": "CustomMetadata allows for additional user-defined metadata", "type": "object" }, "description": { "description": "Description is a human-readable description of the server's purpose and functionality", "type": "string" }, "docker_tags": { "description": "DockerTags lists the available Docker tags for this server image", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "env_vars": { "description": "EnvVars defines environment variables that can be passed to the server", "items": { "$ref": "#/components/schemas/registry.EnvVar" }, "type": "array", "uniqueItems": false }, "image": { "description": "Image is the Docker image reference for the MCP server", "type": "string" }, "metadata": { "$ref": "#/components/schemas/registry.Metadata" }, "name": { "description": "Name is the identifier for the MCP server, used when referencing the server in commands\nIf not provided, it will be auto-generated from the registry key", "type": "string" }, "overview": { "description": "Overview is a longer Markdown-formatted description for web display.\nUnlike the Description field (limited to 500 chars), this supports\nfull Markdown and is intended for rich rendering on catalog pages.", "type": "string" }, "permissions": { "$ref": "#/components/schemas/permissions.Profile" }, "provenance": { "$ref": "#/components/schemas/registry.Provenance" }, "proxy_port": { "description": "ProxyPort is the port for the HTTP proxy to listen on (host port)\nIf not specified, a random available port will be assigned", "type": "integer" }, "repository_url": { "description": "RepositoryURL is the URL to the source code repository for the server", "type": "string" }, "stateless": { "description": "Stateless indicates the server only supports POST (no SSE/GET)", "type": "boolean" }, "status": { "description": "Status indicates whether the server is currently active or deprecated", "type": "string" }, "tags": { "description": "Tags are categorization labels for the server to aid in discovery and filtering", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "target_port": { "description": "TargetPort is the port for the container to expose (only applicable to SSE and Streamable HTTP transports)", "type": "integer" }, "tier": { "description": "Tier represents the tier classification level of the server, e.g., \"Official\" or \"Community\"", "type": "string" }, "title": { "description": "Title is an optional human-readable display name for the server.\nIf not provided, the Name field is used for display purposes.", "type": "string" }, "tools": { "description": "Tools is a list of tool names provided by this MCP server", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "transport": { "description": "Transport defines the communication protocol for the server\nFor containers: stdio, sse, or streamable-http\nFor remote servers: sse or streamable-http (stdio not supported)", "type": "string" } }, "type": "object" }, "registry.KubernetesMetadata": { "description": "Kubernetes contains Kubernetes-specific metadata when the MCP server is deployed in a cluster.\nThis field is optional and only populated when:\n- The server is served from ToolHive Registry Server\n- The server was auto-discovered from a Kubernetes deployment\n- The Kubernetes resource has the required registry annotations", "properties": { "image": { "description": "Image is the container image used by the Kubernetes workload (applicable to MCPServer)", "type": "string" }, "kind": { "description": "Kind is the Kubernetes resource kind (e.g., MCPServer, VirtualMCPServer, MCPRemoteProxy)", "type": "string" }, "name": { "description": "Name is the Kubernetes resource name", "type": "string" }, "namespace": { "description": "Namespace is the Kubernetes namespace where the resource is deployed", "type": "string" }, "transport": { "description": "Transport is the transport type configured for the Kubernetes workload (applicable to MCPServer)", "type": "string" }, "uid": { "description": "UID is the Kubernetes resource UID", "type": "string" } }, "type": "object" }, "registry.Metadata": { "description": "Metadata contains additional information about the server such as popularity metrics", "properties": { "kubernetes": { "$ref": "#/components/schemas/registry.KubernetesMetadata" }, "last_updated": { "description": "LastUpdated is the timestamp when the server was last updated, in RFC3339 format", "type": "string" }, "stars": { "description": "Stars represents the popularity rating or number of stars for the server", "type": "integer" } }, "type": "object" }, "registry.OAuthConfig": { "description": "OAuthConfig provides OAuth/OIDC configuration for authentication to the remote server\nUsed with the thv proxy command's --remote-auth flags", "properties": { "authorize_url": { "description": "AuthorizeURL is the OAuth authorization endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided", "type": "string" }, "callback_port": { "description": "CallbackPort is the specific port to use for the OAuth callback server\nIf not specified, a random available port will be used", "type": "integer" }, "client_id": { "description": "ClientID is the OAuth client ID for authentication", "type": "string" }, "issuer": { "description": "Issuer is the OAuth/OIDC issuer URL (e.g., https://accounts.google.com)\nUsed for OIDC discovery to find authorization and token endpoints", "type": "string" }, "oauth_params": { "additionalProperties": { "type": "string" }, "description": "OAuthParams contains additional OAuth parameters to include in the authorization request\nThese are server-specific parameters like \"prompt\", \"response_mode\", etc.", "type": "object" }, "resource": { "description": "Resource is the OAuth 2.0 resource indicator (RFC 8707)", "type": "string" }, "scopes": { "description": "Scopes are the OAuth scopes to request\nIf not specified, defaults to [\"openid\", \"profile\", \"email\"] for OIDC", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "token_url": { "description": "TokenURL is the OAuth token endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided", "type": "string" }, "use_pkce": { "description": "UsePKCE indicates whether to use PKCE for the OAuth flow\nDefaults to true for enhanced security", "type": "boolean" } }, "type": "object" }, "registry.Plugin": { "properties": { "_meta": { "additionalProperties": {}, "description": "Meta is an opaque payload with extended meta data details of the plugin.", "type": "object" }, "description": { "description": "Description is the description of the plugin.", "type": "string" }, "icons": { "description": "Icons is the list of icons for the plugin.", "items": { "$ref": "#/components/schemas/registry.SkillIcon" }, "type": "array", "uniqueItems": false }, "license": { "description": "License is the SPDX license identifier of the plugin.", "type": "string" }, "metadata": { "additionalProperties": {}, "description": "Metadata is the official metadata of the plugin as reported in the\nplugin manifest file.", "type": "object" }, "name": { "description": "Name is the name of the plugin.\nThe format is that of identifiers, e.g. \"my-plugin\".", "type": "string" }, "namespace": { "description": "Namespace is the namespace of the plugin.\nThe format is reverse-DNS, e.g. \"io.github.user\".", "type": "string" }, "packages": { "description": "Packages is the list of packages for the plugin.", "items": { "$ref": "#/components/schemas/registry.SkillPackage" }, "type": "array", "uniqueItems": false }, "repository": { "$ref": "#/components/schemas/registry.SkillRepository" }, "status": { "description": "Status is the status of the plugin.\nCan be one of \"active\", \"deprecated\", or \"archived\".", "type": "string" }, "title": { "description": "Title is the title of the plugin.\nThis is for human consumption, not an identifier.", "type": "string" }, "version": { "description": "Version is the version of the plugin.\nAny non-empty string is valid, but ideally it should be either a\nsemantic version or a commit hash.", "type": "string" } }, "type": "object" }, "registry.Provenance": { "description": "Provenance is the expected signer identity for this skill, checked on\nfirst install instead of trust-on-first-use. Absent means unconstrained\n— most catalog entries won't have this for a while, and that must not\nbreak installs; it's an opt-in tightening per entry, not a requirement.\n\nEach field constrains independently, and an empty string leaves that\ndimension unconstrained. Attestation is the exception: setting it at\nall, even to an empty struct, requires the artifact to be attested, so\nverification fails against a signature carrying no statement. Its own\nPredicateType and Predicate then follow the usual rule and constrain\nonly when set. Predicate must be a JSON object; anything else can never\nmatch, and Validate rejects it rather than letting it through as a\nconstraint that silently fails every artifact.", "properties": { "attestation": { "$ref": "#/components/schemas/registry.VerifiedAttestation" }, "cert_issuer": { "type": "string" }, "repository_ref": { "type": "string" }, "repository_uri": { "type": "string" }, "runner_environment": { "type": "string" }, "signer_identity": { "type": "string" }, "sigstore_url": { "type": "string" } }, "type": "object" }, "registry.Registry": { "description": "Full registry data", "properties": { "groups": { "description": "Groups is a slice of group definitions containing related MCP servers", "items": { "$ref": "#/components/schemas/registry.Group" }, "type": "array", "uniqueItems": false }, "last_updated": { "description": "LastUpdated is the timestamp when the registry was last updated, in RFC3339 format", "type": "string" }, "remote_servers": { "additionalProperties": { "$ref": "#/components/schemas/registry.RemoteServerMetadata" }, "description": "RemoteServers is a map of server names to their corresponding remote server definitions\nThese are MCP servers accessed via HTTP/HTTPS using the thv proxy command", "type": "object" }, "servers": { "additionalProperties": { "$ref": "#/components/schemas/registry.ImageMetadata" }, "description": "Servers is a map of server names to their corresponding server definitions", "type": "object" }, "version": { "description": "Version is the schema version of the registry", "type": "string" } }, "type": "object" }, "registry.RemoteServerMetadata": { "description": "Remote server details (if it's a remote server)", "properties": { "custom_metadata": { "additionalProperties": {}, "description": "CustomMetadata allows for additional user-defined metadata", "type": "object" }, "description": { "description": "Description is a human-readable description of the server's purpose and functionality", "type": "string" }, "env_vars": { "description": "EnvVars defines environment variables that can be passed to configure the client\nThese might be needed for client-side configuration when connecting to the remote server", "items": { "$ref": "#/components/schemas/registry.EnvVar" }, "type": "array", "uniqueItems": false }, "headers": { "description": "Headers defines HTTP headers that can be passed to the remote server for authentication\nThese are used with the thv proxy command's authentication features", "items": { "$ref": "#/components/schemas/registry.Header" }, "type": "array", "uniqueItems": false }, "metadata": { "$ref": "#/components/schemas/registry.Metadata" }, "name": { "description": "Name is the identifier for the MCP server, used when referencing the server in commands\nIf not provided, it will be auto-generated from the registry key", "type": "string" }, "oauth_config": { "$ref": "#/components/schemas/registry.OAuthConfig" }, "overview": { "description": "Overview is a longer Markdown-formatted description for web display.\nUnlike the Description field (limited to 500 chars), this supports\nfull Markdown and is intended for rich rendering on catalog pages.", "type": "string" }, "proxy_port": { "description": "ProxyPort is the port for the HTTP proxy to listen on (host port)\nIf not specified, a random available port will be assigned", "type": "integer" }, "repository_url": { "description": "RepositoryURL is the URL to the source code repository for the server", "type": "string" }, "stateless": { "description": "Stateless indicates the server only supports POST (no SSE/GET)", "type": "boolean" }, "status": { "description": "Status indicates whether the server is currently active or deprecated", "type": "string" }, "tags": { "description": "Tags are categorization labels for the server to aid in discovery and filtering", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "tier": { "description": "Tier represents the tier classification level of the server, e.g., \"Official\" or \"Community\"", "type": "string" }, "title": { "description": "Title is an optional human-readable display name for the server.\nIf not provided, the Name field is used for display purposes.", "type": "string" }, "tools": { "description": "Tools is a list of tool names provided by this MCP server", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "transport": { "description": "Transport defines the communication protocol for the server\nFor containers: stdio, sse, or streamable-http\nFor remote servers: sse or streamable-http (stdio not supported)", "type": "string" }, "url": { "description": "URL is the endpoint URL for the remote MCP server (e.g., https://api.example.com/mcp)", "type": "string" } }, "type": "object" }, "registry.Skill": { "properties": { "_meta": { "additionalProperties": {}, "description": "Meta is an opaque payload with extended meta data details of the skill.", "type": "object" }, "allowedTools": { "description": "AllowedTools is the list of tools that the skill is compatible with.\nThis is experimental.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "compatibility": { "description": "Compatibility is the environment requirements of the skill.", "type": "string" }, "description": { "description": "Description is the description of the skill.", "type": "string" }, "icons": { "description": "Icons is the list of icons for the skill.", "items": { "$ref": "#/components/schemas/registry.SkillIcon" }, "type": "array", "uniqueItems": false }, "license": { "description": "License is the SPDX license identifier of the skill.", "type": "string" }, "metadata": { "additionalProperties": {}, "description": "Metadata is the official metadata of the skill as reported in the\nSKILL.md file.", "type": "object" }, "name": { "description": "Name is the name of the skill.\nThe format is that of identifiers, e.g. \"my-skill\".", "type": "string" }, "namespace": { "description": "Namespace is the namespace of the skill.\nThe format is reverse-DNS, e.g. \"io.github.user\".", "type": "string" }, "packages": { "description": "Packages is the list of packages for the skill.", "items": { "$ref": "#/components/schemas/registry.SkillPackage" }, "type": "array", "uniqueItems": false }, "provenance": { "$ref": "#/components/schemas/registry.Provenance" }, "repository": { "$ref": "#/components/schemas/registry.SkillRepository" }, "status": { "description": "Status is the status of the skill.\nCan be one of \"active\", \"deprecated\", or \"archived\".", "type": "string" }, "title": { "description": "Title is the title of the skill.\nThis is for human consumption, not an identifier.", "type": "string" }, "version": { "description": "Version is the version of the skill.\nAny non-empty string is valid, but ideally it should be either a\nsemantic version or a commit hash.", "type": "string" } }, "type": "object" }, "registry.SkillIcon": { "properties": { "label": { "description": "Label is the label of the icon.", "type": "string" }, "size": { "description": "Size is the size of the icon.", "type": "string" }, "src": { "description": "Src is the source of the icon.", "type": "string" }, "type": { "description": "Type is the type of the icon.", "type": "string" } }, "type": "object" }, "registry.SkillPackage": { "properties": { "commit": { "description": "Commit is the commit of the package.", "type": "string" }, "digest": { "description": "Digest is the digest of the package.", "type": "string" }, "identifier": { "description": "Identifier is the OCI identifier of the package.", "type": "string" }, "mediaType": { "description": "MediaType is the media type of the package.", "type": "string" }, "ref": { "description": "Ref is the reference of the package.", "type": "string" }, "registryType": { "description": "RegistryType is the type of registry the package is from.\nCan be \"oci\" or \"git\".", "type": "string" }, "subfolder": { "description": "Subfolder is the subfolder of the package.", "type": "string" }, "url": { "description": "URL is the URL of the package.", "type": "string" } }, "type": "object" }, "registry.SkillRepository": { "description": "Repository is the source repository of the skill.", "properties": { "type": { "description": "Type is the type of the repository.", "type": "string" }, "url": { "description": "URL is the URL of the repository.", "type": "string" } }, "type": "object" }, "registry.VerifiedAttestation": { "properties": { "predicate": {}, "predicate_type": { "type": "string" } }, "type": "object" }, "remote.Config": { "description": "RemoteAuthConfig contains OAuth configuration for remote MCP servers", "properties": { "authorize_url": { "type": "string" }, "bearer_token": { "description": "Bearer token configuration (alternative to OAuth)", "type": "string" }, "bearer_token_file": { "type": "string" }, "cached_cimd_client_id": { "description": "CachedCIMDClientID stores the CIMD metadata URL used as client_id when CIMD\nauthentication was used. Kept separate from CachedClientID (which holds\nDCR-issued IDs) so the two can have independent lifecycles — DCR credential\nrotation clears CachedClientID without touching the stable CIMD URL.\nRead by resolveClientCredentials to send the correct client_id on token refresh.", "type": "string" }, "cached_client_id": { "description": "Cached DCR client credentials for persistence across restarts.\nThese are obtained during Dynamic Client Registration and needed to refresh tokens.\nClientID is stored as plain text since it's public information.", "type": "string" }, "cached_client_secret_ref": { "type": "string" }, "cached_dcr_callback_port": { "description": "CachedDCRCallbackPort is the callback port that was actually registered\nduring DCR. It may differ from CallbackPort when the requested port was\nunavailable and a fallback port was selected.", "type": "integer" }, "cached_refresh_token_ref": { "description": "Cached OAuth token reference for persistence across restarts.\nThe refresh token is stored securely in the secret manager, and this field\ncontains the reference to retrieve it (e.g., \"OAUTH_REFRESH_TOKEN_workload\").\nThis enables session restoration without requiring a new browser-based login.", "type": "string" }, "cached_reg_client_uri": { "description": "CachedRegClientURI is the registration_client_uri from the DCR response.\nThis is the endpoint used for RFC 7592 client read/update/delete operations.\nStored as plain text since it is not sensitive.", "type": "string" }, "cached_reg_token_ref": { "description": "CachedRegTokenRef is a secret manager reference to the registration_access_token\nreturned in the DCR response. Used for RFC 7592 client update operations.\nStored as a secret reference since it's sensitive.", "type": "string" }, "cached_secret_expiry": { "description": "ClientSecretExpiresAt indicates when the client secret expires (if provided by the DCR server).\nA zero value means the secret does not expire.", "type": "string" }, "cached_token_auth_method": { "description": "CachedTokenEndpointAuthMethod is the auth method used for the token endpoint\n(e.g., \"client_secret_basic\", \"none\"). Persisted for RFC 7592 updates.", "type": "string" }, "cached_token_expiry": { "type": "string" }, "callback_port": { "type": "integer" }, "client_id": { "type": "string" }, "client_secret": { "type": "string" }, "client_secret_file": { "type": "string" }, "issuer": { "description": "OAuth endpoint configuration (from registry)", "type": "string" }, "oauth_params": { "additionalProperties": { "type": "string" }, "description": "OAuth parameters for server-specific customization", "type": "object" }, "resource": { "description": "Resource is the OAuth 2.0 resource indicator (RFC 8707).", "type": "string" }, "scope_param_name": { "description": "ScopeParamName overrides the query parameter name used to send scopes in the\nauthorization URL. When empty, the standard \"scope\" parameter is used.\nSome providers require a non-standard name (e.g., Slack uses \"user_scope\").", "type": "string" }, "scopes": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "skip_browser": { "type": "boolean" }, "timeout": { "example": "5m", "type": "string" }, "token_url": { "type": "string" }, "use_pkce": { "type": "boolean" } }, "type": "object" }, "secrets.SecretParameter": { "description": "Bearer token for authentication (alternative to OAuth)", "properties": { "name": { "type": "string" }, "target": { "type": "string" } }, "type": "object" }, "storage.ACLUserRunConfig": { "description": "ACLUserConfig contains ACL user authentication configuration.", "properties": { "password_env_var": { "description": "PasswordEnvVar is the environment variable containing the Redis password.", "type": "string" }, "username_env_var": { "description": "UsernameEnvVar is the environment variable containing the Redis username.", "type": "string" } }, "type": "object" }, "storage.RedisRunConfig": { "description": "RedisConfig is the Redis-specific configuration when Type is \"redis\".", "properties": { "acl_user_config": { "$ref": "#/components/schemas/storage.ACLUserRunConfig" }, "addr": { "description": "Addr is the Redis server address (host:port). Required for standalone and cluster modes.\nMutually exclusive with SentinelConfig.", "type": "string" }, "auth_type": { "description": "AuthType must be \"aclUser\" - only ACL user authentication is supported.", "type": "string" }, "cluster_mode": { "description": "ClusterMode enables the Redis Cluster protocol. Requires Addr to be set.", "type": "boolean" }, "dial_timeout": { "description": "DialTimeout is the timeout for establishing connections (e.g., \"5s\").", "type": "string" }, "key_prefix": { "description": "KeyPrefix for multi-tenancy, typically \"thv:auth:{ns}:{name}:\".", "type": "string" }, "read_timeout": { "description": "ReadTimeout is the timeout for read operations (e.g., \"3s\").", "type": "string" }, "sentinel_config": { "$ref": "#/components/schemas/storage.SentinelRunConfig" }, "sentinel_tls": { "$ref": "#/components/schemas/storage.RedisTLSRunConfig" }, "tls": { "$ref": "#/components/schemas/storage.RedisTLSRunConfig" }, "write_timeout": { "description": "WriteTimeout is the timeout for write operations (e.g., \"3s\").", "type": "string" } }, "type": "object" }, "storage.RedisTLSRunConfig": { "description": "SentinelTLS configures TLS for Sentinel connections. Only applies when SentinelConfig is set.", "properties": { "ca_cert_file": { "description": "CACertFile is the path to a PEM-encoded CA certificate file.", "type": "string" }, "insecure_skip_verify": { "description": "InsecureSkipVerify skips certificate verification.", "type": "boolean" } }, "type": "object" }, "storage.RunConfig": { "description": "Storage configures the storage backend for the auth server.\nIf nil, defaults to in-memory storage.", "properties": { "redis_config": { "$ref": "#/components/schemas/storage.RedisRunConfig" }, "type": { "description": "Type specifies the storage backend type. Defaults to \"memory\".", "type": "string" } }, "type": "object" }, "storage.SentinelRunConfig": { "description": "SentinelConfig contains Sentinel-specific configuration.\nMutually exclusive with Addr.", "properties": { "db": { "description": "DB is the Redis database number (default: 0).", "type": "integer" }, "master_name": { "description": "MasterName is the name of the Redis Sentinel master.", "type": "string" }, "sentinel_addrs": { "description": "SentinelAddrs is the list of Sentinel addresses (host:port).", "items": { "type": "string" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "telemetry.Config": { "description": "DEPRECATED: Middleware configuration.\nTelemetryConfig contains the OpenTelemetry configuration", "properties": { "caCertPath": { "description": "CACertPath is the file path to a CA certificate bundle for the OTLP endpoint.\nWhen set, the OTLP exporters use this CA to verify the collector's TLS certificate\ninstead of relying solely on the system CA pool.\n+optional", "type": "string" }, "customAttributes": { "additionalProperties": { "type": "string" }, "description": "CustomAttributes contains custom resource attributes to be added to all telemetry signals.\nThese are parsed from CLI flags (--otel-custom-attributes) or environment variables\n(OTEL_RESOURCE_ATTRIBUTES) as key=value pairs.\n+optional", "type": "object" }, "enablePrometheusMetricsPath": { "description": "EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint.\nThe metrics are served at /metrics on a dedicated diagnostics port rather than on the\nmain transport port, so the endpoint can be restricted by port and is not routed\nalongside application traffic. The endpoint is unauthenticated either way.\nSee PrometheusPort and pkg/diagnostics.\nThis is separate from OTLP metrics which are sent to the Endpoint.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "endpoint": { "description": "Endpoint is the OTLP endpoint URL\n+optional", "type": "string" }, "environmentVariables": { "description": "EnvironmentVariables is a list of environment variable names that should be\nincluded in telemetry spans as attributes. Only variables in this list will\nbe read from the host machine and included in spans for observability.\nExample: [\"NODE_ENV\", \"DEPLOYMENT_ENV\", \"SERVICE_VERSION\"]\n+optional", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "headers": { "additionalProperties": { "type": "string" }, "description": "Headers contains authentication headers for the OTLP endpoint.\n+optional", "type": "object" }, "insecure": { "description": "Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "metricsEnabled": { "description": "MetricsEnabled controls whether OTLP metrics are enabled.\nWhen false, OTLP metrics are not sent even if an endpoint is configured.\nThis is independent of EnablePrometheusMetricsPath.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "metricsOnTransportPort": { "description": "MetricsOnTransportPort controls whether /metrics is ALSO served on the main\ntransport port, in addition to the diagnostics port. It exists to give\ndeployments a migration window: while true, an existing scrape configuration\naimed at the transport port keeps working, and a new one aimed at\nPrometheusPort works too, so a scraper can be moved and verified before the\nold location goes away. See https://github.com/stacklok/toolhive/issues/6384 for\nthe removal timeline.\n\n+optional", "type": "boolean" }, "prometheusPort": { "description": "PrometheusPort is the port the Prometheus /metrics endpoint is served on when\nEnablePrometheusMetricsPath is true. It is deliberately not the main transport port,\nso that access can be restricted with a NetworkPolicy: NetworkPolicy matches on port,\nnot on HTTP path, so a shared port makes \"allow MCP traffic, deny metrics scraping\"\nimpossible to express. The endpoint itself is unauthenticated, so restricting who can\nreach this port is how it is protected.\n\nZero selects the default diagnostics port (9464, the OpenTelemetry specification's\nPrometheus exporter default). If that port is taken the listener falls back to an\navailable one and logs the resolved address. Do not route this port publicly.\n+optional", "type": "integer" }, "samplingRate": { "description": "SamplingRate is the trace sampling rate (0.0-1.0) as a string.\nOnly used when TracingEnabled is true.\nExample: \"0.05\" for 5% sampling.\n+kubebuilder:default=\"0.05\"\n+optional", "type": "string" }, "serviceName": { "description": "ServiceName is the service name for telemetry.\nWhen omitted, defaults to the server name (e.g., VirtualMCPServer name).\n+optional", "type": "string" }, "serviceVersion": { "description": "ServiceVersion is the service version for telemetry.\nWhen omitted, defaults to the ToolHive version.\n+optional", "type": "string" }, "tracingEnabled": { "description": "TracingEnabled controls whether distributed tracing is enabled.\nWhen false, no tracer provider is created even if an endpoint is configured.\n+kubebuilder:default=false\n+optional", "type": "boolean" }, "useLegacyAttributes": { "description": "UseLegacyAttributes controls whether legacy (pre-MCP OTEL semconv) attribute names\nare emitted alongside the new standard attribute names. When true, spans include both\nold and new attribute names for backward compatibility with existing dashboards.\nCurrently defaults to true; this will change to false in a future release.\n+kubebuilder:default=true\n+optional", "type": "boolean" } }, "type": "object" }, "templates.RuntimeConfig": { "description": "RuntimeConfig allows overriding the default runtime configuration\nfor this specific workload (base images and packages)", "properties": { "additional_packages": { "description": "AdditionalPackages lists extra packages to install in the builder and\nruntime stages.\nExamples for Alpine: [\"git\", \"make\", \"gcc\"]\nExamples for Debian: [\"git\", \"build-essential\"]", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "build_with": { "description": "BuildWith lists build-time dependency constraints, interpreted per\npackage ecosystem. For uvx:// builds these are PEP 508 requirement\nspecifiers passed to `uv tool install --with`, used to constrain\ntransitive dependencies the package itself leaves unbounded\n(e.g. \"mcp\u003c2\"). Ecosystems without constraint support (npx://, go://)\nreject a non-empty BuildWith at build time.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "builder_image": { "description": "BuilderImage is the full image reference for the builder stage.\nAn empty string signals \"use the default for this transport type\" during config merging.\nExamples: \"golang:1.26-alpine\", \"node:24-alpine\", \"python:3.14-slim\"", "type": "string" }, "runtime_env": { "additionalProperties": { "type": "string" }, "description": "RuntimeEnv contains environment variables to inject into the Dockerfile's\nfinal runtime stage. Unlike BuildEnv (pkg/container/templates.TemplateData.BuildEnv),\nwhich only affects the builder stage, these variables are baked into the\nshipped image and are present in the running container's process\nenvironment at startup. Use this for values a packaged MCP server reads at\nprocess start (e.g. feature flags, cache backend selection), not for\nbuild-time package manager configuration.\nKeys must be uppercase with underscores, values are validated for safety.", "type": "object" } }, "type": "object" }, "tokenexchange.Config": { "description": "TokenExchangeConfig contains token exchange configuration for external authentication", "properties": { "audience": { "description": "Audience is the target audience for the exchanged token", "type": "string" }, "client_id": { "description": "ClientID is the OAuth 2.0 client identifier", "type": "string" }, "client_secret": { "description": "ClientSecret is the OAuth 2.0 client secret", "type": "string" }, "external_token_header_name": { "description": "ExternalTokenHeaderName is the name of the custom header to use when HeaderStrategy is \"custom\"", "type": "string" }, "header_strategy": { "description": "HeaderStrategy determines how to inject the token\nValid values: HeaderStrategyReplace (default), HeaderStrategyCustom", "type": "string" }, "scopes": { "description": "Scopes is the list of scopes to request for the exchanged token", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "subject_token_type": { "description": "SubjectTokenType specifies the type of the subject token being exchanged.\nCommon values: oauthproto.TokenTypeAccessToken (default), oauthproto.TokenTypeIDToken, oauthproto.TokenTypeJWT.\nIf empty, defaults to oauthproto.TokenTypeAccessToken.", "type": "string" }, "token_url": { "description": "TokenURL is the OAuth 2.0 token endpoint URL", "type": "string" } }, "type": "object" }, "tokenexchange.JWTBearerGrantPolicy": { "description": "JWTBearerGrant optionally enables the plain RFC 7523 JWT-bearer grant.\nIt accepts assertions from this issuer without client authentication and\nlimits their maximum age, subjects, and RFC 8707 resources. It is\nindependent from RFC 8693 delegation policy.", "properties": { "accepted_audiences": { "description": "AcceptedAudiences is the set of \"this AS\" identity strings an\nassertion's \"aud\" claim must intersect — e.g. to support migrating\nthis server's issuer/token-endpoint URL, or exposing it under more\nthan one valid name. Each value uniquely identifies this\nauthorization server for this grant; it is NOT a resource/API\nidentifier — a bare resource audience is deliberately not accepted\nhere, that would let any RFC 8707 resource-scoped token satisfy the\ngrant instead of only tokens minted for this AS. Defaults to\n[tokenEndpoint] when empty, preserving prior exact-match behavior.", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "max_assertion_age": { "type": "string" }, "subject_bindings": { "items": { "$ref": "#/components/schemas/tokenexchange.JWTBearerSubjectBinding" }, "type": "array", "uniqueItems": false } }, "type": "object" }, "tokenexchange.JWTBearerSubjectBinding": { "properties": { "allowed_resources": { "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "subject": { "type": "string" } }, "type": "object" }, "tokenexchange.TrustedIssuer": { "properties": { "actor_claim": { "description": "ActorClaim names the claim identifying the client that requested the\nsubject token from THIS EXTERNAL ISSUER (used by AllowedActors below).\nValues are in the external issuer's namespace, NOT ToolHive client\nIDs. Defaults to \"azp\"; use \"appid\" for Microsoft Entra v1, \"cid\" for\nOkta. The special value \"client_id\" reads ValidatedClaims.ClientID\ninstead of Extra (assignClaim routes it to that field) — it is still\nthe external token's client_id claim, not a ToolHive one.", "type": "string" }, "actor_matcher": { "description": "ActorMatcher is an admin-authored CEL expression evaluated against the\ncomplete signature-verified JWT claims map as \"claims\". A true result\nauthorizes delegation alongside AllowedActors; a syntax or type error\nfails configuration validation. An expression that compiles but does\nnot return bool is NOT caught at that point, though — it compiles\nsuccessfully and is only rejected the first time it is evaluated\nagainst a real token, denying that token (and every one after it, since\nthe expression will never return bool). Any other runtime evaluation\nerror denies the token the same way.", "type": "string" }, "allow_may_act": { "description": "AllowMayAct permits this external issuer's may_act claim to authorize\ndelegation. It defaults to false; external issuers must be opted in\nexplicitly because may_act bypasses AllowedActors and ActorMatcher. It\ndoes not affect self-issued subject tokens. When enabled,\nAllowedDelegateClients must name specific ToolHive clients rather than\nuse the wildcard.", "type": "boolean" }, "allow_private_ips": { "description": "AllowPrivateIPs permits OIDC discovery and JWKS fetches for THIS\nissuer to resolve to a private or loopback address. Use only when the\nissuer is hosted inside the same cluster and has no public endpoint.", "type": "boolean" }, "allowed_actors": { "description": "AllowedActors is the allowlist of ActorClaim values authorized to\nexchange a subject token from this issuer when it carries no\n\"may_act\" claim. ActorMatcher can additionally authorize a token by\nmatching its complete verified claims map; either signal is sufficient.\nWhen both are empty, only may_act-bearing tokens are accepted, and only\nif AllowMayAct is also true for this issuer. By itself names no\nToolHive client — see AllowedDelegateClients and\ndocs/arch/17-token-exchange-delegation.md (\"Accepted limitations\" #1).", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "allowed_delegate_clients": { "description": "AllowedDelegateClients restricts which ToolHive client IDs may\nexchange a subject token from this issuer, for BOTH consent paths.\nRequired (validateTrustedIssuer rejects empty/absent); \"*\" permits\nany confidential client holding the grant. See\ndocs/arch/17-token-exchange-delegation.md (\"Accepted limitations\" #1).", "items": { "type": "string" }, "type": "array", "uniqueItems": false }, "expected_audience": { "description": "ExpectedAudience is the expected \"aud\" claim value that must appear\nin an RFC 8693 subject token's audience list (a resource/API identifier,\nnot a client ID — required for delegation unless JWTBearerGrant is\nconfigured; see looksLikeResourceIdentifier). RFC 7523 assertions use\nthe token endpoint as their audience instead.\nSee docs/arch/17-token-exchange-delegation.md (\"ID/access-token\ndiscrimination\") for why and its limits.", "type": "string" }, "insecure_allow_http": { "description": "InsecureAllowHTTP permits plain-HTTP OIDC discovery and JWKS fetches\nfor THIS issuer only. Development and testing only — never set in\nproduction. Does not relax the private-IP guard; see AllowPrivateIPs.\nDeliberately per-issuer: this server's own InsecureAllowHTTP must not\nsilently permit plaintext discovery for every trusted external issuer\ntoo — a network attacker who can intercept that traffic could\nsubstitute a JWKS and forge subject tokens for that issuer's\nnamespace.", "type": "boolean" }, "issuer_url": { "description": "IssuerURL is the expected \"iss\" claim value (exact match).", "type": "string" }, "jwks_url": { "description": "JWKSURL is the URL to fetch the issuer's JSON Web Key Set from.\nIf empty, it is resolved via OIDC discovery at {IssuerURL}/.well-known/openid-configuration.", "type": "string" }, "jwt_bearer_grant": { "$ref": "#/components/schemas/tokenexchange.JWTBearerGrantPolicy" } }, "type": "object" }, "types.MiddlewareConfig": { "properties": { "parameters": { "description": "Parameters is a JSON object containing the middleware parameters.\nIt is stored as a raw message to allow flexible parameter types.", "type": "object" }, "type": { "description": "Type is a string representing the middleware type.", "type": "string" } }, "type": "object" }, "types.RateLimitBucket": { "description": "PerUser token bucket configuration for this tool.\n+optional", "properties": { "maxTokens": { "description": "MaxTokens is the maximum number of tokens (bucket capacity).\nThis is also the burst size: the maximum number of requests that can be served\ninstantaneously before the bucket is depleted.\n+kubebuilder:validation:Required\n+kubebuilder:validation:Minimum=1", "type": "integer" }, "refillPeriod": { "$ref": "#/components/schemas/v1.Duration" } }, "type": "object" }, "types.ToolRateLimitConfig": { "properties": { "name": { "description": "Name is the MCP tool name this limit applies to.\n+kubebuilder:validation:Required\n+kubebuilder:validation:MinLength=1", "type": "string" }, "perUser": { "$ref": "#/components/schemas/types.RateLimitBucket" }, "shared": { "$ref": "#/components/schemas/types.RateLimitBucket" } }, "type": "object" }, "v0.ServerJSON": { "properties": { "$schema": { "example": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "format": "uri", "minLength": 1, "type": "string" }, "_meta": { "$ref": "#/components/schemas/v0.ServerMeta" }, "description": { "example": "MCP server providing weather data and forecasts via OpenWeatherMap API", "maxLength": 100, "minLength": 1, "type": "string" }, "icons": { "items": { "$ref": "#/components/schemas/model.Icon" }, "type": "array", "uniqueItems": false }, "name": { "example": "io.github.user/weather", "maxLength": 200, "minLength": 3, "pattern": "^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$", "type": "string" }, "packages": { "items": { "$ref": "#/components/schemas/model.Package" }, "type": "array", "uniqueItems": false }, "remotes": { "items": { "$ref": "#/components/schemas/model.Transport" }, "type": "array", "uniqueItems": false }, "repository": { "$ref": "#/components/schemas/model.Repository" }, "title": { "example": "Weather API", "maxLength": 100, "minLength": 1, "type": "string" }, "version": { "example": "1.0.2", "maxLength": 255, "minLength": 1, "type": "string" }, "websiteUrl": { "example": "https://modelcontextprotocol.io/examples", "format": "uri", "type": "string" } }, "type": "object" }, "v0.ServerMeta": { "properties": { "io.modelcontextprotocol.registry/publisher-provided": { "additionalProperties": {}, "type": "object" } }, "type": "object" }, "v1.Duration": { "description": "RefillPeriod is the duration to fully refill the bucket from zero to maxTokens.\nThe effective refill rate is maxTokens / refillPeriod tokens per second.\nFormat: Go duration string (e.g., \"1m0s\", \"30s\", \"1h0m0s\").\n+kubebuilder:validation:Required", "type": "object" }, "v1beta1.RateLimitConfig": { "description": "RateLimitConfig contains the CRD rate limiting configuration.\nWhen set, rate limiting middleware is added to the proxy middleware chain.", "properties": { "perUser": { "$ref": "#/components/schemas/types.RateLimitBucket" }, "shared": { "$ref": "#/components/schemas/types.RateLimitBucket" }, "tools": { "description": "Tools defines per-tool rate limit overrides.\nEach entry applies additional rate limits to calls targeting a specific tool name.\nA request must pass both the server-level limit and the per-tool limit.\n+listType=map\n+listMapKey=name\n+optional", "items": { "$ref": "#/components/schemas/types.ToolRateLimitConfig" }, "type": "array", "uniqueItems": false } }, "type": "object" } } }, "info": { "description": "This is the ToolHive API server.", "title": "ToolHive API", "version": "1.0" }, "externalDocs": { "description": "", "url": "" }, "paths": { "/api/openapi.json": { "get": { "description": "Returns the OpenAPI specification for the API", "responses": { "200": { "content": { "application/json": { "schema": { "type": "object" } } }, "description": "OpenAPI specification" } }, "summary": "Get OpenAPI specification", "tags": ["system"] } }, "/api/v1beta/clients": { "get": { "description": "List all registered clients in ToolHive", "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_client.RegisteredClient" }, "type": "array" } } }, "description": "OK" } }, "summary": "List all clients", "tags": ["clients"] }, "post": { "description": "Register a new client with ToolHive", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.createClientRequest", "summary": "client", "description": "Client to register" } ] } } }, "description": "Client to register", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.createClientResponse" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Invalid request or unsupported client type" } }, "summary": "Register a new client", "tags": ["clients"] } }, "/api/v1beta/clients/register": { "post": { "description": "Register multiple clients with ToolHive", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.bulkClientRequest", "summary": "clients", "description": "Clients to register" } ] } } }, "description": "Clients to register", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "items": { "$ref": "#/components/schemas/pkg_api_v1.createClientResponse" }, "type": "array" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Invalid request or unsupported client type" } }, "summary": "Register multiple clients", "tags": ["clients"] } }, "/api/v1beta/clients/unregister": { "post": { "description": "Unregister multiple clients from ToolHive", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.bulkClientRequest", "summary": "clients", "description": "Clients to unregister" } ] } } }, "description": "Clients to unregister", "required": true }, "responses": { "204": { "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Invalid request or unsupported client type" } }, "summary": "Unregister multiple clients", "tags": ["clients"] } }, "/api/v1beta/clients/{name}": { "delete": { "description": "Unregister a client from ToolHive", "parameters": [ { "description": "Client name to unregister", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Invalid request or unsupported client type" } }, "summary": "Unregister a client", "tags": ["clients"] } }, "/api/v1beta/clients/{name}/groups/{group}": { "delete": { "description": "Unregister a client from a specific group in ToolHive", "parameters": [ { "description": "Client name to unregister", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Group name to remove client from", "in": "path", "name": "group", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Invalid request or unsupported client type" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Client or group not found" } }, "summary": "Unregister a client from a specific group", "tags": ["clients"] } }, "/api/v1beta/discovery/clients": { "get": { "description": "List all clients compatible with ToolHive and their status.\nEach object includes supports_skills when ToolHive can install skills for that client.", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.clientStatusResponse" } } }, "description": "OK" } }, "summary": "List all clients status", "tags": ["discovery"] } }, "/api/v1beta/groups": { "get": { "description": "Get a list of all groups", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.groupListResponse" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List all groups", "tags": ["groups"] }, "post": { "description": "Create a new group with the specified name", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.createGroupRequest", "summary": "group", "description": "Group creation request" } ] } } }, "description": "Group creation request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.createGroupResponse" } } }, "description": "Created" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "409": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Conflict" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Create a new group", "tags": ["groups"] } }, "/api/v1beta/groups/{name}": { "delete": { "description": "Delete a group by name.", "parameters": [ { "description": "Group name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Delete all workloads in the group (default: false, moves workloads to default group)", "in": "query", "name": "with-workloads", "schema": { "type": "boolean" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Delete a group", "tags": ["groups"] }, "get": { "description": "Get details of a specific group", "parameters": [ { "description": "Group name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_groups.Group" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Get group details", "tags": ["groups"] } }, "/api/v1beta/plugins": { "get": { "description": "Get a list of all installed plugins", "parameters": [ { "description": "Filter by scope (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Filter by client app", "in": "query", "name": "client", "schema": { "type": "string" } }, { "description": "Filter by project root path", "in": "query", "name": "project_root", "schema": { "type": "string" } }, { "description": "Filter by group name", "in": "query", "name": "group", "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.pluginListResponse" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List all installed plugins", "tags": ["plugins"] }, "post": { "description": "Install a plugin from a remote source", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.installPluginRequest", "summary": "request", "description": "Install request" } ] } } }, "description": "Install request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.installPluginResponse" } } }, "description": "Created", "headers": { "Location": { "description": "URI of the installed plugin resource", "schema": { "type": "string" } } } }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "401": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Unauthorized (registry refused credentials)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (artifact not present in registry)" }, "409": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Conflict" }, "429": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Too Many Requests (registry rate limit)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "502": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Gateway (upstream registry failure)" }, "504": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Gateway Timeout (upstream pull timed out)" } }, "summary": "Install a plugin", "tags": ["plugins"] } }, "/api/v1beta/plugins/build": { "post": { "description": "Build a plugin from a local directory", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.buildPluginRequest", "summary": "request", "description": "Build request" } ] } } }, "description": "Build request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.BuildResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Build a plugin", "tags": ["plugins"] } }, "/api/v1beta/plugins/builds": { "get": { "description": "Get a list of all locally-built OCI plugin artifacts in the local store", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.pluginBuildListResponse" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List locally-built plugin artifacts", "tags": ["plugins"] } }, "/api/v1beta/plugins/builds/{tag}": { "delete": { "description": "Remove a locally-built OCI plugin artifact and its blobs from the local store", "parameters": [ { "description": "Artifact tag", "in": "path", "name": "tag", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Delete a locally-built plugin artifact", "tags": ["plugins"] } }, "/api/v1beta/plugins/content": { "get": { "description": "Retrieve the plugin.json body and file listing from an artifact\nwithout installing it. Accepts OCI refs, git refs, or local tags.", "parameters": [ { "description": "OCI reference or local build tag", "in": "query", "name": "ref", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.PluginContent" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "401": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Unauthorized (registry refused credentials)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (artifact not present in registry)" }, "429": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Too Many Requests (registry rate limit)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "502": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Gateway (upstream registry or git resolver failure)" }, "504": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Gateway Timeout (upstream pull timed out)" } }, "summary": "Get plugin content", "tags": ["plugins"] } }, "/api/v1beta/plugins/push": { "post": { "description": "Push a built plugin artifact to a remote registry", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.pushPluginRequest", "summary": "request", "description": "Push request" } ] } } }, "description": "Push request", "required": true }, "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Push a plugin", "tags": ["plugins"] } }, "/api/v1beta/plugins/sync": { "post": { "description": "Restore a project's installed plugins to match toolhive.lock.yaml", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.syncPluginsRequest", "summary": "request", "description": "Sync request" } ] } } }, "description": "Sync request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.SyncResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden (feature not enabled)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "501": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Implemented" } }, "summary": "Sync project plugins from the lock file", "tags": ["plugins"] } }, "/api/v1beta/plugins/upgrade": { "post": { "description": "Re-resolve a project's lock entries and install newer content where available", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.upgradePluginsRequest", "summary": "request", "description": "Upgrade request" } ] } } }, "description": "Upgrade request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.UpgradeResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden (feature not enabled)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (a requested name is not in the lock file)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "501": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Implemented" } }, "summary": "Upgrade project plugins", "tags": ["plugins"] } }, "/api/v1beta/plugins/validate": { "post": { "description": "Validate a plugin definition", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.validatePluginRequest", "summary": "request", "description": "Validate request" } ] } } }, "description": "Validate request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.ValidationResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Validate a plugin", "tags": ["plugins"] } }, "/api/v1beta/plugins/{name}": { "delete": { "description": "Remove an installed plugin", "parameters": [ { "description": "Plugin name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Scope to uninstall from (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Project root path for project-scoped plugins", "in": "query", "name": "project_root", "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Uninstall a plugin", "tags": ["plugins"] }, "get": { "description": "Get detailed information about a specific plugin", "parameters": [ { "description": "Plugin name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Filter by scope (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Project root path for project-scoped plugins", "in": "query", "name": "project_root", "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_plugins.PluginInfo" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Get plugin details", "tags": ["plugins"] } }, "/api/v1beta/registry": { "get": { "description": "Get a list of the current registries", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryListResponse" } } }, "description": "OK" } }, "summary": "List registries", "tags": ["registry"] }, "post": { "description": "Add a new registry", "requestBody": { "content": { "application/json": { "schema": { "type": "object" } } } }, "responses": { "501": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Implemented" } }, "summary": "Add a registry", "tags": ["registry"] } }, "/api/v1beta/registry/auth/login": { "post": { "description": "Trigger an interactive OAuth flow to authenticate with the configured registry. Only available in serve mode.", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": { "type": "string" }, "type": "object" } } }, "description": "Authenticated successfully" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request - Registry OAuth not configured" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Registry login", "tags": ["registry"] } }, "/api/v1beta/registry/auth/logout": { "post": { "description": "Clear cached OAuth tokens for the configured registry. Only available in serve mode.", "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": { "type": "string" }, "type": "object" } } }, "description": "Logged out successfully" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request - Registry OAuth not configured" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Registry logout", "tags": ["registry"] } }, "/api/v1beta/registry/{name}": { "delete": { "description": "Remove a specific registry", "parameters": [ { "description": "Registry name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden - blocked by policy" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Remove a registry", "tags": ["registry"] }, "get": { "description": "Get details of a specific registry", "parameters": [ { "description": "Registry name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.getRegistryResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Get a registry", "tags": ["registry"] }, "put": { "description": "Update registry URL or local path for the default registry", "parameters": [ { "description": "Registry name (must be 'default')", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.UpdateRegistryRequest", "summary": "body", "description": "Registry configuration" } ] } } }, "description": "Registry configuration", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.UpdateRegistryResponse" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden - blocked by policy" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "502": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Gateway - Registry validation failed" }, "504": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Gateway Timeout - Registry unreachable" } }, "summary": "Update registry configuration", "tags": ["registry"] } }, "/api/v1beta/registry/{name}/refresh": { "post": { "description": "Force a refresh of the server-side registry cache for the default registry", "parameters": [ { "description": "Registry name (must be 'default')", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "additionalProperties": { "type": "string" }, "type": "object" } } }, "description": "Registry refreshed" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "Refresh registry cache", "tags": ["registry"] } }, "/api/v1beta/registry/{name}/servers": { "get": { "description": "Get a list of servers in a specific registry", "parameters": [ { "description": "Registry name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.listServersResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "List servers in a registry", "tags": ["registry"] } }, "/api/v1beta/registry/{name}/servers/{serverName}": { "get": { "description": "Get details of a specific server in a registry", "parameters": [ { "description": "Registry name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "ImageMetadata name", "in": "path", "name": "serverName", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.getServerResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Get a server from a registry", "tags": ["registry"] } }, "/api/v1beta/secrets": { "post": { "description": "Setup the secrets provider with the specified type and configuration.", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.setupSecretsRequest", "summary": "request", "description": "Setup secrets provider request" } ] } } }, "description": "Setup secrets provider request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.setupSecretsResponse" } } }, "description": "Created" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Setup or reconfigure secrets provider", "tags": ["secrets"] } }, "/api/v1beta/secrets/default": { "get": { "description": "Get details of the default secrets provider", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.getSecretsProviderResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found - Provider not setup" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Get secrets provider details", "tags": ["secrets"] } }, "/api/v1beta/secrets/default/keys": { "get": { "description": "Get a list of all secret keys from the default provider", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.listSecretsResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found - Provider not setup" }, "405": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Method Not Allowed - Provider doesn't support listing" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List secrets", "tags": ["secrets"] }, "post": { "description": "Create a new secret in the default provider (encrypted provider only)", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.createSecretRequest", "summary": "request", "description": "Create secret request" } ] } } }, "description": "Create secret request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.createSecretResponse" } } }, "description": "Created" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found - Provider not setup" }, "405": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Method Not Allowed - Provider doesn't support writing" }, "409": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Conflict - Secret already exists" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Create a new secret", "tags": ["secrets"] } }, "/api/v1beta/secrets/default/keys/{key}": { "delete": { "description": "Delete a secret from the default provider (encrypted provider only)", "parameters": [ { "description": "Secret key", "in": "path", "name": "key", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found - Provider not setup or secret not found" }, "405": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Method Not Allowed - Provider doesn't support deletion" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Delete a secret", "tags": ["secrets"] }, "put": { "description": "Update an existing secret in the default provider (encrypted provider only)", "parameters": [ { "description": "Secret key", "in": "path", "name": "key", "required": true, "schema": { "type": "string" } } ], "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.updateSecretRequest", "summary": "request", "description": "Update secret request" } ] } } }, "description": "Update secret request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.updateSecretResponse" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found - Provider not setup or secret not found" }, "405": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Method Not Allowed - Provider doesn't support writing" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Update a secret", "tags": ["secrets"] } }, "/api/v1beta/skills": { "get": { "description": "Get a list of all installed skills", "parameters": [ { "description": "Filter by scope (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Filter by client app", "in": "query", "name": "client", "schema": { "type": "string" } }, { "description": "Filter by project root path", "in": "query", "name": "project_root", "schema": { "type": "string" } }, { "description": "Filter by group name", "in": "query", "name": "group", "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.skillListResponse" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List all installed skills", "tags": ["skills"] }, "post": { "description": "Install a skill from a remote source", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.installSkillRequest", "summary": "request", "description": "Install request" } ] } } }, "description": "Install request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.installSkillResponse" } } }, "description": "Created", "headers": { "Location": { "description": "URI of the installed skill resource", "schema": { "type": "string" } } } }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "401": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Unauthorized (registry refused credentials)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (artifact not present in registry)" }, "409": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Conflict" }, "429": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Too Many Requests (registry rate limit)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "502": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Gateway (upstream registry failure)" }, "504": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Gateway Timeout (upstream pull timed out)" } }, "summary": "Install a skill", "tags": ["skills"] } }, "/api/v1beta/skills/build": { "post": { "description": "Build a skill from a local directory", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.buildSkillRequest", "summary": "request", "description": "Build request" } ] } } }, "description": "Build request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.BuildResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Build a skill", "tags": ["skills"] } }, "/api/v1beta/skills/builds": { "get": { "description": "Get a list of all locally-built OCI skill artifacts in the local store", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.buildListResponse" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "List locally-built skill artifacts", "tags": ["skills"] } }, "/api/v1beta/skills/builds/{tag}": { "delete": { "description": "Remove a locally-built OCI skill artifact and its blobs from the local store", "parameters": [ { "description": "Artifact tag", "in": "path", "name": "tag", "required": true, "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Delete a locally-built skill artifact", "tags": ["skills"] } }, "/api/v1beta/skills/content": { "get": { "description": "Retrieve the SKILL.md body and file listing from an artifact\nwithout installing it. Accepts OCI refs, git refs, or local tags.", "parameters": [ { "description": "OCI reference or local build tag", "in": "query", "name": "ref", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SkillContent" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "401": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Unauthorized (registry refused credentials)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (artifact not present in registry)" }, "429": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Too Many Requests (registry rate limit)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "502": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Gateway (upstream registry or git resolver failure)" }, "504": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Gateway Timeout (upstream pull timed out)" } }, "summary": "Get skill content", "tags": ["skills"] } }, "/api/v1beta/skills/push": { "post": { "description": "Push a built skill artifact to a remote registry", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.pushSkillRequest", "summary": "request", "description": "Push request" } ] } } }, "description": "Push request", "required": true }, "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Push a skill", "tags": ["skills"] } }, "/api/v1beta/skills/sync": { "post": { "description": "Restore a project's installed skills to match toolhive.lock.yaml", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.syncSkillsRequest", "summary": "request", "description": "Sync request" } ] } } }, "description": "Sync request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SyncResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden (feature not enabled)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "501": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Implemented" } }, "summary": "Sync project skills from the lock file", "tags": ["skills"] } }, "/api/v1beta/skills/upgrade": { "post": { "description": "Re-resolve a project's lock entries and install newer content where available", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.upgradeSkillsRequest", "summary": "request", "description": "Upgrade request" } ] } } }, "description": "Upgrade request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.UpgradeResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "403": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Forbidden (feature not enabled)" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found (a requested name is not in the lock file)" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" }, "501": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Implemented" } }, "summary": "Upgrade project skills", "tags": ["skills"] } }, "/api/v1beta/skills/validate": { "post": { "description": "Validate a skill definition", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.validateSkillRequest", "summary": "request", "description": "Validate request" } ] } } }, "description": "Validate request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.ValidationResult" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Validate a skill", "tags": ["skills"] } }, "/api/v1beta/skills/{name}": { "delete": { "description": "Remove an installed skill", "parameters": [ { "description": "Skill name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Scope to uninstall from (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Project root path for project-scoped skills", "in": "query", "name": "project_root", "schema": { "type": "string" } } ], "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Uninstall a skill", "tags": ["skills"] }, "get": { "description": "Get detailed information about a specific skill", "parameters": [ { "description": "Skill name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } }, { "description": "Filter by scope (user or project)", "in": "query", "name": "scope", "schema": { "enum": ["user", "project"], "type": "string" } }, { "description": "Project root path for project-scoped skills", "in": "query", "name": "project_root", "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_skills.SkillInfo" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" }, "500": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Internal Server Error" } }, "summary": "Get skill details", "tags": ["skills"] } }, "/api/v1beta/version": { "get": { "description": "Returns the current version of the server", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.versionResponse" } } }, "description": "OK" } }, "summary": "Get server version", "tags": ["version"] } }, "/api/v1beta/workloads": { "get": { "description": "Get a list of all running workloads, optionally filtered by group", "parameters": [ { "description": "List all workloads, including stopped ones", "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.workloadListResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Group not found" } }, "summary": "List all workloads", "tags": ["workloads"] }, "post": { "description": "Create and start a new workload\nruntime_config is only accepted for protocol-scheme images\n(uvx://, npx://, go://); supplying it with an ordinary image\nreference or a remote url is rejected with 400.", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.createRequest", "summary": "request", "description": "Create workload request" } ] } } }, "description": "Create workload request", "required": true }, "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.createWorkloadResponse" } } }, "description": "Created" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "409": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Conflict" } }, "summary": "Create a new workload", "tags": ["workloads"] } }, "/api/v1beta/workloads/delete": { "post": { "description": "Delete multiple workloads by name or by group asynchronously.\nReturns 202 Accepted immediately. Deletion happens in the background.", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.bulkOperationRequest", "summary": "request", "description": "Bulk delete request (names or group)" } ] } } }, "description": "Bulk delete request (names or group)", "required": true }, "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted - deletion started" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" } }, "summary": "Delete workloads in bulk", "tags": ["workloads"] } }, "/api/v1beta/workloads/restart": { "post": { "description": "Restart multiple workloads by name or by group", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.bulkOperationRequest", "summary": "request", "description": "Bulk restart request (names or group)" } ] } } }, "description": "Bulk restart request (names or group)", "required": true }, "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" } }, "summary": "Restart workloads in bulk", "tags": ["workloads"] } }, "/api/v1beta/workloads/stop": { "post": { "description": "Stop multiple workloads by name or by group", "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object" }, { "$ref": "#/components/schemas/pkg_api_v1.bulkOperationRequest", "summary": "request", "description": "Bulk stop request (names or group)" } ] } } }, "description": "Bulk stop request (names or group)", "required": true }, "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" } }, "summary": "Stop workloads in bulk", "tags": ["workloads"] } }, "/api/v1beta/workloads/upgrade-check": { "get": { "description": "Check all workloads (optionally filtered by group) for newer\nimages available in their source registries. This is an offline\nmetadata comparison; it does not pull images. Secret values are\nnever 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"] } }, "/api/v1beta/workloads/{name}": { "delete": { "description": "Delete a workload asynchronously. Returns 202 Accepted immediately.\nThe deletion happens in the background. Poll the workload list to confirm deletion.", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted - deletion started" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Delete a workload", "tags": ["workloads"] }, "get": { "description": "Get details of a specific workload", "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.createRequest" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Get workload details", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/edit": { "post": { "description": "Update an existing workload configuration\nruntime_config on a non-protocol-scheme image is accepted only when it\nexactly matches the workload's persisted config and the image and url\nare unchanged (an inert echo, e.g. from a prior GET); otherwise it is\nrejected with 400.", "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.updateRequest", "summary": "request", "description": "Update workload request" } ] } } }, "description": "Update workload request", "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.createWorkloadResponse" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Update workload", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/export": { "get": { "description": "Export a workload's run configuration as JSON", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/github_com_stacklok_toolhive_pkg_runner.RunConfig" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Export workload configuration", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/logs": { "get": { "description": "Retrieve at most 1000 lines of logs for a specific workload by name.", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Logs for the specified workload" }, "400": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Invalid workload name" }, "404": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Get logs for a specific workload", "tags": ["logs"] } }, "/api/v1beta/workloads/{name}/proxy-logs": { "get": { "description": "Retrieve at most 1000 lines of proxy logs for a specific workload by name from the file system.", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Proxy logs for the specified workload" }, "400": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Invalid workload name" }, "404": { "content": { "text/plain": { "schema": { "type": "string" } } }, "description": "Proxy logs not found for workload" } }, "summary": "Get proxy logs for a specific workload", "tags": ["logs"] } }, "/api/v1beta/workloads/{name}/restart": { "post": { "description": "Restart a running workload", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Restart a workload", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/status": { "get": { "description": "Get the current status of a specific workload", "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.workloadStatusResponse" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Get workload status", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/stop": { "post": { "description": "Stop a running workload", "parameters": [ { "description": "Workload name", "in": "path", "name": "name", "required": true, "schema": { "type": "string" } } ], "responses": { "202": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Accepted" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Bad Request" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "Not Found" } }, "summary": "Stop a workload", "tags": ["workloads"] } }, "/api/v1beta/workloads/{name}/upgrade": { "post": { "description": "Apply a registry-sourced upgrade to a single workload. This\nre-resolves and verifies the candidate image, pulls it, and only\nthen recreates the workload with the new image, preserving the\nexisting configuration. If the workload is already up to date or\nis not registry-sourced, the current check result is returned\nunchanged (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", "summary": "request", "description": "Upgrade options" } ] } } }, "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\nits source registry. This is an offline metadata comparison; it\ndoes 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"] } }, "/health": { "get": { "description": "Check if the API is healthy", "responses": { "204": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "No Content" } }, "summary": "Health check", "tags": ["system"] } }, "/registry/{registryName}/v0.1/servers": { "get": { "description": "Get a paginated list of servers from the registry. Supports optional full-text search and pagination.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Search filter — matches against server name and description", "in": "query", "name": "q", "schema": { "type": "string" } }, { "description": "Page number, 1-based (default: 1)", "in": "query", "name": "page", "schema": { "type": "integer" } }, { "description": "Items per page, max 200 (default: 50)", "in": "query", "name": "limit", "schema": { "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.serversV01Response" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "List available registry servers", "tags": ["registry-servers"] } }, "/registry/{registryName}/v0.1/servers/{serverName}/versions/latest": { "get": { "description": "Retrieve a single server by name. Names use reverse-DNS format; URL-encode slashes.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Server name (URL-encoded reverse-DNS format)", "in": "path", "name": "serverName", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/v0.ServerJSON" } } }, "description": "OK" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Invalid server name encoding" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Server not found" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "Get a registry server", "tags": ["registry-servers"] } }, "/registry/{registryName}/v0.1/x/dev.toolhive/plugins": { "get": { "description": "Get a paginated list of plugins from the registry. Supports optional full-text search and pagination.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Search filter — matches against plugin name, namespace, and description", "in": "query", "name": "q", "schema": { "type": "string" } }, { "description": "Page number, 1-based (default: 1)", "in": "query", "name": "page", "schema": { "type": "integer" } }, { "description": "Items per page, max 200 (default: 50)", "in": "query", "name": "limit", "schema": { "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.pluginsV01Response" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "List available registry plugins", "tags": ["registry-plugins"] } }, "/registry/{registryName}/v0.1/x/dev.toolhive/plugins/{namespace}/{pluginName}": { "get": { "description": "Retrieve a single plugin by its namespace and name from the registry.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Plugin namespace in reverse-DNS format (e.g. io.github.stacklok)", "in": "path", "name": "namespace", "required": true, "schema": { "type": "string" } }, { "description": "Plugin name", "in": "path", "name": "pluginName", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/registry.Plugin" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Plugin not found" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "Get a registry plugin", "tags": ["registry-plugins"] } }, "/registry/{registryName}/v0.1/x/dev.toolhive/skills": { "get": { "description": "Get a paginated list of skills from the registry. Supports optional full-text search and pagination.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Search filter — matches against skill name, namespace, and description", "in": "query", "name": "q", "schema": { "type": "string" } }, { "description": "Page number, 1-based (default: 1)", "in": "query", "name": "page", "schema": { "type": "integer" } }, { "description": "Items per page, max 200 (default: 50)", "in": "query", "name": "limit", "schema": { "type": "integer" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.skillsV01Response" } } }, "description": "OK" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "List available registry skills", "tags": ["registry-skills"] } }, "/registry/{registryName}/v0.1/x/dev.toolhive/skills/{namespace}/{skillName}": { "get": { "description": "Retrieve a single skill by its namespace and name from the registry.", "parameters": [ { "description": "Registry name (currently ignored, uses the default provider)", "in": "path", "name": "registryName", "required": true, "schema": { "type": "string" } }, { "description": "Skill namespace in reverse-DNS format (e.g. io.github.stacklok)", "in": "path", "name": "namespace", "required": true, "schema": { "type": "string" } }, { "description": "Skill name", "in": "path", "name": "skillName", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/registry.Skill" } } }, "description": "OK" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Skill not found" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Internal server error" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/pkg_api_v1.registryErrorResponse" } } }, "description": "Registry authentication required or upstream registry unavailable" } }, "summary": "Get a registry skill", "tags": ["registry-skills"] } } }, "openapi": "3.1.0" }