diff --git a/core/pyproject.toml b/core/pyproject.toml index e9214da..c5c8f07 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "utcp" -version = "1.1.1" +version = "1.1.2" authors = [ { name = "UTCP Contributors" }, ] diff --git a/core/src/utcp/implementations/default_variable_substitutor.py b/core/src/utcp/implementations/default_variable_substitutor.py index ccc6dfb..b197d0b 100644 --- a/core/src/utcp/implementations/default_variable_substitutor.py +++ b/core/src/utcp/implementations/default_variable_substitutor.py @@ -100,8 +100,8 @@ def substitute(self, obj: dict | list | str, config: UtcpClientConfig, variable_ raise ValueError(f"Variable namespace '{variable_namespace}' contains invalid characters. Only alphanumeric characters and underscores are allowed.") if isinstance(obj, str): - # Skip substitution for JSON $ref strings - if '$ref' in obj: + # Skip substitution for JSON Schema $ref (but not variables like $refresh_token) + if re.search(r'\$ref(?![a-zA-Z0-9_])', obj): return obj # Use a regular expression to find all variables in the string, supporting ${VAR} and $VAR formats @@ -168,9 +168,10 @@ def find_required_variables(self, obj: dict | list | str, variable_namespace: Op result.extend(vars) return result elif isinstance(obj, str): - # Skip substitution for JSON $ref strings - if '$ref' in obj: + # Skip JSON Schema $ref (but not variables like $refresh_token) + if re.search(r'\$ref(?![a-zA-Z0-9_])', obj): return [] + # Find all variables in the string, supporting ${VAR} and $VAR formats variables = [] pattern = r'\${([a-zA-Z0-9_]+)}|\$([a-zA-Z0-9_]+)' diff --git a/plugins/communication_protocols/http/pyproject.toml b/plugins/communication_protocols/http/pyproject.toml index ae759ed..0ce7a83 100644 --- a/plugins/communication_protocols/http/pyproject.toml +++ b/plugins/communication_protocols/http/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "utcp-http" -version = "1.1.0" +version = "1.1.1" authors = [ { name = "UTCP Contributors" }, ] diff --git a/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py b/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py index c16412a..824df2a 100644 --- a/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py +++ b/plugins/communication_protocols/http/src/utcp_http/openapi_converter.py @@ -83,11 +83,12 @@ class OpenApiConverter: Attributes: spec: The parsed OpenAPI specification dictionary. spec_url: Optional URL where the specification was retrieved from. + base_url: Optional base URL override for all API endpoints. placeholder_counter: Counter for generating unique placeholder variables. call_template_name: Normalized name for the call_template derived from the spec. """ - def __init__(self, openapi_spec: Dict[str, Any], spec_url: Optional[str] = None, call_template_name: Optional[str] = None, auth_tools: Optional[Auth] = None): + def __init__(self, openapi_spec: Dict[str, Any], spec_url: Optional[str] = None, call_template_name: Optional[str] = None, auth_tools: Optional[Auth] = None, base_url: Optional[str] = None): """Initializes the OpenAPI converter. Args: @@ -98,10 +99,13 @@ def __init__(self, openapi_spec: Dict[str, Any], spec_url: Optional[str] = None, the specification title is not provided. auth_tools: Optional auth configuration for generated tools. Applied only to endpoints that require authentication per OpenAPI spec. + base_url: Optional base URL override for all API endpoints. + When provided, this takes precedence over servers in the spec. """ self.spec = openapi_spec self.spec_url = spec_url self.auth_tools = auth_tools + self._base_url_override = base_url # Single counter for all placeholder variables self.placeholder_counter = 0 if call_template_name is None: @@ -141,9 +145,12 @@ def convert(self) -> UtcpManual: """ self.placeholder_counter = 0 tools = [] - servers = self.spec.get("servers") - if servers: - base_url = servers[0].get("url", "/") + + # Determine base URL: override > servers > spec_url > fallback + if self._base_url_override: + base_url = self._base_url_override + elif self.spec.get("servers"): + base_url = self.spec["servers"][0].get("url", "/") elif self.spec_url: parsed_url = urlparse(self.spec_url) base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"