From 29e36759cb5c339b36ccf51533de80045b0a1db4 Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Wed, 22 Oct 2025 18:46:56 +0200 Subject: [PATCH 1/7] regenerate api client --- api/generated/types.gen.ts | 5 + api/openapi.json | 3149 +----------------------------------- 2 files changed, 10 insertions(+), 3144 deletions(-) diff --git a/api/generated/types.gen.ts b/api/generated/types.gen.ts index 1395e3262..d8dab2afb 100644 --- a/api/generated/types.gen.ts +++ b/api/generated/types.gen.ts @@ -234,6 +234,11 @@ export type PermissionsInboundNetworkPermissions = { */ export type PermissionsNetworkPermissions = { inbound?: PermissionsInboundNetworkPermissions + /** + * Mode specifies the network mode for the container (e.g., "host", "bridge", "none") + * When empty, the default container runtime network mode is used + */ + mode?: string outbound?: PermissionsOutboundNetworkPermissions } diff --git a/api/openapi.json b/api/openapi.json index d2cfc5c63..77d2569c5 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -1,3146 +1,7 @@ { - "components": { - "schemas": { - "audit.Config": { - "description": "AuditConfig contains the audit logging configuration", - "properties": { - "component": { - "description": "Component is the component name to use in audit events", - "type": "string" - }, - "event_types": { - "description": "EventTypes specifies which event types to audit. If empty, all events are audited.", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "exclude_event_types": { - "description": "ExcludeEventTypes specifies which event types to exclude from auditing.\nThis takes precedence over EventTypes.", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "include_request_data": { - "description": "IncludeRequestData determines whether to include request data in audit logs", - "type": "boolean" - }, - "include_response_data": { - "description": "IncludeResponseData determines whether to include response data in audit logs", - "type": "boolean" - }, - "log_file": { - "description": "LogFile specifies the file path for audit logs. If empty, logs to stdout.", - "type": "string" - }, - "max_data_size": { - "description": "MaxDataSize limits the size of request/response data included in audit logs (in bytes)", - "type": "integer" - } - }, - "type": "object" - }, - "auth.TokenValidatorConfig": { - "description": "OIDCConfig 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" - }, - "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" - } - }, - "type": "object" - }, - "authz.CedarConfig": { - "description": "Cedar is the Cedar-specific configuration.\nThis is only used when Type is ConfigTypeCedarV1.", - "properties": { - "entities_json": { - "description": "EntitiesJSON is the JSON string representing Cedar entities", - "type": "string" - }, - "policies": { - "description": "Policies is a list of Cedar policy strings", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "authz.Config": { - "description": "AuthzConfig contains the authorization configuration", - "properties": { - "cedar": { "$ref": "#/components/schemas/authz.CedarConfig" }, - "type": { "$ref": "#/components/schemas/authz.ConfigType" }, - "version": { - "description": "Version is the version of the configuration format.", - "type": "string" - } - }, - "type": "object" - }, - "authz.ConfigType": { - "description": "Type is the type of authorization configuration.", - "type": "string", - "x-enum-varnames": ["ConfigTypeCedarV1"] - }, - "client.MCPClient": { - "type": "string", - "x-enum-varnames": [ - "RooCode", - "Cline", - "Cursor", - "VSCodeInsider", - "VSCode", - "ClaudeCode", - "Windsurf", - "WindsurfJetBrains", - "AmpCli", - "AmpVSCode", - "AmpCursor", - "AmpVSCodeInsider", - "AmpWindsurf", - "LMStudio", - "Goose", - "Trae", - "Continue" - ] - }, - "client.MCPClientStatus": { - "properties": { - "client_type": { - "description": "ClientType is the type of MCP client", - "type": "string", - "x-enum-varnames": [ - "RooCode", - "Cline", - "Cursor", - "VSCodeInsider", - "VSCode", - "ClaudeCode", - "Windsurf", - "WindsurfJetBrains", - "AmpCli", - "AmpVSCode", - "AmpCursor", - "AmpVSCodeInsider", - "AmpWindsurf", - "LMStudio", - "Goose", - "Trae", - "Continue" - ] - }, - "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" - } - }, - "type": "object" - }, - "client.RegisteredClient": { - "properties": { - "groups": { - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "name": { "$ref": "#/components/schemas/client.MCPClient" } - }, - "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" - }, - "status": { "$ref": "#/components/schemas/runtime.WorkloadStatus" }, - "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" - }, - "tool_type": { - "description": "ToolType is the type of tool this workload represents.\nFor now, it will always be \"mcp\" - representing an MCP server.", - "type": "string" - }, - "tools": { - "description": "ToolsFilter is the filter on tools applied to the workload.", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "transport_type": { - "$ref": "#/components/schemas/types.TransportType" - }, - "url": { - "description": "URL is the URL of the workload exposed by the ToolHive proxy.", - "type": "string" - } - }, - "type": "object" - }, - "groups.Group": { - "properties": { - "name": { "type": "string" }, - "registered_clients": { - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "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" - }, - "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" - }, - "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": "PermissionProfile is the permission profile to use", - "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" - }, - "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" - }, - "permissions": { "$ref": "#/components/schemas/permissions.Profile" }, - "provenance": { "$ref": "#/components/schemas/registry.Provenance" }, - "repository_url": { - "description": "RepositoryURL is the URL to the source code repository for the server", - "type": "string" - }, - "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" - }, - "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.Metadata": { - "description": "Metadata contains additional information about the server such as popularity metrics", - "properties": { - "last_updated": { - "description": "LastUpdated is the timestamp when the server was last updated, in RFC3339 format", - "type": "string" - }, - "pulls": { - "description": "Pulls indicates how many times the server image has been downloaded", - "type": "integer" - }, - "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" - }, - "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.Provenance": { - "description": "Provenance contains verification and signing metadata", - "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" - }, - "repository_url": { - "description": "RepositoryURL is the URL to the source code repository for the server", - "type": "string" - }, - "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" - }, - "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.VerifiedAttestation": { - "properties": { - "predicate": {}, - "predicate_type": { "type": "string" } - }, - "type": "object" - }, - "runner.RemoteAuthConfig": { - "description": "RemoteAuthConfig contains OAuth configuration for remote MCP servers", - "properties": { - "authorize_url": { "type": "string" }, - "callback_port": { "type": "integer" }, - "client_id": { "type": "string" }, - "client_secret": { "type": "string" }, - "client_secret_file": { "type": "string" }, - "env_vars": { - "description": "Environment variables for the client", - "items": { "$ref": "#/components/schemas/registry.EnvVar" }, - "type": "array", - "uniqueItems": false - }, - "headers": { - "description": "Headers for HTTP requests", - "items": { "$ref": "#/components/schemas/registry.Header" }, - "type": "array", - "uniqueItems": false - }, - "issuer": { - "description": "OAuth endpoint configuration (from registry)", - "type": "string" - }, - "oauth_params": { - "additionalProperties": { "type": "string" }, - "description": "OAuth parameters for server-specific customization", - "type": "object" - }, - "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" - }, - "runner.RunConfig": { - "properties": { - "audit_config": { "$ref": "#/components/schemas/audit.Config" }, - "audit_config_path": { - "description": "AuditConfigPath is the path to the audit configuration file", - "type": "string" - }, - "authz_config": { "$ref": "#/components/schemas/authz.Config" }, - "authz_config_path": { - "description": "AuthzConfigPath is the path to the authorization configuration file", - "type": "string" - }, - "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" - }, - "env_file_dir": { - "description": "EnvFileDir 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" - }, - "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": "JWKSAuthTokenFile 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" - }, - "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 - }, - "name": { - "description": "Name is the name of the MCP server", - "type": "string" - }, - "oidc_config": { - "$ref": "#/components/schemas/auth.TokenValidatorConfig" - }, - "permission_profile": { - "$ref": "#/components/schemas/permissions.Profile" - }, - "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": { "$ref": "#/components/schemas/types.ProxyMode" }, - "remote_auth_config": { - "$ref": "#/components/schemas/runner.RemoteAuthConfig" - }, - "remote_url": { - "description": "RemoteURL is the URL of the remote MCP server (if running remotely)", - "type": "string" - }, - "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 - }, - "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": "ThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations", - "type": "string" - }, - "tools_filter": { - "description": "ToolsFilter is the list of tools to filter", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "tools_override": { - "additionalProperties": { - "$ref": "#/components/schemas/runner.ToolOverride" - }, - "description": "ToolsOverride 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)", - "type": "string", - "x-enum-varnames": [ - "TransportTypeStdio", - "TransportTypeSSE", - "TransportTypeStreamableHTTP", - "TransportTypeInspector" - ] - }, - "trust_proxy_headers": { - "description": "TrustProxyHeaders indicates whether to trust X-Forwarded-* headers from reverse proxies", - "type": "boolean" - }, - "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" - }, - "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" - }, - "runtime.WorkloadStatus": { - "description": "Status is the current status of the workload.", - "type": "string", - "x-enum-varnames": [ - "WorkloadStatusRunning", - "WorkloadStatusStopped", - "WorkloadStatusError", - "WorkloadStatusStarting", - "WorkloadStatusStopping", - "WorkloadStatusUnhealthy", - "WorkloadStatusRemoving", - "WorkloadStatusUnknown" - ] - }, - "secrets.SecretParameter": { - "properties": { - "name": { "type": "string" }, - "target": { "type": "string" } - }, - "type": "object" - }, - "telemetry.Config": { - "description": "TelemetryConfig contains the OpenTelemetry configuration", - "properties": { - "enablePrometheusMetricsPath": { - "description": "EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint\nThe metrics are served on the main transport port at /metrics\nThis is separate from OTLP metrics which are sent to the Endpoint", - "type": "boolean" - }, - "endpoint": { - "description": "Endpoint is the OTLP endpoint URL", - "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: []string{\"NODE_ENV\", \"DEPLOYMENT_ENV\", \"SERVICE_VERSION\"}", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "headers": { - "additionalProperties": { "type": "string" }, - "description": "Headers contains authentication headers for the OTLP endpoint", - "type": "object" - }, - "insecure": { - "description": "Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint", - "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", - "type": "boolean" - }, - "samplingRate": { - "description": "SamplingRate is the trace sampling rate (0.0-1.0)\nOnly used when TracingEnabled is true", - "type": "number" - }, - "serviceName": { - "description": "ServiceName is the service name for telemetry", - "type": "string" - }, - "serviceVersion": { - "description": "ServiceVersion is the service version for telemetry", - "type": "string" - }, - "tracingEnabled": { - "description": "TracingEnabled controls whether distributed tracing is enabled\nWhen false, no tracer provider is created even if an endpoint is configured", - "type": "boolean" - } - }, - "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.ProxyMode": { - "description": "ProxyMode is the proxy mode for stdio transport (\"sse\" or \"streamable-http\")", - "type": "string", - "x-enum-varnames": ["ProxyModeSSE", "ProxyModeStreamableHTTP"] - }, - "types.TransportType": { - "description": "TransportType is the type of transport used for this workload.", - "type": "string", - "x-enum-varnames": [ - "TransportTypeStdio", - "TransportTypeSSE", - "TransportTypeStreamableHTTP", - "TransportTypeInspector" - ] - }, - "v1.RegistryType": { - "description": "Type of registry (file, url, or default)", - "type": "string", - "x-enum-varnames": [ - "RegistryTypeFile", - "RegistryTypeURL", - "RegistryTypeDefault" - ] - }, - "v1.UpdateRegistryRequest": { - "description": "Request containing registry configuration updates", - "properties": { - "allow_private_ip": { - "description": "Allow private IP addresses for registry URL", - "type": "boolean" - }, - "local_path": { - "description": "Local registry file path", - "type": "string" - }, - "url": { - "description": "Registry URL (for remote registries)", - "type": "string" - } - }, - "type": "object" - }, - "v1.UpdateRegistryResponse": { - "description": "Response containing update result", - "properties": { - "message": { "description": "Status message", "type": "string" }, - "type": { - "description": "Registry type after update", - "type": "string" - } - }, - "type": "object" - }, - "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": { - "type": "string", - "x-enum-varnames": [ - "RooCode", - "Cline", - "Cursor", - "VSCodeInsider", - "VSCode", - "ClaudeCode", - "Windsurf", - "WindsurfJetBrains", - "AmpCli", - "AmpVSCode", - "AmpCursor", - "AmpVSCodeInsider", - "AmpWindsurf", - "LMStudio", - "Goose", - "Trae", - "Continue" - ] - }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "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" - }, - "v1.clientStatusResponse": { - "properties": { - "clients": { - "items": { "$ref": "#/components/schemas/client.MCPClientStatus" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "v1.createClientRequest": { - "properties": { - "groups": { - "description": "Groups is the list of groups configured on the client.", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "name": { - "description": "Name is the type of the client to register.", - "type": "string", - "x-enum-varnames": [ - "RooCode", - "Cline", - "Cursor", - "VSCodeInsider", - "VSCode", - "ClaudeCode", - "Windsurf", - "WindsurfJetBrains", - "AmpCli", - "AmpVSCode", - "AmpCursor", - "AmpVSCodeInsider", - "AmpWindsurf", - "LMStudio", - "Goose", - "Trae", - "Continue" - ] - } - }, - "type": "object" - }, - "v1.createClientResponse": { - "properties": { - "groups": { - "description": "Groups is the list of groups configured on the client.", - "items": { "type": "string" }, - "type": "array", - "uniqueItems": false - }, - "name": { - "description": "Name is the type of the client that was registered.", - "type": "string", - "x-enum-varnames": [ - "RooCode", - "Cline", - "Cursor", - "VSCodeInsider", - "VSCode", - "ClaudeCode", - "Windsurf", - "WindsurfJetBrains", - "AmpCli", - "AmpVSCode", - "AmpCursor", - "AmpVSCodeInsider", - "AmpWindsurf", - "LMStudio", - "Goose", - "Trae", - "Continue" - ] - } - }, - "type": "object" - }, - "v1.createGroupRequest": { - "properties": { - "name": { - "description": "Name of the group to create", - "type": "string" - } - }, - "type": "object" - }, - "v1.createGroupResponse": { - "properties": { - "name": { - "description": "Name of the created group", - "type": "string" - } - }, - "type": "object" - }, - "v1.createRequest": { - "description": "Request to create a new workload", - "properties": { - "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" - }, - "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.", - "type": "boolean" - }, - "oauth_config": { - "$ref": "#/components/schemas/v1.remoteOAuthConfig" - }, - "oidc": { "$ref": "#/components/schemas/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" - }, - "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/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" - }, - "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" - }, - "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" - }, - "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" - }, - "v1.getRegistryResponse": { - "description": "Response containing registry details", - "properties": { - "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": { - "description": "Type of registry (file, url, or default)", - "type": "string", - "x-enum-varnames": [ - "RegistryTypeFile", - "RegistryTypeURL", - "RegistryTypeDefault" - ] - }, - "version": { - "description": "Version of the registry schema", - "type": "string" - } - }, - "type": "object" - }, - "v1.getSecretsProviderResponse": { - "description": "Response containing secrets provider details", - "properties": { - "capabilities": { - "$ref": "#/components/schemas/v1.providerCapabilitiesResponse" - }, - "name": { - "description": "Name of the secrets provider", - "type": "string" - }, - "provider_type": { - "description": "Type of the secrets provider", - "type": "string" - } - }, - "type": "object" - }, - "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" - }, - "v1.groupListResponse": { - "properties": { - "groups": { - "description": "List of groups", - "items": { "$ref": "#/components/schemas/groups.Group" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "v1.listSecretsResponse": { - "description": "Response containing a list of secret keys", - "properties": { - "keys": { - "description": "List of secret keys", - "items": { "$ref": "#/components/schemas/v1.secretKeyResponse" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "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" - }, - "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" - } - }, - "type": "object" - }, - "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" - }, - "v1.registryInfo": { - "description": "Basic information about a registry", - "properties": { - "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/v1.RegistryType" }, - "version": { - "description": "Version of the registry schema", - "type": "string" - } - }, - "type": "object" - }, - "v1.registryListResponse": { - "description": "Response containing a list of registries", - "properties": { - "registries": { - "description": "List of registries", - "items": { "$ref": "#/components/schemas/v1.registryInfo" }, - "type": "array", - "uniqueItems": false - } - }, - "type": "object" - }, - "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" - }, - "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" - }, - "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" - }, - "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" - }, - "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, none)", - "type": "string" - } - }, - "type": "object" - }, - "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" - }, - "v1.toolOverride": { - "description": "Tool override", - "properties": { - "description": { - "description": "Description of the tool", - "type": "string" - }, - "name": { "description": "Name of the tool", "type": "string" } - }, - "type": "object" - }, - "v1.updateRequest": { - "description": "Request to update an existing workload (name cannot be changed)", - "properties": { - "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" - }, - "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.", - "type": "boolean" - }, - "oauth_config": { - "$ref": "#/components/schemas/v1.remoteOAuthConfig" - }, - "oidc": { "$ref": "#/components/schemas/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" - }, - "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/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" - }, - "v1.updateSecretRequest": { - "description": "Request to update an existing secret", - "properties": { - "value": { "description": "New secret value", "type": "string" } - }, - "type": "object" - }, - "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" - }, - "v1.versionResponse": { - "properties": { "version": { "type": "string" } }, - "type": "object" - }, - "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" - }, - "v1.workloadStatusResponse": { - "description": "Response containing workload status information", - "properties": { - "status": { - "description": "Current status of the workload", - "type": "string", - "x-enum-varnames": [ - "WorkloadStatusRunning", - "WorkloadStatusStopped", - "WorkloadStatusError", - "WorkloadStatusStarting", - "WorkloadStatusStopping", - "WorkloadStatusUnhealthy", - "WorkloadStatusRemoving", - "WorkloadStatusUnknown" - ] - } - }, - "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/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": { - "$ref": "#/components/schemas/v1.createClientRequest" - } - } - }, - "description": "Client to register", - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/v1.createClientResponse" - } - } - }, - "description": "OK" - }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Invalid request" - } - }, - "summary": "Register a new client", - "tags": ["clients"] - } - }, - "/api/v1beta/clients/register": { - "post": { - "description": "Register multiple clients with ToolHive", - "requestBody": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/v1.bulkClientRequest" } - } - }, - "description": "Clients to register", - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/v1.createClientResponse" - }, - "type": "array" - } - } - }, - "description": "OK" - }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Invalid request" - } - }, - "summary": "Register multiple clients", - "tags": ["clients"] - } - }, - "/api/v1beta/clients/unregister": { - "post": { - "description": "Unregister multiple clients from ToolHive", - "requestBody": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/v1.bulkClientRequest" } - } - }, - "description": "Clients to unregister", - "required": true - }, - "responses": { - "204": { "description": "No Content" }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Invalid request" - } - }, - "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" - } - }, - "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" - }, - "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", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/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": { "$ref": "#/components/schemas/v1.createGroupRequest" } - } - }, - "description": "Group creation request", - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/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/registry": { - "get": { - "description": "Get a list of the current registries", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/{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" - }, - "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/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": { - "$ref": "#/components/schemas/v1.UpdateRegistryRequest" - } - } - }, - "description": "Registry configuration", - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/v1.UpdateRegistryResponse" - } - } - }, - "description": "OK" - }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Bad Request" - }, - "404": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Not Found" - } - }, - "summary": "Update registry configuration", - "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/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/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": { - "$ref": "#/components/schemas/v1.setupSecretsRequest" - } - } - }, - "description": "Setup secrets provider request", - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/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/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": { - "$ref": "#/components/schemas/v1.createSecretRequest" - } - } - }, - "description": "Create secret request", - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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": { - "$ref": "#/components/schemas/v1.updateSecretRequest" - } - } - }, - "description": "Update secret request", - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/version": { - "get": { - "description": "Returns the current version of the server", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/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/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", - "requestBody": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/v1.createRequest" } - } - }, - "description": "Create workload request", - "required": true - }, - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/v1.bulkOperationRequest" - } - } - }, - "description": "Bulk delete 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": "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": { - "$ref": "#/components/schemas/v1.bulkOperationRequest" - } - } - }, - "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": { - "$ref": "#/components/schemas/v1.bulkOperationRequest" - } - } - }, - "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/{name}": { - "delete": { - "description": "Delete a 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": "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/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", - "parameters": [ - { - "description": "Workload name", - "in": "path", - "name": "name", - "required": true, - "schema": { "type": "string" } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/v1.updateRequest" } - } - }, - "description": "Update workload request", - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/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/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 100 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": { - "application/json": { "schema": { "type": "string" } }, - "text/plain": { "schema": { "type": "string" } } - }, - "description": "Logs for the specified workload" - }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Invalid workload name" - }, - "404": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Not Found" - } - }, - "summary": "Get logs for a specific workload", - "tags": ["logs"] - } - }, - "/api/v1beta/workloads/{name}/proxy-logs": { - "get": { - "description": "Retrieve 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": { - "application/json": { "schema": { "type": "string" } }, - "text/plain": { "schema": { "type": "string" } } - }, - "description": "Proxy logs for the specified workload" - }, - "400": { - "content": { - "application/json": { "schema": { "type": "string" } } - }, - "description": "Invalid workload name" - }, - "404": { - "content": { - "application/json": { "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/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"] - } - }, - "/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"] - } - } - }, - "openapi": "3.1.0" + "components": {"schemas":{"audit.Config":{"description":"AuditConfig contains the audit logging configuration","properties":{"component":{"description":"Component is the component name to use in audit events","type":"string"},"event_types":{"description":"EventTypes specifies which event types to audit. If empty, all events are audited.","items":{"type":"string"},"type":"array","uniqueItems":false},"exclude_event_types":{"description":"ExcludeEventTypes specifies which event types to exclude from auditing.\nThis takes precedence over EventTypes.","items":{"type":"string"},"type":"array","uniqueItems":false},"include_request_data":{"description":"IncludeRequestData determines whether to include request data in audit logs","type":"boolean"},"include_response_data":{"description":"IncludeResponseData determines whether to include response data in audit logs","type":"boolean"},"log_file":{"description":"LogFile specifies the file path for audit logs. If empty, logs to stdout.","type":"string"},"max_data_size":{"description":"MaxDataSize limits the size of request/response data included in audit logs (in bytes)","type":"integer"}},"type":"object"},"auth.TokenValidatorConfig":{"description":"OIDCConfig 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"},"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"}},"type":"object"},"authz.CedarConfig":{"description":"Cedar is the Cedar-specific configuration.\nThis is only used when Type is ConfigTypeCedarV1.","properties":{"entities_json":{"description":"EntitiesJSON is the JSON string representing Cedar entities","type":"string"},"policies":{"description":"Policies is a list of Cedar policy strings","items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"authz.Config":{"description":"AuthzConfig contains the authorization configuration","properties":{"cedar":{"$ref":"#/components/schemas/authz.CedarConfig"},"type":{"$ref":"#/components/schemas/authz.ConfigType"},"version":{"description":"Version is the version of the configuration format.","type":"string"}},"type":"object"},"authz.ConfigType":{"description":"Type is the type of authorization configuration.","type":"string","x-enum-varnames":["ConfigTypeCedarV1"]},"client.MCPClient":{"type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"client.MCPClientStatus":{"properties":{"client_type":{"description":"ClientType is the type of MCP client","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"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"}},"type":"object"},"client.RegisteredClient":{"properties":{"groups":{"items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"$ref":"#/components/schemas/client.MCPClient"}},"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"},"status":{"$ref":"#/components/schemas/runtime.WorkloadStatus"},"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"},"tool_type":{"description":"ToolType is the type of tool this workload represents.\nFor now, it will always be \"mcp\" - representing an MCP server.","type":"string"},"tools":{"description":"ToolsFilter is the filter on tools applied to the workload.","items":{"type":"string"},"type":"array","uniqueItems":false},"transport_type":{"$ref":"#/components/schemas/types.TransportType"},"url":{"description":"URL is the URL of the workload exposed by the ToolHive proxy.","type":"string"}},"type":"object"},"groups.Group":{"properties":{"name":{"type":"string"},"registered_clients":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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":"PermissionProfile is the permission profile to use","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"},"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"},"permissions":{"$ref":"#/components/schemas/permissions.Profile"},"provenance":{"$ref":"#/components/schemas/registry.Provenance"},"repository_url":{"description":"RepositoryURL is the URL to the source code repository for the server","type":"string"},"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"},"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.Metadata":{"description":"Metadata contains additional information about the server such as popularity metrics","properties":{"last_updated":{"description":"LastUpdated is the timestamp when the server was last updated, in RFC3339 format","type":"string"},"pulls":{"description":"Pulls indicates how many times the server image has been downloaded","type":"integer"},"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"},"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.Provenance":{"description":"Provenance contains verification and signing metadata","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"},"repository_url":{"description":"RepositoryURL is the URL to the source code repository for the server","type":"string"},"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"},"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.VerifiedAttestation":{"properties":{"predicate":{},"predicate_type":{"type":"string"}},"type":"object"},"runner.RemoteAuthConfig":{"description":"RemoteAuthConfig contains OAuth configuration for remote MCP servers","properties":{"authorize_url":{"type":"string"},"callback_port":{"type":"integer"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"client_secret_file":{"type":"string"},"env_vars":{"description":"Environment variables for the client","items":{"$ref":"#/components/schemas/registry.EnvVar"},"type":"array","uniqueItems":false},"headers":{"description":"Headers for HTTP requests","items":{"$ref":"#/components/schemas/registry.Header"},"type":"array","uniqueItems":false},"issuer":{"description":"OAuth endpoint configuration (from registry)","type":"string"},"oauth_params":{"additionalProperties":{"type":"string"},"description":"OAuth parameters for server-specific customization","type":"object"},"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"},"runner.RunConfig":{"properties":{"audit_config":{"$ref":"#/components/schemas/audit.Config"},"audit_config_path":{"description":"AuditConfigPath is the path to the audit configuration file","type":"string"},"authz_config":{"$ref":"#/components/schemas/authz.Config"},"authz_config_path":{"description":"AuthzConfigPath is the path to the authorization configuration file","type":"string"},"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"},"env_file_dir":{"description":"EnvFileDir 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"},"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":"JWKSAuthTokenFile 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"},"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},"name":{"description":"Name is the name of the MCP server","type":"string"},"oidc_config":{"$ref":"#/components/schemas/auth.TokenValidatorConfig"},"permission_profile":{"$ref":"#/components/schemas/permissions.Profile"},"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":{"$ref":"#/components/schemas/types.ProxyMode"},"remote_auth_config":{"$ref":"#/components/schemas/runner.RemoteAuthConfig"},"remote_url":{"description":"RemoteURL is the URL of the remote MCP server (if running remotely)","type":"string"},"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},"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":"ThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations","type":"string"},"tools_filter":{"description":"ToolsFilter is the list of tools to filter","items":{"type":"string"},"type":"array","uniqueItems":false},"tools_override":{"additionalProperties":{"$ref":"#/components/schemas/runner.ToolOverride"},"description":"ToolsOverride 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)","type":"string","x-enum-varnames":["TransportTypeStdio","TransportTypeSSE","TransportTypeStreamableHTTP","TransportTypeInspector"]},"trust_proxy_headers":{"description":"TrustProxyHeaders indicates whether to trust X-Forwarded-* headers from reverse proxies","type":"boolean"},"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"},"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"},"runtime.WorkloadStatus":{"description":"Status is the current status of the workload.","type":"string","x-enum-varnames":["WorkloadStatusRunning","WorkloadStatusStopped","WorkloadStatusError","WorkloadStatusStarting","WorkloadStatusStopping","WorkloadStatusUnhealthy","WorkloadStatusRemoving","WorkloadStatusUnknown"]},"secrets.SecretParameter":{"properties":{"name":{"type":"string"},"target":{"type":"string"}},"type":"object"},"telemetry.Config":{"description":"TelemetryConfig contains the OpenTelemetry configuration","properties":{"enablePrometheusMetricsPath":{"description":"EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint\nThe metrics are served on the main transport port at /metrics\nThis is separate from OTLP metrics which are sent to the Endpoint","type":"boolean"},"endpoint":{"description":"Endpoint is the OTLP endpoint URL","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: []string{\"NODE_ENV\", \"DEPLOYMENT_ENV\", \"SERVICE_VERSION\"}","items":{"type":"string"},"type":"array","uniqueItems":false},"headers":{"additionalProperties":{"type":"string"},"description":"Headers contains authentication headers for the OTLP endpoint","type":"object"},"insecure":{"description":"Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint","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","type":"boolean"},"samplingRate":{"description":"SamplingRate is the trace sampling rate (0.0-1.0)\nOnly used when TracingEnabled is true","type":"number"},"serviceName":{"description":"ServiceName is the service name for telemetry","type":"string"},"serviceVersion":{"description":"ServiceVersion is the service version for telemetry","type":"string"},"tracingEnabled":{"description":"TracingEnabled controls whether distributed tracing is enabled\nWhen false, no tracer provider is created even if an endpoint is configured","type":"boolean"}},"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.ProxyMode":{"description":"ProxyMode is the proxy mode for stdio transport (\"sse\" or \"streamable-http\")","type":"string","x-enum-varnames":["ProxyModeSSE","ProxyModeStreamableHTTP"]},"types.TransportType":{"description":"TransportType is the type of transport used for this workload.","type":"string","x-enum-varnames":["TransportTypeStdio","TransportTypeSSE","TransportTypeStreamableHTTP","TransportTypeInspector"]},"v1.RegistryType":{"description":"Type of registry (file, url, or default)","type":"string","x-enum-varnames":["RegistryTypeFile","RegistryTypeURL","RegistryTypeDefault"]},"v1.UpdateRegistryRequest":{"description":"Request containing registry configuration updates","properties":{"allow_private_ip":{"description":"Allow private IP addresses for registry URL","type":"boolean"},"local_path":{"description":"Local registry file path","type":"string"},"url":{"description":"Registry URL (for remote registries)","type":"string"}},"type":"object"},"v1.UpdateRegistryResponse":{"description":"Response containing update result","properties":{"message":{"description":"Status message","type":"string"},"type":{"description":"Registry type after update","type":"string"}},"type":"object"},"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":{"type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"type":"array","uniqueItems":false}},"type":"object"},"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"},"v1.clientStatusResponse":{"properties":{"clients":{"items":{"$ref":"#/components/schemas/client.MCPClientStatus"},"type":"array","uniqueItems":false}},"type":"object"},"v1.createClientRequest":{"properties":{"groups":{"description":"Groups is the list of groups configured on the client.","items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"description":"Name is the type of the client to register.","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]}},"type":"object"},"v1.createClientResponse":{"properties":{"groups":{"description":"Groups is the list of groups configured on the client.","items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"description":"Name is the type of the client that was registered.","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]}},"type":"object"},"v1.createGroupRequest":{"properties":{"name":{"description":"Name of the group to create","type":"string"}},"type":"object"},"v1.createGroupResponse":{"properties":{"name":{"description":"Name of the created group","type":"string"}},"type":"object"},"v1.createRequest":{"description":"Request to create a new workload","properties":{"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"},"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.","type":"boolean"},"oauth_config":{"$ref":"#/components/schemas/v1.remoteOAuthConfig"},"oidc":{"$ref":"#/components/schemas/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"},"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/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"},"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"},"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"},"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"},"v1.getRegistryResponse":{"description":"Response containing registry details","properties":{"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":{"description":"Type of registry (file, url, or default)","type":"string","x-enum-varnames":["RegistryTypeFile","RegistryTypeURL","RegistryTypeDefault"]},"version":{"description":"Version of the registry schema","type":"string"}},"type":"object"},"v1.getSecretsProviderResponse":{"description":"Response containing secrets provider details","properties":{"capabilities":{"$ref":"#/components/schemas/v1.providerCapabilitiesResponse"},"name":{"description":"Name of the secrets provider","type":"string"},"provider_type":{"description":"Type of the secrets provider","type":"string"}},"type":"object"},"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"},"v1.groupListResponse":{"properties":{"groups":{"description":"List of groups","items":{"$ref":"#/components/schemas/groups.Group"},"type":"array","uniqueItems":false}},"type":"object"},"v1.listSecretsResponse":{"description":"Response containing a list of secret keys","properties":{"keys":{"description":"List of secret keys","items":{"$ref":"#/components/schemas/v1.secretKeyResponse"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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"}},"type":"object"},"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"},"v1.registryInfo":{"description":"Basic information about a registry","properties":{"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/v1.RegistryType"},"version":{"description":"Version of the registry schema","type":"string"}},"type":"object"},"v1.registryListResponse":{"description":"Response containing a list of registries","properties":{"registries":{"description":"List of registries","items":{"$ref":"#/components/schemas/v1.registryInfo"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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"},"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"},"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"},"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, none)","type":"string"}},"type":"object"},"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"},"v1.toolOverride":{"description":"Tool override","properties":{"description":{"description":"Description of the tool","type":"string"},"name":{"description":"Name of the tool","type":"string"}},"type":"object"},"v1.updateRequest":{"description":"Request to update an existing workload (name cannot be changed)","properties":{"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"},"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.","type":"boolean"},"oauth_config":{"$ref":"#/components/schemas/v1.remoteOAuthConfig"},"oidc":{"$ref":"#/components/schemas/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"},"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/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"},"v1.updateSecretRequest":{"description":"Request to update an existing secret","properties":{"value":{"description":"New secret value","type":"string"}},"type":"object"},"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"},"v1.versionResponse":{"properties":{"version":{"type":"string"}},"type":"object"},"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"},"v1.workloadStatusResponse":{"description":"Response containing workload status information","properties":{"status":{"description":"Current status of the workload","type":"string","x-enum-varnames":["WorkloadStatusRunning","WorkloadStatusStopped","WorkloadStatusError","WorkloadStatusStarting","WorkloadStatusStopping","WorkloadStatusUnhealthy","WorkloadStatusRemoving","WorkloadStatusUnknown"]}},"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/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":{"$ref":"#/components/schemas/v1.createClientRequest"}}},"description":"Client to register","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.createClientResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"summary":"Register a new client","tags":["clients"]}},"/api/v1beta/clients/register":{"post":{"description":"Register multiple clients with ToolHive","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkClientRequest"}}},"description":"Clients to register","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/v1.createClientResponse"},"type":"array"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"summary":"Register multiple clients","tags":["clients"]}},"/api/v1beta/clients/unregister":{"post":{"description":"Unregister multiple clients from ToolHive","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkClientRequest"}}},"description":"Clients to unregister","required":true},"responses":{"204":{"description":"No Content"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"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"}},"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"},"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","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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":{"$ref":"#/components/schemas/v1.createGroupRequest"}}},"description":"Group creation request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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/registry":{"get":{"description":"Get a list of the current registries","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/{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"},"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/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":{"$ref":"#/components/schemas/v1.UpdateRegistryRequest"}}},"description":"Registry configuration","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.UpdateRegistryResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Bad Request"},"404":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Not Found"}},"summary":"Update registry configuration","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/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/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":{"$ref":"#/components/schemas/v1.setupSecretsRequest"}}},"description":"Setup secrets provider request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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/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":{"$ref":"#/components/schemas/v1.createSecretRequest"}}},"description":"Create secret request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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":{"$ref":"#/components/schemas/v1.updateSecretRequest"}}},"description":"Update secret request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/version":{"get":{"description":"Returns the current version of the server","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.createRequest"}}},"description":"Create workload request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"description":"Bulk delete 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":"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":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"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":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"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/{name}":{"delete":{"description":"Delete a 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":"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/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","parameters":[{"description":"Workload name","in":"path","name":"name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.updateRequest"}}},"description":"Update workload request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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 100 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":{"application/json":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}}},"description":"Logs for the specified workload"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid workload name"},"404":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Not Found"}},"summary":"Get logs for a specific workload","tags":["logs"]}},"/api/v1beta/workloads/{name}/proxy-logs":{"get":{"description":"Retrieve 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":{"application/json":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}}},"description":"Proxy logs for the specified workload"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid workload name"},"404":{"content":{"application/json":{"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/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"]}},"/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"]}}}, + "openapi": "3.1.0" } From 5f1aeff85e6aa2c0b620610f5f06814a87125e0b Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Wed, 22 Oct 2025 18:51:46 +0200 Subject: [PATCH 2/7] use host networking for metamcp --- renderer/src/common/lib/__tests__/meta-optimizer.test.ts | 5 +++++ renderer/src/common/lib/meta-optimizer.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts index adaec6696..035b6c8f4 100644 --- a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts +++ b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts @@ -113,6 +113,11 @@ describe('Meta Optimizer', () => { image: 'ghcr.io/stackloklabs/meta-mcp:latest', transport: 'streamable-http', env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default' }, + permission_profile: { + network: { + mode: 'host', + }, + }, }), }) }) diff --git a/renderer/src/common/lib/meta-optimizer.ts b/renderer/src/common/lib/meta-optimizer.ts index 9afbf652b..e6a27fa8a 100644 --- a/renderer/src/common/lib/meta-optimizer.ts +++ b/renderer/src/common/lib/meta-optimizer.ts @@ -71,6 +71,11 @@ async function createMetaOptimizerWorkload() { network_isolation: false, volumes: [], group: MCP_OPTIMIZER_GROUP_NAME, + permission_profile: { + network: { + mode: 'host', + }, + }, } const response = await postApiV1BetaWorkloads({ From a93ea9bce5f163640a7b0ea6940545bb7e1f5dad Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Wed, 22 Oct 2025 19:06:25 +0200 Subject: [PATCH 3/7] update network configuration --- renderer/src/common/lib/__tests__/meta-optimizer.test.ts | 5 ----- renderer/src/common/lib/meta-optimizer.ts | 5 ----- 2 files changed, 10 deletions(-) diff --git a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts index 035b6c8f4..adaec6696 100644 --- a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts +++ b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts @@ -113,11 +113,6 @@ describe('Meta Optimizer', () => { image: 'ghcr.io/stackloklabs/meta-mcp:latest', transport: 'streamable-http', env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default' }, - permission_profile: { - network: { - mode: 'host', - }, - }, }), }) }) diff --git a/renderer/src/common/lib/meta-optimizer.ts b/renderer/src/common/lib/meta-optimizer.ts index e6a27fa8a..9afbf652b 100644 --- a/renderer/src/common/lib/meta-optimizer.ts +++ b/renderer/src/common/lib/meta-optimizer.ts @@ -71,11 +71,6 @@ async function createMetaOptimizerWorkload() { network_isolation: false, volumes: [], group: MCP_OPTIMIZER_GROUP_NAME, - permission_profile: { - network: { - mode: 'host', - }, - }, } const response = await postApiV1BetaWorkloads({ From d90448ad198900821cdd5edd63f80f16b02fba86 Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Thu, 23 Oct 2025 18:28:18 +0200 Subject: [PATCH 4/7] . --- api/generated/types.gen.ts | 5 - api/openapi.json | 3149 ++++++++++++++++- .../lib/__tests__/meta-optimizer.test.ts | 55 +- renderer/src/common/lib/meta-optimizer.ts | 63 +- utils/fetch-thv.ts | 7 +- 5 files changed, 3250 insertions(+), 29 deletions(-) diff --git a/api/generated/types.gen.ts b/api/generated/types.gen.ts index d8dab2afb..1395e3262 100644 --- a/api/generated/types.gen.ts +++ b/api/generated/types.gen.ts @@ -234,11 +234,6 @@ export type PermissionsInboundNetworkPermissions = { */ export type PermissionsNetworkPermissions = { inbound?: PermissionsInboundNetworkPermissions - /** - * Mode specifies the network mode for the container (e.g., "host", "bridge", "none") - * When empty, the default container runtime network mode is used - */ - mode?: string outbound?: PermissionsOutboundNetworkPermissions } diff --git a/api/openapi.json b/api/openapi.json index 77d2569c5..d2cfc5c63 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -1,7 +1,3146 @@ { - "components": {"schemas":{"audit.Config":{"description":"AuditConfig contains the audit logging configuration","properties":{"component":{"description":"Component is the component name to use in audit events","type":"string"},"event_types":{"description":"EventTypes specifies which event types to audit. If empty, all events are audited.","items":{"type":"string"},"type":"array","uniqueItems":false},"exclude_event_types":{"description":"ExcludeEventTypes specifies which event types to exclude from auditing.\nThis takes precedence over EventTypes.","items":{"type":"string"},"type":"array","uniqueItems":false},"include_request_data":{"description":"IncludeRequestData determines whether to include request data in audit logs","type":"boolean"},"include_response_data":{"description":"IncludeResponseData determines whether to include response data in audit logs","type":"boolean"},"log_file":{"description":"LogFile specifies the file path for audit logs. If empty, logs to stdout.","type":"string"},"max_data_size":{"description":"MaxDataSize limits the size of request/response data included in audit logs (in bytes)","type":"integer"}},"type":"object"},"auth.TokenValidatorConfig":{"description":"OIDCConfig 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"},"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"}},"type":"object"},"authz.CedarConfig":{"description":"Cedar is the Cedar-specific configuration.\nThis is only used when Type is ConfigTypeCedarV1.","properties":{"entities_json":{"description":"EntitiesJSON is the JSON string representing Cedar entities","type":"string"},"policies":{"description":"Policies is a list of Cedar policy strings","items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"authz.Config":{"description":"AuthzConfig contains the authorization configuration","properties":{"cedar":{"$ref":"#/components/schemas/authz.CedarConfig"},"type":{"$ref":"#/components/schemas/authz.ConfigType"},"version":{"description":"Version is the version of the configuration format.","type":"string"}},"type":"object"},"authz.ConfigType":{"description":"Type is the type of authorization configuration.","type":"string","x-enum-varnames":["ConfigTypeCedarV1"]},"client.MCPClient":{"type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"client.MCPClientStatus":{"properties":{"client_type":{"description":"ClientType is the type of MCP client","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"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"}},"type":"object"},"client.RegisteredClient":{"properties":{"groups":{"items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"$ref":"#/components/schemas/client.MCPClient"}},"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"},"status":{"$ref":"#/components/schemas/runtime.WorkloadStatus"},"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"},"tool_type":{"description":"ToolType is the type of tool this workload represents.\nFor now, it will always be \"mcp\" - representing an MCP server.","type":"string"},"tools":{"description":"ToolsFilter is the filter on tools applied to the workload.","items":{"type":"string"},"type":"array","uniqueItems":false},"transport_type":{"$ref":"#/components/schemas/types.TransportType"},"url":{"description":"URL is the URL of the workload exposed by the ToolHive proxy.","type":"string"}},"type":"object"},"groups.Group":{"properties":{"name":{"type":"string"},"registered_clients":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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":"PermissionProfile is the permission profile to use","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"},"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"},"permissions":{"$ref":"#/components/schemas/permissions.Profile"},"provenance":{"$ref":"#/components/schemas/registry.Provenance"},"repository_url":{"description":"RepositoryURL is the URL to the source code repository for the server","type":"string"},"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"},"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.Metadata":{"description":"Metadata contains additional information about the server such as popularity metrics","properties":{"last_updated":{"description":"LastUpdated is the timestamp when the server was last updated, in RFC3339 format","type":"string"},"pulls":{"description":"Pulls indicates how many times the server image has been downloaded","type":"integer"},"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"},"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.Provenance":{"description":"Provenance contains verification and signing metadata","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"},"repository_url":{"description":"RepositoryURL is the URL to the source code repository for the server","type":"string"},"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"},"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.VerifiedAttestation":{"properties":{"predicate":{},"predicate_type":{"type":"string"}},"type":"object"},"runner.RemoteAuthConfig":{"description":"RemoteAuthConfig contains OAuth configuration for remote MCP servers","properties":{"authorize_url":{"type":"string"},"callback_port":{"type":"integer"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"client_secret_file":{"type":"string"},"env_vars":{"description":"Environment variables for the client","items":{"$ref":"#/components/schemas/registry.EnvVar"},"type":"array","uniqueItems":false},"headers":{"description":"Headers for HTTP requests","items":{"$ref":"#/components/schemas/registry.Header"},"type":"array","uniqueItems":false},"issuer":{"description":"OAuth endpoint configuration (from registry)","type":"string"},"oauth_params":{"additionalProperties":{"type":"string"},"description":"OAuth parameters for server-specific customization","type":"object"},"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"},"runner.RunConfig":{"properties":{"audit_config":{"$ref":"#/components/schemas/audit.Config"},"audit_config_path":{"description":"AuditConfigPath is the path to the audit configuration file","type":"string"},"authz_config":{"$ref":"#/components/schemas/authz.Config"},"authz_config_path":{"description":"AuthzConfigPath is the path to the authorization configuration file","type":"string"},"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"},"env_file_dir":{"description":"EnvFileDir 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"},"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":"JWKSAuthTokenFile 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"},"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},"name":{"description":"Name is the name of the MCP server","type":"string"},"oidc_config":{"$ref":"#/components/schemas/auth.TokenValidatorConfig"},"permission_profile":{"$ref":"#/components/schemas/permissions.Profile"},"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":{"$ref":"#/components/schemas/types.ProxyMode"},"remote_auth_config":{"$ref":"#/components/schemas/runner.RemoteAuthConfig"},"remote_url":{"description":"RemoteURL is the URL of the remote MCP server (if running remotely)","type":"string"},"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},"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":"ThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations","type":"string"},"tools_filter":{"description":"ToolsFilter is the list of tools to filter","items":{"type":"string"},"type":"array","uniqueItems":false},"tools_override":{"additionalProperties":{"$ref":"#/components/schemas/runner.ToolOverride"},"description":"ToolsOverride 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)","type":"string","x-enum-varnames":["TransportTypeStdio","TransportTypeSSE","TransportTypeStreamableHTTP","TransportTypeInspector"]},"trust_proxy_headers":{"description":"TrustProxyHeaders indicates whether to trust X-Forwarded-* headers from reverse proxies","type":"boolean"},"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"},"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"},"runtime.WorkloadStatus":{"description":"Status is the current status of the workload.","type":"string","x-enum-varnames":["WorkloadStatusRunning","WorkloadStatusStopped","WorkloadStatusError","WorkloadStatusStarting","WorkloadStatusStopping","WorkloadStatusUnhealthy","WorkloadStatusRemoving","WorkloadStatusUnknown"]},"secrets.SecretParameter":{"properties":{"name":{"type":"string"},"target":{"type":"string"}},"type":"object"},"telemetry.Config":{"description":"TelemetryConfig contains the OpenTelemetry configuration","properties":{"enablePrometheusMetricsPath":{"description":"EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint\nThe metrics are served on the main transport port at /metrics\nThis is separate from OTLP metrics which are sent to the Endpoint","type":"boolean"},"endpoint":{"description":"Endpoint is the OTLP endpoint URL","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: []string{\"NODE_ENV\", \"DEPLOYMENT_ENV\", \"SERVICE_VERSION\"}","items":{"type":"string"},"type":"array","uniqueItems":false},"headers":{"additionalProperties":{"type":"string"},"description":"Headers contains authentication headers for the OTLP endpoint","type":"object"},"insecure":{"description":"Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint","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","type":"boolean"},"samplingRate":{"description":"SamplingRate is the trace sampling rate (0.0-1.0)\nOnly used when TracingEnabled is true","type":"number"},"serviceName":{"description":"ServiceName is the service name for telemetry","type":"string"},"serviceVersion":{"description":"ServiceVersion is the service version for telemetry","type":"string"},"tracingEnabled":{"description":"TracingEnabled controls whether distributed tracing is enabled\nWhen false, no tracer provider is created even if an endpoint is configured","type":"boolean"}},"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.ProxyMode":{"description":"ProxyMode is the proxy mode for stdio transport (\"sse\" or \"streamable-http\")","type":"string","x-enum-varnames":["ProxyModeSSE","ProxyModeStreamableHTTP"]},"types.TransportType":{"description":"TransportType is the type of transport used for this workload.","type":"string","x-enum-varnames":["TransportTypeStdio","TransportTypeSSE","TransportTypeStreamableHTTP","TransportTypeInspector"]},"v1.RegistryType":{"description":"Type of registry (file, url, or default)","type":"string","x-enum-varnames":["RegistryTypeFile","RegistryTypeURL","RegistryTypeDefault"]},"v1.UpdateRegistryRequest":{"description":"Request containing registry configuration updates","properties":{"allow_private_ip":{"description":"Allow private IP addresses for registry URL","type":"boolean"},"local_path":{"description":"Local registry file path","type":"string"},"url":{"description":"Registry URL (for remote registries)","type":"string"}},"type":"object"},"v1.UpdateRegistryResponse":{"description":"Response containing update result","properties":{"message":{"description":"Status message","type":"string"},"type":{"description":"Registry type after update","type":"string"}},"type":"object"},"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":{"type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]},"type":"array","uniqueItems":false}},"type":"object"},"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"},"v1.clientStatusResponse":{"properties":{"clients":{"items":{"$ref":"#/components/schemas/client.MCPClientStatus"},"type":"array","uniqueItems":false}},"type":"object"},"v1.createClientRequest":{"properties":{"groups":{"description":"Groups is the list of groups configured on the client.","items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"description":"Name is the type of the client to register.","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]}},"type":"object"},"v1.createClientResponse":{"properties":{"groups":{"description":"Groups is the list of groups configured on the client.","items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"description":"Name is the type of the client that was registered.","type":"string","x-enum-varnames":["RooCode","Cline","Cursor","VSCodeInsider","VSCode","ClaudeCode","Windsurf","WindsurfJetBrains","AmpCli","AmpVSCode","AmpCursor","AmpVSCodeInsider","AmpWindsurf","LMStudio","Goose","Trae","Continue"]}},"type":"object"},"v1.createGroupRequest":{"properties":{"name":{"description":"Name of the group to create","type":"string"}},"type":"object"},"v1.createGroupResponse":{"properties":{"name":{"description":"Name of the created group","type":"string"}},"type":"object"},"v1.createRequest":{"description":"Request to create a new workload","properties":{"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"},"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.","type":"boolean"},"oauth_config":{"$ref":"#/components/schemas/v1.remoteOAuthConfig"},"oidc":{"$ref":"#/components/schemas/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"},"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/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"},"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"},"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"},"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"},"v1.getRegistryResponse":{"description":"Response containing registry details","properties":{"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":{"description":"Type of registry (file, url, or default)","type":"string","x-enum-varnames":["RegistryTypeFile","RegistryTypeURL","RegistryTypeDefault"]},"version":{"description":"Version of the registry schema","type":"string"}},"type":"object"},"v1.getSecretsProviderResponse":{"description":"Response containing secrets provider details","properties":{"capabilities":{"$ref":"#/components/schemas/v1.providerCapabilitiesResponse"},"name":{"description":"Name of the secrets provider","type":"string"},"provider_type":{"description":"Type of the secrets provider","type":"string"}},"type":"object"},"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"},"v1.groupListResponse":{"properties":{"groups":{"description":"List of groups","items":{"$ref":"#/components/schemas/groups.Group"},"type":"array","uniqueItems":false}},"type":"object"},"v1.listSecretsResponse":{"description":"Response containing a list of secret keys","properties":{"keys":{"description":"List of secret keys","items":{"$ref":"#/components/schemas/v1.secretKeyResponse"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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"}},"type":"object"},"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"},"v1.registryInfo":{"description":"Basic information about a registry","properties":{"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/v1.RegistryType"},"version":{"description":"Version of the registry schema","type":"string"}},"type":"object"},"v1.registryListResponse":{"description":"Response containing a list of registries","properties":{"registries":{"description":"List of registries","items":{"$ref":"#/components/schemas/v1.registryInfo"},"type":"array","uniqueItems":false}},"type":"object"},"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"},"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"},"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"},"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"},"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, none)","type":"string"}},"type":"object"},"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"},"v1.toolOverride":{"description":"Tool override","properties":{"description":{"description":"Description of the tool","type":"string"},"name":{"description":"Name of the tool","type":"string"}},"type":"object"},"v1.updateRequest":{"description":"Request to update an existing workload (name cannot be changed)","properties":{"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"},"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.","type":"boolean"},"oauth_config":{"$ref":"#/components/schemas/v1.remoteOAuthConfig"},"oidc":{"$ref":"#/components/schemas/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"},"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/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"},"v1.updateSecretRequest":{"description":"Request to update an existing secret","properties":{"value":{"description":"New secret value","type":"string"}},"type":"object"},"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"},"v1.versionResponse":{"properties":{"version":{"type":"string"}},"type":"object"},"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"},"v1.workloadStatusResponse":{"description":"Response containing workload status information","properties":{"status":{"description":"Current status of the workload","type":"string","x-enum-varnames":["WorkloadStatusRunning","WorkloadStatusStopped","WorkloadStatusError","WorkloadStatusStarting","WorkloadStatusStopping","WorkloadStatusUnhealthy","WorkloadStatusRemoving","WorkloadStatusUnknown"]}},"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/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":{"$ref":"#/components/schemas/v1.createClientRequest"}}},"description":"Client to register","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.createClientResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"summary":"Register a new client","tags":["clients"]}},"/api/v1beta/clients/register":{"post":{"description":"Register multiple clients with ToolHive","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkClientRequest"}}},"description":"Clients to register","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/v1.createClientResponse"},"type":"array"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"summary":"Register multiple clients","tags":["clients"]}},"/api/v1beta/clients/unregister":{"post":{"description":"Unregister multiple clients from ToolHive","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkClientRequest"}}},"description":"Clients to unregister","required":true},"responses":{"204":{"description":"No Content"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid request"}},"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"}},"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"},"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","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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":{"$ref":"#/components/schemas/v1.createGroupRequest"}}},"description":"Group creation request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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/registry":{"get":{"description":"Get a list of the current registries","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/{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"},"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/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":{"$ref":"#/components/schemas/v1.UpdateRegistryRequest"}}},"description":"Registry configuration","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.UpdateRegistryResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Bad Request"},"404":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Not Found"}},"summary":"Update registry configuration","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/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/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":{"$ref":"#/components/schemas/v1.setupSecretsRequest"}}},"description":"Setup secrets provider request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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/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":{"$ref":"#/components/schemas/v1.createSecretRequest"}}},"description":"Create secret request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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":{"$ref":"#/components/schemas/v1.updateSecretRequest"}}},"description":"Update secret request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/version":{"get":{"description":"Returns the current version of the server","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.createRequest"}}},"description":"Create workload request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"description":"Bulk delete 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":"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":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"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":{"$ref":"#/components/schemas/v1.bulkOperationRequest"}}},"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/{name}":{"delete":{"description":"Delete a 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":"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/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","parameters":[{"description":"Workload name","in":"path","name":"name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/v1.updateRequest"}}},"description":"Update workload request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/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/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 100 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":{"application/json":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}}},"description":"Logs for the specified workload"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid workload name"},"404":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Not Found"}},"summary":"Get logs for a specific workload","tags":["logs"]}},"/api/v1beta/workloads/{name}/proxy-logs":{"get":{"description":"Retrieve 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":{"application/json":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}}},"description":"Proxy logs for the specified workload"},"400":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"Invalid workload name"},"404":{"content":{"application/json":{"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/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"]}},"/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"]}}}, - "openapi": "3.1.0" + "components": { + "schemas": { + "audit.Config": { + "description": "AuditConfig contains the audit logging configuration", + "properties": { + "component": { + "description": "Component is the component name to use in audit events", + "type": "string" + }, + "event_types": { + "description": "EventTypes specifies which event types to audit. If empty, all events are audited.", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "exclude_event_types": { + "description": "ExcludeEventTypes specifies which event types to exclude from auditing.\nThis takes precedence over EventTypes.", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "include_request_data": { + "description": "IncludeRequestData determines whether to include request data in audit logs", + "type": "boolean" + }, + "include_response_data": { + "description": "IncludeResponseData determines whether to include response data in audit logs", + "type": "boolean" + }, + "log_file": { + "description": "LogFile specifies the file path for audit logs. If empty, logs to stdout.", + "type": "string" + }, + "max_data_size": { + "description": "MaxDataSize limits the size of request/response data included in audit logs (in bytes)", + "type": "integer" + } + }, + "type": "object" + }, + "auth.TokenValidatorConfig": { + "description": "OIDCConfig 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" + }, + "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" + } + }, + "type": "object" + }, + "authz.CedarConfig": { + "description": "Cedar is the Cedar-specific configuration.\nThis is only used when Type is ConfigTypeCedarV1.", + "properties": { + "entities_json": { + "description": "EntitiesJSON is the JSON string representing Cedar entities", + "type": "string" + }, + "policies": { + "description": "Policies is a list of Cedar policy strings", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "authz.Config": { + "description": "AuthzConfig contains the authorization configuration", + "properties": { + "cedar": { "$ref": "#/components/schemas/authz.CedarConfig" }, + "type": { "$ref": "#/components/schemas/authz.ConfigType" }, + "version": { + "description": "Version is the version of the configuration format.", + "type": "string" + } + }, + "type": "object" + }, + "authz.ConfigType": { + "description": "Type is the type of authorization configuration.", + "type": "string", + "x-enum-varnames": ["ConfigTypeCedarV1"] + }, + "client.MCPClient": { + "type": "string", + "x-enum-varnames": [ + "RooCode", + "Cline", + "Cursor", + "VSCodeInsider", + "VSCode", + "ClaudeCode", + "Windsurf", + "WindsurfJetBrains", + "AmpCli", + "AmpVSCode", + "AmpCursor", + "AmpVSCodeInsider", + "AmpWindsurf", + "LMStudio", + "Goose", + "Trae", + "Continue" + ] + }, + "client.MCPClientStatus": { + "properties": { + "client_type": { + "description": "ClientType is the type of MCP client", + "type": "string", + "x-enum-varnames": [ + "RooCode", + "Cline", + "Cursor", + "VSCodeInsider", + "VSCode", + "ClaudeCode", + "Windsurf", + "WindsurfJetBrains", + "AmpCli", + "AmpVSCode", + "AmpCursor", + "AmpVSCodeInsider", + "AmpWindsurf", + "LMStudio", + "Goose", + "Trae", + "Continue" + ] + }, + "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" + } + }, + "type": "object" + }, + "client.RegisteredClient": { + "properties": { + "groups": { + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "name": { "$ref": "#/components/schemas/client.MCPClient" } + }, + "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" + }, + "status": { "$ref": "#/components/schemas/runtime.WorkloadStatus" }, + "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" + }, + "tool_type": { + "description": "ToolType is the type of tool this workload represents.\nFor now, it will always be \"mcp\" - representing an MCP server.", + "type": "string" + }, + "tools": { + "description": "ToolsFilter is the filter on tools applied to the workload.", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "transport_type": { + "$ref": "#/components/schemas/types.TransportType" + }, + "url": { + "description": "URL is the URL of the workload exposed by the ToolHive proxy.", + "type": "string" + } + }, + "type": "object" + }, + "groups.Group": { + "properties": { + "name": { "type": "string" }, + "registered_clients": { + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "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" + }, + "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" + }, + "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": "PermissionProfile is the permission profile to use", + "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" + }, + "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" + }, + "permissions": { "$ref": "#/components/schemas/permissions.Profile" }, + "provenance": { "$ref": "#/components/schemas/registry.Provenance" }, + "repository_url": { + "description": "RepositoryURL is the URL to the source code repository for the server", + "type": "string" + }, + "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" + }, + "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.Metadata": { + "description": "Metadata contains additional information about the server such as popularity metrics", + "properties": { + "last_updated": { + "description": "LastUpdated is the timestamp when the server was last updated, in RFC3339 format", + "type": "string" + }, + "pulls": { + "description": "Pulls indicates how many times the server image has been downloaded", + "type": "integer" + }, + "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" + }, + "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.Provenance": { + "description": "Provenance contains verification and signing metadata", + "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" + }, + "repository_url": { + "description": "RepositoryURL is the URL to the source code repository for the server", + "type": "string" + }, + "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" + }, + "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.VerifiedAttestation": { + "properties": { + "predicate": {}, + "predicate_type": { "type": "string" } + }, + "type": "object" + }, + "runner.RemoteAuthConfig": { + "description": "RemoteAuthConfig contains OAuth configuration for remote MCP servers", + "properties": { + "authorize_url": { "type": "string" }, + "callback_port": { "type": "integer" }, + "client_id": { "type": "string" }, + "client_secret": { "type": "string" }, + "client_secret_file": { "type": "string" }, + "env_vars": { + "description": "Environment variables for the client", + "items": { "$ref": "#/components/schemas/registry.EnvVar" }, + "type": "array", + "uniqueItems": false + }, + "headers": { + "description": "Headers for HTTP requests", + "items": { "$ref": "#/components/schemas/registry.Header" }, + "type": "array", + "uniqueItems": false + }, + "issuer": { + "description": "OAuth endpoint configuration (from registry)", + "type": "string" + }, + "oauth_params": { + "additionalProperties": { "type": "string" }, + "description": "OAuth parameters for server-specific customization", + "type": "object" + }, + "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" + }, + "runner.RunConfig": { + "properties": { + "audit_config": { "$ref": "#/components/schemas/audit.Config" }, + "audit_config_path": { + "description": "AuditConfigPath is the path to the audit configuration file", + "type": "string" + }, + "authz_config": { "$ref": "#/components/schemas/authz.Config" }, + "authz_config_path": { + "description": "AuthzConfigPath is the path to the authorization configuration file", + "type": "string" + }, + "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" + }, + "env_file_dir": { + "description": "EnvFileDir 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" + }, + "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": "JWKSAuthTokenFile 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" + }, + "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 + }, + "name": { + "description": "Name is the name of the MCP server", + "type": "string" + }, + "oidc_config": { + "$ref": "#/components/schemas/auth.TokenValidatorConfig" + }, + "permission_profile": { + "$ref": "#/components/schemas/permissions.Profile" + }, + "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": { "$ref": "#/components/schemas/types.ProxyMode" }, + "remote_auth_config": { + "$ref": "#/components/schemas/runner.RemoteAuthConfig" + }, + "remote_url": { + "description": "RemoteURL is the URL of the remote MCP server (if running remotely)", + "type": "string" + }, + "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 + }, + "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": "ThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations", + "type": "string" + }, + "tools_filter": { + "description": "ToolsFilter is the list of tools to filter", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "tools_override": { + "additionalProperties": { + "$ref": "#/components/schemas/runner.ToolOverride" + }, + "description": "ToolsOverride 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)", + "type": "string", + "x-enum-varnames": [ + "TransportTypeStdio", + "TransportTypeSSE", + "TransportTypeStreamableHTTP", + "TransportTypeInspector" + ] + }, + "trust_proxy_headers": { + "description": "TrustProxyHeaders indicates whether to trust X-Forwarded-* headers from reverse proxies", + "type": "boolean" + }, + "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" + }, + "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" + }, + "runtime.WorkloadStatus": { + "description": "Status is the current status of the workload.", + "type": "string", + "x-enum-varnames": [ + "WorkloadStatusRunning", + "WorkloadStatusStopped", + "WorkloadStatusError", + "WorkloadStatusStarting", + "WorkloadStatusStopping", + "WorkloadStatusUnhealthy", + "WorkloadStatusRemoving", + "WorkloadStatusUnknown" + ] + }, + "secrets.SecretParameter": { + "properties": { + "name": { "type": "string" }, + "target": { "type": "string" } + }, + "type": "object" + }, + "telemetry.Config": { + "description": "TelemetryConfig contains the OpenTelemetry configuration", + "properties": { + "enablePrometheusMetricsPath": { + "description": "EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint\nThe metrics are served on the main transport port at /metrics\nThis is separate from OTLP metrics which are sent to the Endpoint", + "type": "boolean" + }, + "endpoint": { + "description": "Endpoint is the OTLP endpoint URL", + "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: []string{\"NODE_ENV\", \"DEPLOYMENT_ENV\", \"SERVICE_VERSION\"}", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "headers": { + "additionalProperties": { "type": "string" }, + "description": "Headers contains authentication headers for the OTLP endpoint", + "type": "object" + }, + "insecure": { + "description": "Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint", + "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", + "type": "boolean" + }, + "samplingRate": { + "description": "SamplingRate is the trace sampling rate (0.0-1.0)\nOnly used when TracingEnabled is true", + "type": "number" + }, + "serviceName": { + "description": "ServiceName is the service name for telemetry", + "type": "string" + }, + "serviceVersion": { + "description": "ServiceVersion is the service version for telemetry", + "type": "string" + }, + "tracingEnabled": { + "description": "TracingEnabled controls whether distributed tracing is enabled\nWhen false, no tracer provider is created even if an endpoint is configured", + "type": "boolean" + } + }, + "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.ProxyMode": { + "description": "ProxyMode is the proxy mode for stdio transport (\"sse\" or \"streamable-http\")", + "type": "string", + "x-enum-varnames": ["ProxyModeSSE", "ProxyModeStreamableHTTP"] + }, + "types.TransportType": { + "description": "TransportType is the type of transport used for this workload.", + "type": "string", + "x-enum-varnames": [ + "TransportTypeStdio", + "TransportTypeSSE", + "TransportTypeStreamableHTTP", + "TransportTypeInspector" + ] + }, + "v1.RegistryType": { + "description": "Type of registry (file, url, or default)", + "type": "string", + "x-enum-varnames": [ + "RegistryTypeFile", + "RegistryTypeURL", + "RegistryTypeDefault" + ] + }, + "v1.UpdateRegistryRequest": { + "description": "Request containing registry configuration updates", + "properties": { + "allow_private_ip": { + "description": "Allow private IP addresses for registry URL", + "type": "boolean" + }, + "local_path": { + "description": "Local registry file path", + "type": "string" + }, + "url": { + "description": "Registry URL (for remote registries)", + "type": "string" + } + }, + "type": "object" + }, + "v1.UpdateRegistryResponse": { + "description": "Response containing update result", + "properties": { + "message": { "description": "Status message", "type": "string" }, + "type": { + "description": "Registry type after update", + "type": "string" + } + }, + "type": "object" + }, + "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": { + "type": "string", + "x-enum-varnames": [ + "RooCode", + "Cline", + "Cursor", + "VSCodeInsider", + "VSCode", + "ClaudeCode", + "Windsurf", + "WindsurfJetBrains", + "AmpCli", + "AmpVSCode", + "AmpCursor", + "AmpVSCodeInsider", + "AmpWindsurf", + "LMStudio", + "Goose", + "Trae", + "Continue" + ] + }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "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" + }, + "v1.clientStatusResponse": { + "properties": { + "clients": { + "items": { "$ref": "#/components/schemas/client.MCPClientStatus" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "v1.createClientRequest": { + "properties": { + "groups": { + "description": "Groups is the list of groups configured on the client.", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "name": { + "description": "Name is the type of the client to register.", + "type": "string", + "x-enum-varnames": [ + "RooCode", + "Cline", + "Cursor", + "VSCodeInsider", + "VSCode", + "ClaudeCode", + "Windsurf", + "WindsurfJetBrains", + "AmpCli", + "AmpVSCode", + "AmpCursor", + "AmpVSCodeInsider", + "AmpWindsurf", + "LMStudio", + "Goose", + "Trae", + "Continue" + ] + } + }, + "type": "object" + }, + "v1.createClientResponse": { + "properties": { + "groups": { + "description": "Groups is the list of groups configured on the client.", + "items": { "type": "string" }, + "type": "array", + "uniqueItems": false + }, + "name": { + "description": "Name is the type of the client that was registered.", + "type": "string", + "x-enum-varnames": [ + "RooCode", + "Cline", + "Cursor", + "VSCodeInsider", + "VSCode", + "ClaudeCode", + "Windsurf", + "WindsurfJetBrains", + "AmpCli", + "AmpVSCode", + "AmpCursor", + "AmpVSCodeInsider", + "AmpWindsurf", + "LMStudio", + "Goose", + "Trae", + "Continue" + ] + } + }, + "type": "object" + }, + "v1.createGroupRequest": { + "properties": { + "name": { + "description": "Name of the group to create", + "type": "string" + } + }, + "type": "object" + }, + "v1.createGroupResponse": { + "properties": { + "name": { + "description": "Name of the created group", + "type": "string" + } + }, + "type": "object" + }, + "v1.createRequest": { + "description": "Request to create a new workload", + "properties": { + "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" + }, + "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.", + "type": "boolean" + }, + "oauth_config": { + "$ref": "#/components/schemas/v1.remoteOAuthConfig" + }, + "oidc": { "$ref": "#/components/schemas/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" + }, + "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/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" + }, + "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" + }, + "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" + }, + "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" + }, + "v1.getRegistryResponse": { + "description": "Response containing registry details", + "properties": { + "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": { + "description": "Type of registry (file, url, or default)", + "type": "string", + "x-enum-varnames": [ + "RegistryTypeFile", + "RegistryTypeURL", + "RegistryTypeDefault" + ] + }, + "version": { + "description": "Version of the registry schema", + "type": "string" + } + }, + "type": "object" + }, + "v1.getSecretsProviderResponse": { + "description": "Response containing secrets provider details", + "properties": { + "capabilities": { + "$ref": "#/components/schemas/v1.providerCapabilitiesResponse" + }, + "name": { + "description": "Name of the secrets provider", + "type": "string" + }, + "provider_type": { + "description": "Type of the secrets provider", + "type": "string" + } + }, + "type": "object" + }, + "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" + }, + "v1.groupListResponse": { + "properties": { + "groups": { + "description": "List of groups", + "items": { "$ref": "#/components/schemas/groups.Group" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "v1.listSecretsResponse": { + "description": "Response containing a list of secret keys", + "properties": { + "keys": { + "description": "List of secret keys", + "items": { "$ref": "#/components/schemas/v1.secretKeyResponse" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "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" + }, + "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" + } + }, + "type": "object" + }, + "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" + }, + "v1.registryInfo": { + "description": "Basic information about a registry", + "properties": { + "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/v1.RegistryType" }, + "version": { + "description": "Version of the registry schema", + "type": "string" + } + }, + "type": "object" + }, + "v1.registryListResponse": { + "description": "Response containing a list of registries", + "properties": { + "registries": { + "description": "List of registries", + "items": { "$ref": "#/components/schemas/v1.registryInfo" }, + "type": "array", + "uniqueItems": false + } + }, + "type": "object" + }, + "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" + }, + "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" + }, + "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" + }, + "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" + }, + "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, none)", + "type": "string" + } + }, + "type": "object" + }, + "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" + }, + "v1.toolOverride": { + "description": "Tool override", + "properties": { + "description": { + "description": "Description of the tool", + "type": "string" + }, + "name": { "description": "Name of the tool", "type": "string" } + }, + "type": "object" + }, + "v1.updateRequest": { + "description": "Request to update an existing workload (name cannot be changed)", + "properties": { + "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" + }, + "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.", + "type": "boolean" + }, + "oauth_config": { + "$ref": "#/components/schemas/v1.remoteOAuthConfig" + }, + "oidc": { "$ref": "#/components/schemas/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" + }, + "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/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" + }, + "v1.updateSecretRequest": { + "description": "Request to update an existing secret", + "properties": { + "value": { "description": "New secret value", "type": "string" } + }, + "type": "object" + }, + "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" + }, + "v1.versionResponse": { + "properties": { "version": { "type": "string" } }, + "type": "object" + }, + "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" + }, + "v1.workloadStatusResponse": { + "description": "Response containing workload status information", + "properties": { + "status": { + "description": "Current status of the workload", + "type": "string", + "x-enum-varnames": [ + "WorkloadStatusRunning", + "WorkloadStatusStopped", + "WorkloadStatusError", + "WorkloadStatusStarting", + "WorkloadStatusStopping", + "WorkloadStatusUnhealthy", + "WorkloadStatusRemoving", + "WorkloadStatusUnknown" + ] + } + }, + "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/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": { + "$ref": "#/components/schemas/v1.createClientRequest" + } + } + }, + "description": "Client to register", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.createClientResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Invalid request" + } + }, + "summary": "Register a new client", + "tags": ["clients"] + } + }, + "/api/v1beta/clients/register": { + "post": { + "description": "Register multiple clients with ToolHive", + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/v1.bulkClientRequest" } + } + }, + "description": "Clients to register", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/v1.createClientResponse" + }, + "type": "array" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Invalid request" + } + }, + "summary": "Register multiple clients", + "tags": ["clients"] + } + }, + "/api/v1beta/clients/unregister": { + "post": { + "description": "Unregister multiple clients from ToolHive", + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/v1.bulkClientRequest" } + } + }, + "description": "Clients to unregister", + "required": true + }, + "responses": { + "204": { "description": "No Content" }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Invalid request" + } + }, + "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" + } + }, + "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" + }, + "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", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/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": { "$ref": "#/components/schemas/v1.createGroupRequest" } + } + }, + "description": "Group creation request", + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/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/registry": { + "get": { + "description": "Get a list of the current registries", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/{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" + }, + "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/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": { + "$ref": "#/components/schemas/v1.UpdateRegistryRequest" + } + } + }, + "description": "Registry configuration", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.UpdateRegistryResponse" + } + } + }, + "description": "OK" + }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Bad Request" + }, + "404": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Not Found" + } + }, + "summary": "Update registry configuration", + "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/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/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": { + "$ref": "#/components/schemas/v1.setupSecretsRequest" + } + } + }, + "description": "Setup secrets provider request", + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/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/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": { + "$ref": "#/components/schemas/v1.createSecretRequest" + } + } + }, + "description": "Create secret request", + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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": { + "$ref": "#/components/schemas/v1.updateSecretRequest" + } + } + }, + "description": "Update secret request", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/version": { + "get": { + "description": "Returns the current version of the server", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/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/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", + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/v1.createRequest" } + } + }, + "description": "Create workload request", + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/v1.bulkOperationRequest" + } + } + }, + "description": "Bulk delete 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": "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": { + "$ref": "#/components/schemas/v1.bulkOperationRequest" + } + } + }, + "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": { + "$ref": "#/components/schemas/v1.bulkOperationRequest" + } + } + }, + "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/{name}": { + "delete": { + "description": "Delete a 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": "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/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", + "parameters": [ + { + "description": "Workload name", + "in": "path", + "name": "name", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/v1.updateRequest" } + } + }, + "description": "Update workload request", + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/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/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 100 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": { + "application/json": { "schema": { "type": "string" } }, + "text/plain": { "schema": { "type": "string" } } + }, + "description": "Logs for the specified workload" + }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Invalid workload name" + }, + "404": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Not Found" + } + }, + "summary": "Get logs for a specific workload", + "tags": ["logs"] + } + }, + "/api/v1beta/workloads/{name}/proxy-logs": { + "get": { + "description": "Retrieve 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": { + "application/json": { "schema": { "type": "string" } }, + "text/plain": { "schema": { "type": "string" } } + }, + "description": "Proxy logs for the specified workload" + }, + "400": { + "content": { + "application/json": { "schema": { "type": "string" } } + }, + "description": "Invalid workload name" + }, + "404": { + "content": { + "application/json": { "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/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"] + } + }, + "/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"] + } + } + }, + "openapi": "3.1.0" } diff --git a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts index adaec6696..9ac465555 100644 --- a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts +++ b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts @@ -23,6 +23,7 @@ const mockElectronAPI = { featureFlags: { get: vi.fn(), }, + getToolhivePort: vi.fn(), } Object.defineProperty(window, 'electronAPI', { @@ -55,6 +56,7 @@ describe('Meta Optimizer', () => { it('initialize and create group when it does not exist', async () => { mockElectronAPI.featureFlags.get.mockResolvedValue(true) + mockElectronAPI.getToolhivePort.mockResolvedValue(50055) const postGroupsSpy = vi.spyOn(apiSdk, 'postApiV1BetaGroups') @@ -79,6 +81,7 @@ describe('Meta Optimizer', () => { it('initialize and create group and workload when they do not exist', async () => { mockElectronAPI.featureFlags.get.mockResolvedValue(true) + mockElectronAPI.getToolhivePort.mockResolvedValue(50055) const postWorkloadsSpy = vi.spyOn(apiSdk, 'postApiV1BetaWorkloads') @@ -91,16 +94,24 @@ describe('Meta Optimizer', () => { http.get(mswEndpoint('/api/v1beta/workloads/:name'), () => HttpResponse.json({ error: 'Workload not found' }, { status: 404 }) ), - // Mock server from registry + // Mock server from registry - mcp-optimizer not found, falls back to meta-mcp http.get( mswEndpoint('/api/v1beta/registry/:name/servers/:serverName'), - () => - HttpResponse.json({ + ({ params }) => { + if (params.serverName === 'mcp-optimizer') { + return HttpResponse.json( + { error: 'Server not found' }, + { status: 404 } + ) + } + // Fallback to meta-mcp + return HttpResponse.json({ server: { image: 'ghcr.io/stackloklabs/meta-mcp:latest', transport: 'streamable-http', }, }) + } ) ) @@ -110,9 +121,19 @@ describe('Meta Optimizer', () => { body: expect.objectContaining({ name: META_MCP_SERVER_NAME, group: MCP_OPTIMIZER_GROUP_NAME, + // Image should come from registry (meta-mcp) image: 'ghcr.io/stackloklabs/meta-mcp:latest', transport: 'streamable-http', - env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default' }, + env_vars: { + [ALLOWED_GROUPS_ENV_VAR]: 'default', + TOOLHIVE_HOST: '127.0.0.1', + TOOLHIVE_PORT: '50055', + }, + permission_profile: { + network: { + mode: 'host', + }, + }, }), }) }) @@ -194,6 +215,7 @@ describe('Meta Optimizer', () => { it('handle workload creation failure', async () => { mockElectronAPI.featureFlags.get.mockResolvedValue(true) + mockElectronAPI.getToolhivePort.mockResolvedValue(50055) server.use( // Mock groups check - group doesn't exist @@ -205,13 +227,21 @@ describe('Meta Optimizer', () => { ), http.get( mswEndpoint('/api/v1beta/registry/:name/servers/:serverName'), - () => - HttpResponse.json({ + ({ params }) => { + if (params.serverName === 'mcp-optimizer') { + return HttpResponse.json( + { error: 'Server not found' }, + { status: 404 } + ) + } + // Fallback to meta-mcp + return HttpResponse.json({ server: { image: 'ghcr.io/stackloklabs/meta-mcp:latest', transport: 'streamable-http', }, }) + } ), http.post(mswEndpoint('/api/v1beta/workloads'), () => HttpResponse.json( @@ -231,6 +261,7 @@ describe('Meta Optimizer', () => { it('handle missing server from registry', async () => { mockElectronAPI.featureFlags.get.mockResolvedValue(true) + mockElectronAPI.getToolhivePort.mockResolvedValue(50055) server.use( // Mock groups check - group doesn't exist @@ -240,9 +271,19 @@ describe('Meta Optimizer', () => { http.get(mswEndpoint('/api/v1beta/workloads/:name'), () => HttpResponse.json({ error: 'Workload not found' }, { status: 404 }) ), + // Both mcp-optimizer and meta-mcp return null (not found) http.get( mswEndpoint('/api/v1beta/registry/:name/servers/:serverName'), - () => HttpResponse.json({ server: null }) + ({ params }) => { + if (params.serverName === 'mcp-optimizer') { + return HttpResponse.json( + { error: 'Server not found' }, + { status: 404 } + ) + } + // meta-mcp also returns null + return HttpResponse.json({ server: null }) + } ) ) diff --git a/renderer/src/common/lib/meta-optimizer.ts b/renderer/src/common/lib/meta-optimizer.ts index 9afbf652b..f7dd122c4 100644 --- a/renderer/src/common/lib/meta-optimizer.ts +++ b/renderer/src/common/lib/meta-optimizer.ts @@ -48,29 +48,72 @@ async function createMetaOptimizerWorkload() { return workloadDetail } - const { server } = await queryClient.fetchQuery( - getApiV1BetaRegistryByNameServersByServerNameOptions({ - path: { - name: 'default', - serverName: 'meta-mcp', - }, - }) - ) + // Try to fetch mcp-optimizer first, fall back to meta-mcp if not found + let server + try { + const response = await queryClient.fetchQuery( + getApiV1BetaRegistryByNameServersByServerNameOptions({ + path: { + name: 'default', + serverName: 'mcp-optimizer', + }, + }) + ) + server = response.server + } catch (error) { + log.info( + '[createMetaOptimizerWorkload] mcp-optimizer not found in registry, falling back to meta-mcp' + ) + // Fallback to meta-mcp registry entry + const response = await queryClient.fetchQuery( + getApiV1BetaRegistryByNameServersByServerNameOptions({ + path: { + name: 'default', + serverName: 'meta-mcp', + }, + }) + ) + server = response.server + } + if (!server) { log.info('[createMetaOptimizerWorkload] Server not found in the registry') return } + // Get the thv serve port from the main process + const toolhivePort = await window.electronAPI.getToolhivePort() + if (!toolhivePort) { + log.error( + '[createMetaOptimizerWorkload] ToolHive port not available, cannot create workload' + ) + return + } + const body: V1CreateRequest = { name: META_MCP_SERVER_NAME, - image: server.image, + // Use the latest mcp-optimizer image (fixed for x86_64) + image: 'ghcr.io/stackloklabs/mcp-optimizer:latest', transport: server.transport, - env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default' }, + // Use fixed port for host networking mode (avoids thv port management bugs) + target_port: 50051, + env_vars: { + [ALLOWED_GROUPS_ENV_VAR]: 'default', + // With host networking mode, localhost refers to the host machine + TOOLHIVE_HOST: '127.0.0.1', + // Pass explicit port to avoid port scanning issues with host networking + TOOLHIVE_PORT: String(toolhivePort), + }, secrets: [], cmd_arguments: [], network_isolation: false, volumes: [], group: MCP_OPTIMIZER_GROUP_NAME, + permission_profile: { + network: { + mode: 'host', + }, + }, } const response = await postApiV1BetaWorkloads({ diff --git a/utils/fetch-thv.ts b/utils/fetch-thv.ts index 4e808f934..bd7ef8108 100644 --- a/utils/fetch-thv.ts +++ b/utils/fetch-thv.ts @@ -41,7 +41,10 @@ async function fetchLatestRelease(): Promise { } async function checkBinaryVersion(binPath: string): Promise { - const latestTag = await fetchLatestRelease() + // DISABLED: Auto-update disabled for testing + return false + + /* const latestTag = await fetchLatestRelease() try { await access(binPath) @@ -64,7 +67,7 @@ async function checkBinaryVersion(binPath: string): Promise { return shouldDownload } catch { return true - } + } */ } async function cleanBinaryDirectory(binDir: string): Promise { From 3c097714f9a007e74adb11f0b817a24503e1863d Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Thu, 23 Oct 2025 18:49:20 +0200 Subject: [PATCH 5/7] . --- main/src/utils/mcp-tools.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main/src/utils/mcp-tools.ts b/main/src/utils/mcp-tools.ts index 4151eb89d..d5342b070 100644 --- a/main/src/utils/mcp-tools.ts +++ b/main/src/utils/mcp-tools.ts @@ -56,7 +56,10 @@ export function createTransport(workload: CoreWorkload): MCPClientConfig { }), }), 'streamable-http': () => { - const url = new URL(`http://localhost:${workload.port}/mcp`) + // Use fixed port for mcp-optimizer (host networking mode) + const port = + workload.name === 'internal---meta-mcp' ? 50051 : workload.port + const url = new URL(`http://localhost:${port}/mcp`) return { name: workload.name, transport: new StreamableHTTPClientTransport(url), From f43c6cc4f360f7ea5a64e5b9d0cd6c2df0c07f3c Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Fri, 24 Oct 2025 10:24:36 +0200 Subject: [PATCH 6/7] chore: re-enable auto-update and bump to thv v0.4.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-enable the auto-update logic in fetch-thv.ts that was temporarily disabled during host networking testing. Also update to thv v0.4.2 which includes the host networking support needed for mcp-optimizer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- api/generated/types.gen.ts | 5 +++++ api/openapi.json | 4 ++++ utils/constants.ts | 2 +- utils/fetch-thv.ts | 7 ++----- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/api/generated/types.gen.ts b/api/generated/types.gen.ts index 74fa231b7..25d80bbb0 100644 --- a/api/generated/types.gen.ts +++ b/api/generated/types.gen.ts @@ -67,6 +67,11 @@ export type AuthTokenValidatorConfig = { * ClientSecret is the optional OIDC client secret for introspection */ clientSecret?: string + /** + * InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing + * WARNING: This is insecure and should NEVER be used in production + */ + insecureAllowHTTP?: boolean /** * IntrospectionURL is the optional introspection endpoint for validating tokens */ diff --git a/api/openapi.json b/api/openapi.json index eece84426..ab7c82e14 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -66,6 +66,10 @@ "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" diff --git a/utils/constants.ts b/utils/constants.ts index f755be62d..68119ba9d 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -1 +1 @@ -export const TOOLHIVE_VERSION = process.env.THV_VERSION ?? 'v0.4.1' +export const TOOLHIVE_VERSION = process.env.THV_VERSION ?? 'v0.4.2' diff --git a/utils/fetch-thv.ts b/utils/fetch-thv.ts index bd7ef8108..4e808f934 100644 --- a/utils/fetch-thv.ts +++ b/utils/fetch-thv.ts @@ -41,10 +41,7 @@ async function fetchLatestRelease(): Promise { } async function checkBinaryVersion(binPath: string): Promise { - // DISABLED: Auto-update disabled for testing - return false - - /* const latestTag = await fetchLatestRelease() + const latestTag = await fetchLatestRelease() try { await access(binPath) @@ -67,7 +64,7 @@ async function checkBinaryVersion(binPath: string): Promise { return shouldDownload } catch { return true - } */ + } } async function cleanBinaryDirectory(binDir: string): Promise { From bc27c089918ba79bad5ee51bcfd4d5685f0a2bce Mon Sep 17 00:00:00 2001 From: Daniel Kantor Date: Fri, 24 Oct 2025 15:42:59 +0200 Subject: [PATCH 7/7] . --- main/src/utils/mcp-tools.ts | 20 +++++++++++-- .../lib/__tests__/meta-optimizer.test.ts | 6 ++-- renderer/src/common/lib/constants.ts | 11 +++++++ renderer/src/common/lib/meta-optimizer.ts | 30 ++++++++++++------- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/main/src/utils/mcp-tools.ts b/main/src/utils/mcp-tools.ts index d5342b070..7f32de283 100644 --- a/main/src/utils/mcp-tools.ts +++ b/main/src/utils/mcp-tools.ts @@ -8,6 +8,17 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/ import type { CoreWorkload } from '@api/types.gen' import log from '../logger' +/** + * Determines if the platform is probably using native containers. + * Native containers mean Docker runs directly on the host OS (Linux), + * as opposed to running in a VM (macOS/Windows with Docker Desktop). + * When native containers are used, host networking mode allows direct + * access to the host's network stack. + */ +function isProbablyUsingNativeContainers(): boolean { + return process.platform === 'linux' +} + export interface McpToolDefinition { description?: string inputSchema: Tool['inputSchema'] @@ -56,9 +67,12 @@ export function createTransport(workload: CoreWorkload): MCPClientConfig { }), }), 'streamable-http': () => { - // Use fixed port for mcp-optimizer (host networking mode) - const port = - workload.name === 'internal---meta-mcp' ? 50051 : workload.port + // On platforms with native containers (Linux), use fixed port for mcp-optimizer + // to work around thv port management bugs in host networking mode + const useFixedPort = + isProbablyUsingNativeContainers() && + workload.name === 'internal---meta-mcp' + const port = useFixedPort ? 50051 : workload.port const url = new URL(`http://localhost:${port}/mcp`) return { name: workload.name, diff --git a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts index 9ac465555..6eee67ef7 100644 --- a/renderer/src/common/lib/__tests__/meta-optimizer.test.ts +++ b/renderer/src/common/lib/__tests__/meta-optimizer.test.ts @@ -24,6 +24,7 @@ const mockElectronAPI = { get: vi.fn(), }, getToolhivePort: vi.fn(), + isLinux: true, // Tests run on Linux by default (for host networking tests) } Object.defineProperty(window, 'electronAPI', { @@ -121,9 +122,10 @@ describe('Meta Optimizer', () => { body: expect.objectContaining({ name: META_MCP_SERVER_NAME, group: MCP_OPTIMIZER_GROUP_NAME, - // Image should come from registry (meta-mcp) - image: 'ghcr.io/stackloklabs/meta-mcp:latest', + // Image is hardcoded to mcp-optimizer:latest + image: 'ghcr.io/stackloklabs/mcp-optimizer:latest', transport: 'streamable-http', + target_port: 50051, env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default', TOOLHIVE_HOST: '127.0.0.1', diff --git a/renderer/src/common/lib/constants.ts b/renderer/src/common/lib/constants.ts index 20a5e41c0..cd210207d 100644 --- a/renderer/src/common/lib/constants.ts +++ b/renderer/src/common/lib/constants.ts @@ -2,3 +2,14 @@ export const MCP_OPTIMIZER_GROUP_NAME = '__mcp-optimizer__' export const META_MCP_SERVER_NAME = 'internal---meta-mcp' export const ALLOWED_GROUPS_ENV_VAR = 'ALLOWED_GROUPS' export const MCP_OPTIMIZER_REGISTRY_SERVER_NAME = 'mcp-optimizer' + +/** + * Determines if the platform is probably using native containers. + * Native containers mean Docker runs directly on the host OS (Linux), + * as opposed to running in a VM (macOS/Windows with Docker Desktop). + * When native containers are used, host networking mode allows direct + * access to the host's network stack. + */ +export function isProbablyUsingNativeContainers(): boolean { + return window.electronAPI.isLinux +} diff --git a/renderer/src/common/lib/meta-optimizer.ts b/renderer/src/common/lib/meta-optimizer.ts index f7dd122c4..a48131b4d 100644 --- a/renderer/src/common/lib/meta-optimizer.ts +++ b/renderer/src/common/lib/meta-optimizer.ts @@ -16,6 +16,7 @@ import { META_MCP_SERVER_NAME, MCP_OPTIMIZER_GROUP_NAME, ALLOWED_GROUPS_ENV_VAR, + isProbablyUsingNativeContainers, } from './constants' async function ensureMetaOptimizerWorkload() { @@ -60,7 +61,7 @@ async function createMetaOptimizerWorkload() { }) ) server = response.server - } catch (error) { + } catch { log.info( '[createMetaOptimizerWorkload] mcp-optimizer not found in registry, falling back to meta-mcp' ) @@ -90,30 +91,37 @@ async function createMetaOptimizerWorkload() { return } + // On platforms with native containers (Linux), use host networking mode + // to allow the container to access the host's ToolHive API + const useHostNetworking = isProbablyUsingNativeContainers() + const body: V1CreateRequest = { name: META_MCP_SERVER_NAME, // Use the latest mcp-optimizer image (fixed for x86_64) image: 'ghcr.io/stackloklabs/mcp-optimizer:latest', transport: server.transport, - // Use fixed port for host networking mode (avoids thv port management bugs) - target_port: 50051, + // Use fixed port for host networking mode (avoids thv port management bugs on Linux) + ...(useHostNetworking && { target_port: 50051 }), env_vars: { [ALLOWED_GROUPS_ENV_VAR]: 'default', - // With host networking mode, localhost refers to the host machine - TOOLHIVE_HOST: '127.0.0.1', - // Pass explicit port to avoid port scanning issues with host networking - TOOLHIVE_PORT: String(toolhivePort), + // With host networking mode on Linux, localhost refers to the host machine + ...(useHostNetworking && { + TOOLHIVE_HOST: '127.0.0.1', + TOOLHIVE_PORT: String(toolhivePort), + }), }, secrets: [], cmd_arguments: [], network_isolation: false, volumes: [], group: MCP_OPTIMIZER_GROUP_NAME, - permission_profile: { - network: { - mode: 'host', + ...(useHostNetworking && { + permission_profile: { + network: { + mode: 'host', + }, }, - }, + }), } const response = await postApiV1BetaWorkloads({