diff --git a/src/mcp_optimizer/toolhive/api_models/__init__.py b/src/mcp_optimizer/toolhive/api_models/__init__.py index 49be486..f8c19d5 100644 --- a/src/mcp_optimizer/toolhive/api_models/__init__.py +++ b/src/mcp_optimizer/toolhive/api_models/__init__.py @@ -1,3 +1,3 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 diff --git a/src/mcp_optimizer/toolhive/api_models/audit.py b/src/mcp_optimizer/toolhive/api_models/audit.py index efc4b3e..c02b352 100644 --- a/src/mcp_optimizer/toolhive/api_models/audit.py +++ b/src/mcp_optimizer/toolhive/api_models/audit.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/auth.py b/src/mcp_optimizer/toolhive/api_models/auth.py index ac6c288..b9099ee 100644 --- a/src/mcp_optimizer/toolhive/api_models/auth.py +++ b/src/mcp_optimizer/toolhive/api_models/auth.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/authz.py b/src/mcp_optimizer/toolhive/api_models/authz.py index 2a42525..60e8bb5 100644 --- a/src/mcp_optimizer/toolhive/api_models/authz.py +++ b/src/mcp_optimizer/toolhive/api_models/authz.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/client.py b/src/mcp_optimizer/toolhive/api_models/client.py index cb9fc4d..5fb36de 100644 --- a/src/mcp_optimizer/toolhive/api_models/client.py +++ b/src/mcp_optimizer/toolhive/api_models/client.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/core.py b/src/mcp_optimizer/toolhive/api_models/core.py index c3f209c..a36bebb 100644 --- a/src/mcp_optimizer/toolhive/api_models/core.py +++ b/src/mcp_optimizer/toolhive/api_models/core.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/groups.py b/src/mcp_optimizer/toolhive/api_models/groups.py index 5a0ad8b..b6e8707 100644 --- a/src/mcp_optimizer/toolhive/api_models/groups.py +++ b/src/mcp_optimizer/toolhive/api_models/groups.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/ignore.py b/src/mcp_optimizer/toolhive/api_models/ignore.py index 325eed6..17781e4 100644 --- a/src/mcp_optimizer/toolhive/api_models/ignore.py +++ b/src/mcp_optimizer/toolhive/api_models/ignore.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/permissions.py b/src/mcp_optimizer/toolhive/api_models/permissions.py index 4030d1e..29720f0 100644 --- a/src/mcp_optimizer/toolhive/api_models/permissions.py +++ b/src/mcp_optimizer/toolhive/api_models/permissions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/registry.py b/src/mcp_optimizer/toolhive/api_models/registry.py deleted file mode 100644 index 3a7d108..0000000 --- a/src/mcp_optimizer/toolhive/api_models/registry.py +++ /dev/null @@ -1,274 +0,0 @@ -# generated by datamodel-codegen: -# filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 - -from __future__ import annotations - -from typing import Any, Optional - -from pydantic import BaseModel, Field - -from . import permissions as permissions_1 - - -class EnvVar(BaseModel): - default: Optional[str] = Field( - None, - description='Default is the value to use if the environment variable is not explicitly provided\nOnly used for non-required variables', - ) - description: Optional[str] = Field( - None, - description="Description is a human-readable explanation of the variable's purpose", - ) - name: Optional[str] = Field( - None, description='Name is the environment variable name (e.g., API_KEY)' - ) - required: Optional[bool] = Field( - None, - 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', - ) - secret: Optional[bool] = Field( - None, - 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', - ) - - -class Header(BaseModel): - choices: Optional[list[str]] = Field( - None, - description='Choices provides a list of valid values for the header (optional)', - ) - default: Optional[str] = Field( - None, - description='Default is the value to use if the header is not explicitly provided\nOnly used for non-required headers', - ) - description: Optional[str] = Field( - None, - description="Description is a human-readable explanation of the header's purpose", - ) - name: Optional[str] = Field( - None, description='Name is the header name (e.g., X-API-Key, Authorization)' - ) - required: Optional[bool] = Field( - None, - 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', - ) - secret: Optional[bool] = Field( - None, - description='Secret indicates whether this header contains sensitive information\nIf true, the value will be stored as a secret rather than as plain text', - ) - - -class Metadata(BaseModel): - last_updated: Optional[str] = Field( - None, - description='LastUpdated is the timestamp when the server was last updated, in RFC3339 format', - ) - pulls: Optional[int] = Field( - None, - description='Pulls indicates how many times the server image has been downloaded', - ) - stars: Optional[int] = Field( - None, - description='Stars represents the popularity rating or number of stars for the server', - ) - - -class OAuthConfig(BaseModel): - authorize_url: Optional[str] = Field( - None, - description='AuthorizeURL is the OAuth authorization endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided', - ) - callback_port: Optional[int] = Field( - None, - description='CallbackPort is the specific port to use for the OAuth callback server\nIf not specified, a random available port will be used', - ) - client_id: Optional[str] = Field( - None, description='ClientID is the OAuth client ID for authentication' - ) - issuer: Optional[str] = Field( - None, - description='Issuer is the OAuth/OIDC issuer URL (e.g., https://accounts.google.com)\nUsed for OIDC discovery to find authorization and token endpoints', - ) - oauth_params: Optional[dict[str, str]] = Field( - None, - description='OAuthParams contains additional OAuth parameters to include in the authorization request\nThese are server-specific parameters like "prompt", "response_mode", etc.', - ) - scopes: Optional[list[str]] = Field( - None, - description='Scopes are the OAuth scopes to request\nIf not specified, defaults to ["openid", "profile", "email"] for OIDC', - ) - token_url: Optional[str] = Field( - None, - description='TokenURL is the OAuth token endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided', - ) - use_pkce: Optional[bool] = Field( - None, - description='UsePKCE indicates whether to use PKCE for the OAuth flow\nDefaults to true for enhanced security', - ) - - -class RemoteServerMetadata(BaseModel): - custom_metadata: Optional[dict[str, Any]] = Field( - None, description='CustomMetadata allows for additional user-defined metadata' - ) - description: Optional[str] = Field( - None, - description="Description is a human-readable description of the server's purpose and functionality", - ) - env_vars: Optional[list[EnvVar]] = Field( - None, - 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', - ) - headers: Optional[list[Header]] = Field( - None, - 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", - ) - metadata: Optional[Metadata] = None - name: Optional[str] = Field( - None, - 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', - ) - oauth_config: Optional[OAuthConfig] = None - repository_url: Optional[str] = Field( - None, - description='RepositoryURL is the URL to the source code repository for the server', - ) - status: Optional[str] = Field( - None, - description='Status indicates whether the server is currently active or deprecated', - ) - tags: Optional[list[str]] = Field( - None, - description='Tags are categorization labels for the server to aid in discovery and filtering', - ) - tier: Optional[str] = Field( - None, - description='Tier represents the tier classification level of the server, e.g., "Official" or "Community"', - ) - tools: Optional[list[str]] = Field( - None, description='Tools is a list of tool names provided by this MCP server' - ) - transport: Optional[str] = Field( - None, - 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)', - ) - url: Optional[str] = Field( - None, - description='URL is the endpoint URL for the remote MCP server (e.g., https://api.example.com/mcp)', - ) - - -class VerifiedAttestation(BaseModel): - predicate: Optional[Any] = None - predicate_type: Optional[str] = None - - -class Provenance(BaseModel): - attestation: Optional[VerifiedAttestation] = None - cert_issuer: Optional[str] = None - repository_ref: Optional[str] = None - repository_uri: Optional[str] = None - runner_environment: Optional[str] = None - signer_identity: Optional[str] = None - sigstore_url: Optional[str] = None - - -class ImageMetadata(BaseModel): - args: Optional[list[str]] = Field( - None, - 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.', - ) - custom_metadata: Optional[dict[str, Any]] = Field( - None, description='CustomMetadata allows for additional user-defined metadata' - ) - description: Optional[str] = Field( - None, - description="Description is a human-readable description of the server's purpose and functionality", - ) - docker_tags: Optional[list[str]] = Field( - None, - description='DockerTags lists the available Docker tags for this server image', - ) - env_vars: Optional[list[EnvVar]] = Field( - None, - description='EnvVars defines environment variables that can be passed to the server', - ) - image: Optional[str] = Field( - None, description='Image is the Docker image reference for the MCP server' - ) - metadata: Optional[Metadata] = None - name: Optional[str] = Field( - None, - 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', - ) - permissions: Optional[permissions_1.Profile] = None - provenance: Optional[Provenance] = None - repository_url: Optional[str] = Field( - None, - description='RepositoryURL is the URL to the source code repository for the server', - ) - status: Optional[str] = Field( - None, - description='Status indicates whether the server is currently active or deprecated', - ) - tags: Optional[list[str]] = Field( - None, - description='Tags are categorization labels for the server to aid in discovery and filtering', - ) - target_port: Optional[int] = Field( - None, - description='TargetPort is the port for the container to expose (only applicable to SSE and Streamable HTTP transports)', - ) - tier: Optional[str] = Field( - None, - description='Tier represents the tier classification level of the server, e.g., "Official" or "Community"', - ) - tools: Optional[list[str]] = Field( - None, description='Tools is a list of tool names provided by this MCP server' - ) - transport: Optional[str] = Field( - None, - 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)', - ) - - -class Group(BaseModel): - description: Optional[str] = Field( - None, - description="Description is a human-readable description of the group's purpose and functionality", - ) - name: Optional[str] = Field( - None, - description='Name is the identifier for the group, used when referencing the group in commands', - ) - remote_servers: Optional[dict[str, RemoteServerMetadata]] = Field( - None, - description='RemoteServers is a map of server names to their corresponding remote server definitions within this group', - ) - servers: Optional[dict[str, ImageMetadata]] = Field( - None, - description='Servers is a map of server names to their corresponding server definitions within this group', - ) - - -class Registry(BaseModel): - groups: Optional[list[Group]] = Field( - None, - description='Groups is a slice of group definitions containing related MCP servers', - ) - last_updated: Optional[str] = Field( - None, - description='LastUpdated is the timestamp when the registry was last updated, in RFC3339 format', - ) - remote_servers: Optional[dict[str, RemoteServerMetadata]] = Field( - None, - 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', - ) - servers: Optional[dict[str, ImageMetadata]] = Field( - None, - description='Servers is a map of server names to their corresponding server definitions', - ) - version: Optional[str] = Field( - None, description='Version is the schema version of the registry' - ) diff --git a/src/mcp_optimizer/toolhive/api_models/remote.py b/src/mcp_optimizer/toolhive/api_models/remote.py new file mode 100644 index 0000000..ac09676 --- /dev/null +++ b/src/mcp_optimizer/toolhive/api_models/remote.py @@ -0,0 +1,39 @@ +# generated by datamodel-codegen: +# filename: http://127.0.0.1:8080/api/openapi.json +# timestamp: 2025-11-16T00:38:59+00:00 + +from __future__ import annotations + +from typing import Optional + +from pydantic import BaseModel, Field + +from . import types + + +class Config(BaseModel): + authorize_url: Optional[str] = None + callback_port: Optional[int] = None + client_id: Optional[str] = None + client_secret: Optional[str] = None + client_secret_file: Optional[str] = None + env_vars: Optional[list[types.EnvVar]] = Field( + None, description='Environment variables for the client' + ) + headers: Optional[list[types.Header]] = Field( + None, description='Headers for HTTP requests' + ) + issuer: Optional[str] = Field( + None, description='OAuth endpoint configuration (from registry)' + ) + oauth_params: Optional[dict[str, str]] = Field( + None, description='OAuth parameters for server-specific customization' + ) + resource: Optional[str] = Field( + None, description='Resource is the OAuth 2.0 resource indicator (RFC 8707).' + ) + scopes: Optional[list[str]] = None + skip_browser: Optional[bool] = None + timeout: Optional[str] = Field(None, examples=['5m']) + token_url: Optional[str] = None + use_pkce: Optional[bool] = None diff --git a/src/mcp_optimizer/toolhive/api_models/runner.py b/src/mcp_optimizer/toolhive/api_models/runner.py index 8e19d4a..ca338db 100644 --- a/src/mcp_optimizer/toolhive/api_models/runner.py +++ b/src/mcp_optimizer/toolhive/api_models/runner.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations @@ -8,32 +8,17 @@ from pydantic import BaseModel, Field -from . import audit, auth, authz, ignore, permissions, registry, telemetry, types - - -class RemoteAuthConfig(BaseModel): - authorize_url: Optional[str] = None - callback_port: Optional[int] = None - client_id: Optional[str] = None - client_secret: Optional[str] = None - client_secret_file: Optional[str] = None - env_vars: Optional[list[registry.EnvVar]] = Field( - None, description='Environment variables for the client' - ) - headers: Optional[list[registry.Header]] = Field( - None, description='Headers for HTTP requests' - ) - issuer: Optional[str] = Field( - None, description='OAuth endpoint configuration (from registry)' - ) - oauth_params: Optional[dict[str, str]] = Field( - None, description='OAuth parameters for server-specific customization' - ) - scopes: Optional[list[str]] = None - skip_browser: Optional[bool] = None - timeout: Optional[str] = Field(None, examples=['5m']) - token_url: Optional[str] = None - use_pkce: Optional[bool] = None +from . import ( + audit, + auth, + authz, + ignore, + permissions, + remote, + telemetry, + tokenexchange, + types, +) class ToolOverride(BaseModel): @@ -116,7 +101,7 @@ class RunConfig(BaseModel): None, description='ProxyMode is the proxy mode for stdio transport ("sse" or "streamable-http")', ) - remote_auth_config: Optional[RemoteAuthConfig] = None + remote_auth_config: Optional[remote.Config] = None remote_url: Optional[str] = Field( None, description='RemoteURL is the URL of the remote MCP server (if running remotely)', @@ -141,6 +126,7 @@ class RunConfig(BaseModel): None, description='ThvCABundle is the path to the CA certificate bundle for ToolHive HTTP operations', ) + token_exchange_config: Optional[tokenexchange.Config] = None tools_filter: Optional[list[str]] = Field( None, description='ToolsFilter is the list of tools to filter' ) diff --git a/src/mcp_optimizer/toolhive/api_models/secrets.py b/src/mcp_optimizer/toolhive/api_models/secrets.py index fdd255b..2d3899b 100644 --- a/src/mcp_optimizer/toolhive/api_models/secrets.py +++ b/src/mcp_optimizer/toolhive/api_models/secrets.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/telemetry.py b/src/mcp_optimizer/toolhive/api_models/telemetry.py index 22baef1..eeb1c84 100644 --- a/src/mcp_optimizer/toolhive/api_models/telemetry.py +++ b/src/mcp_optimizer/toolhive/api_models/telemetry.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations diff --git a/src/mcp_optimizer/toolhive/api_models/tokenexchange.py b/src/mcp_optimizer/toolhive/api_models/tokenexchange.py new file mode 100644 index 0000000..be145a5 --- /dev/null +++ b/src/mcp_optimizer/toolhive/api_models/tokenexchange.py @@ -0,0 +1,40 @@ +# generated by datamodel-codegen: +# filename: http://127.0.0.1:8080/api/openapi.json +# timestamp: 2025-11-16T00:38:59+00:00 + +from __future__ import annotations + +from typing import Optional + +from pydantic import BaseModel, Field + + +class Config(BaseModel): + audience: Optional[str] = Field( + None, description='Audience is the target audience for the exchanged token' + ) + client_id: Optional[str] = Field( + None, description='ClientID is the OAuth 2.0 client identifier' + ) + client_secret: Optional[str] = Field( + None, description='ClientSecret is the OAuth 2.0 client secret' + ) + external_token_header_name: Optional[str] = Field( + None, + description='ExternalTokenHeaderName is the name of the custom header to use when HeaderStrategy is "custom"', + ) + header_strategy: Optional[str] = Field( + None, + description='HeaderStrategy determines how to inject the token\nValid values: HeaderStrategyReplace (default), HeaderStrategyCustom', + ) + scopes: Optional[list[str]] = Field( + None, + description='Scopes is the list of scopes to request for the exchanged token', + ) + subject_token_type: Optional[str] = Field( + None, + description='SubjectTokenType specifies the type of the subject token being exchanged.\nCommon values: tokenTypeAccessToken (default), tokenTypeIDToken, tokenTypeJWT.\nIf empty, defaults to tokenTypeAccessToken.', + ) + token_url: Optional[str] = Field( + None, description='TokenURL is the OAuth 2.0 token endpoint URL' + ) diff --git a/src/mcp_optimizer/toolhive/api_models/types.py b/src/mcp_optimizer/toolhive/api_models/types.py index 1cb259d..376fe5f 100644 --- a/src/mcp_optimizer/toolhive/api_models/types.py +++ b/src/mcp_optimizer/toolhive/api_models/types.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations @@ -8,6 +8,71 @@ from pydantic import BaseModel, Field, RootModel +from . import permissions as permissions_1 + + +class EnvVar(BaseModel): + default: Optional[str] = Field( + None, + description='Default is the value to use if the environment variable is not explicitly provided\nOnly used for non-required variables', + ) + description: Optional[str] = Field( + None, + description="Description is a human-readable explanation of the variable's purpose", + ) + name: Optional[str] = Field( + None, description='Name is the environment variable name (e.g., API_KEY)' + ) + required: Optional[bool] = Field( + None, + 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', + ) + secret: Optional[bool] = Field( + None, + 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', + ) + + +class Header(BaseModel): + choices: Optional[list[str]] = Field( + None, + description='Choices provides a list of valid values for the header (optional)', + ) + default: Optional[str] = Field( + None, + description='Default is the value to use if the header is not explicitly provided\nOnly used for non-required headers', + ) + description: Optional[str] = Field( + None, + description="Description is a human-readable explanation of the header's purpose", + ) + name: Optional[str] = Field( + None, description='Name is the header name (e.g., X-API-Key, Authorization)' + ) + required: Optional[bool] = Field( + None, + 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', + ) + secret: Optional[bool] = Field( + None, + description='Secret indicates whether this header contains sensitive information\nIf true, the value will be stored as a secret rather than as plain text', + ) + + +class Metadata(BaseModel): + last_updated: Optional[str] = Field( + None, + description='LastUpdated is the timestamp when the server was last updated, in RFC3339 format', + ) + pulls: Optional[int] = Field( + None, + description='Pulls indicates how many times the server image has been downloaded', + ) + stars: Optional[int] = Field( + None, + description='Stars represents the popularity rating or number of stars for the server', + ) + class MiddlewareConfig(BaseModel): parameters: Optional[dict[str, Any]] = Field( @@ -17,3 +82,206 @@ class MiddlewareConfig(BaseModel): type: Optional[str] = Field( None, description='Type is a string representing the middleware type.' ) + + +class OAuthConfig(BaseModel): + authorize_url: Optional[str] = Field( + None, + description='AuthorizeURL is the OAuth authorization endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided', + ) + callback_port: Optional[int] = Field( + None, + description='CallbackPort is the specific port to use for the OAuth callback server\nIf not specified, a random available port will be used', + ) + client_id: Optional[str] = Field( + None, description='ClientID is the OAuth client ID for authentication' + ) + issuer: Optional[str] = Field( + None, + description='Issuer is the OAuth/OIDC issuer URL (e.g., https://accounts.google.com)\nUsed for OIDC discovery to find authorization and token endpoints', + ) + oauth_params: Optional[dict[str, str]] = Field( + None, + description='OAuthParams contains additional OAuth parameters to include in the authorization request\nThese are server-specific parameters like "prompt", "response_mode", etc.', + ) + resource: Optional[str] = Field( + None, description='Resource is the OAuth 2.0 resource indicator (RFC 8707)' + ) + scopes: Optional[list[str]] = Field( + None, + description='Scopes are the OAuth scopes to request\nIf not specified, defaults to ["openid", "profile", "email"] for OIDC', + ) + token_url: Optional[str] = Field( + None, + description='TokenURL is the OAuth token endpoint URL\nUsed for non-OIDC OAuth flows when issuer is not provided', + ) + use_pkce: Optional[bool] = Field( + None, + description='UsePKCE indicates whether to use PKCE for the OAuth flow\nDefaults to true for enhanced security', + ) + + +class RemoteServerMetadata(BaseModel): + custom_metadata: Optional[dict[str, Any]] = Field( + None, description='CustomMetadata allows for additional user-defined metadata' + ) + description: Optional[str] = Field( + None, + description="Description is a human-readable description of the server's purpose and functionality", + ) + env_vars: Optional[list[EnvVar]] = Field( + None, + 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', + ) + headers: Optional[list[Header]] = Field( + None, + 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", + ) + metadata: Optional[Metadata] = None + name: Optional[str] = Field( + None, + 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', + ) + oauth_config: Optional[OAuthConfig] = None + repository_url: Optional[str] = Field( + None, + description='RepositoryURL is the URL to the source code repository for the server', + ) + status: Optional[str] = Field( + None, + description='Status indicates whether the server is currently active or deprecated', + ) + tags: Optional[list[str]] = Field( + None, + description='Tags are categorization labels for the server to aid in discovery and filtering', + ) + tier: Optional[str] = Field( + None, + description='Tier represents the tier classification level of the server, e.g., "Official" or "Community"', + ) + tools: Optional[list[str]] = Field( + None, description='Tools is a list of tool names provided by this MCP server' + ) + transport: Optional[str] = Field( + None, + 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)', + ) + url: Optional[str] = Field( + None, + description='URL is the endpoint URL for the remote MCP server (e.g., https://api.example.com/mcp)', + ) + + +class VerifiedAttestation(BaseModel): + predicate: Optional[Any] = None + predicate_type: Optional[str] = None + + +class Provenance(BaseModel): + attestation: Optional[VerifiedAttestation] = None + cert_issuer: Optional[str] = None + repository_ref: Optional[str] = None + repository_uri: Optional[str] = None + runner_environment: Optional[str] = None + signer_identity: Optional[str] = None + sigstore_url: Optional[str] = None + + +class ImageMetadata(BaseModel): + args: Optional[list[str]] = Field( + None, + 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.', + ) + custom_metadata: Optional[dict[str, Any]] = Field( + None, description='CustomMetadata allows for additional user-defined metadata' + ) + description: Optional[str] = Field( + None, + description="Description is a human-readable description of the server's purpose and functionality", + ) + docker_tags: Optional[list[str]] = Field( + None, + description='DockerTags lists the available Docker tags for this server image', + ) + env_vars: Optional[list[EnvVar]] = Field( + None, + description='EnvVars defines environment variables that can be passed to the server', + ) + image: Optional[str] = Field( + None, description='Image is the Docker image reference for the MCP server' + ) + metadata: Optional[Metadata] = None + name: Optional[str] = Field( + None, + 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', + ) + permissions: Optional[permissions_1.Profile] = None + provenance: Optional[Provenance] = None + repository_url: Optional[str] = Field( + None, + description='RepositoryURL is the URL to the source code repository for the server', + ) + status: Optional[str] = Field( + None, + description='Status indicates whether the server is currently active or deprecated', + ) + tags: Optional[list[str]] = Field( + None, + description='Tags are categorization labels for the server to aid in discovery and filtering', + ) + target_port: Optional[int] = Field( + None, + description='TargetPort is the port for the container to expose (only applicable to SSE and Streamable HTTP transports)', + ) + tier: Optional[str] = Field( + None, + description='Tier represents the tier classification level of the server, e.g., "Official" or "Community"', + ) + tools: Optional[list[str]] = Field( + None, description='Tools is a list of tool names provided by this MCP server' + ) + transport: Optional[str] = Field( + None, + 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)', + ) + + +class Group(BaseModel): + description: Optional[str] = Field( + None, + description="Description is a human-readable description of the group's purpose and functionality", + ) + name: Optional[str] = Field( + None, + description='Name is the identifier for the group, used when referencing the group in commands', + ) + remote_servers: Optional[dict[str, RemoteServerMetadata]] = Field( + None, + description='RemoteServers is a map of server names to their corresponding remote server definitions within this group', + ) + servers: Optional[dict[str, ImageMetadata]] = Field( + None, + description='Servers is a map of server names to their corresponding server definitions within this group', + ) + + +class Registry(BaseModel): + groups: Optional[list[Group]] = Field( + None, + description='Groups is a slice of group definitions containing related MCP servers', + ) + last_updated: Optional[str] = Field( + None, + description='LastUpdated is the timestamp when the registry was last updated, in RFC3339 format', + ) + remote_servers: Optional[dict[str, RemoteServerMetadata]] = Field( + None, + 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', + ) + servers: Optional[dict[str, ImageMetadata]] = Field( + None, + description='Servers is a map of server names to their corresponding server definitions', + ) + version: Optional[str] = Field( + None, description='Version is the schema version of the registry' + ) diff --git a/src/mcp_optimizer/toolhive/api_models/v1.py b/src/mcp_optimizer/toolhive/api_models/v1.py index 1abc279..137e0e5 100644 --- a/src/mcp_optimizer/toolhive/api_models/v1.py +++ b/src/mcp_optimizer/toolhive/api_models/v1.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: http://127.0.0.1:8080/api/openapi.json -# timestamp: 2025-11-02T00:37:46+00:00 +# timestamp: 2025-11-16T00:38:59+00:00 from __future__ import annotations @@ -11,14 +11,15 @@ from . import client, core from . import groups as groups_1 from . import permissions -from . import registry as registry_1 from . import secrets as secrets_1 +from . import types class UpdateRegistryRequest(BaseModel): allow_private_ip: Optional[bool] = Field( - None, description='Allow private IP addresses for registry URL' + None, description='Allow private IP addresses for registry URL or API URL' ) + api_url: Optional[str] = Field(None, description='MCP Registry API URL') local_path: Optional[str] = Field(None, description='Local registry file path') url: Optional[str] = Field(None, description='Registry URL (for remote registries)') @@ -165,6 +166,9 @@ class RemoteOAuthConfig(BaseModel): None, description='Additional OAuth parameters for server-specific customization', ) + resource: Optional[str] = Field( + None, description='OAuth 2.0 resource indicator (RFC 8707)' + ) scopes: Optional[list[str]] = Field(None, description='OAuth scopes to request') skip_browser: Optional[bool] = Field( None, @@ -236,7 +240,7 @@ class CreateRequest(BaseModel): group: Optional[str] = Field( None, description='Group name this workload belongs to' ) - headers: Optional[list[registry_1.Header]] = None + headers: Optional[list[types.Header]] = None host: Optional[str] = Field(None, description='Host to bind to') image: Optional[str] = Field(None, description='Docker image to use') name: Optional[str] = Field(None, description='Name of the workload') @@ -294,7 +298,7 @@ class UpdateRequest(BaseModel): group: Optional[str] = Field( None, description='Group name this workload belongs to' ) - headers: Optional[list[registry_1.Header]] = None + headers: Optional[list[types.Header]] = None host: Optional[str] = Field(None, description='Host to bind to') image: Optional[str] = Field(None, description='Docker image to use') network_isolation: Optional[bool] = Field( @@ -336,15 +340,15 @@ class GetServerResponse(BaseModel): is_remote: Optional[bool] = Field( None, description='Indicates if this is a remote server' ) - remote_server: Optional[registry_1.RemoteServerMetadata] = None - server: Optional[registry_1.ImageMetadata] = None + remote_server: Optional[types.RemoteServerMetadata] = None + server: Optional[types.ImageMetadata] = None class ListServersResponse(BaseModel): - remote_servers: Optional[list[registry_1.RemoteServerMetadata]] = Field( + remote_servers: Optional[list[types.RemoteServerMetadata]] = Field( None, description='List of remote servers in the registry (if any)' ) - servers: Optional[list[registry_1.ImageMetadata]] = Field( + servers: Optional[list[types.ImageMetadata]] = Field( None, description='List of container servers in the registry' ) @@ -352,7 +356,7 @@ class ListServersResponse(BaseModel): class GetRegistryResponse(BaseModel): last_updated: Optional[str] = Field(None, description='Last updated timestamp') name: Optional[str] = Field(None, description='Name of the registry') - registry: Optional[registry_1.Registry] = None + registry: Optional[types.Registry] = None server_count: Optional[int] = Field( None, description='Number of servers in the registry' )