awslabs.openapi-mcp-server 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. awslabs/__init__.py +16 -0
  2. awslabs/openapi_mcp_server/__init__.py +69 -0
  3. awslabs/openapi_mcp_server/api/__init__.py +18 -0
  4. awslabs/openapi_mcp_server/api/config.py +200 -0
  5. awslabs/openapi_mcp_server/auth/__init__.py +27 -0
  6. awslabs/openapi_mcp_server/auth/api_key_auth.py +185 -0
  7. awslabs/openapi_mcp_server/auth/auth_cache.py +190 -0
  8. awslabs/openapi_mcp_server/auth/auth_errors.py +206 -0
  9. awslabs/openapi_mcp_server/auth/auth_factory.py +146 -0
  10. awslabs/openapi_mcp_server/auth/auth_protocol.py +63 -0
  11. awslabs/openapi_mcp_server/auth/auth_provider.py +160 -0
  12. awslabs/openapi_mcp_server/auth/base_auth.py +218 -0
  13. awslabs/openapi_mcp_server/auth/basic_auth.py +171 -0
  14. awslabs/openapi_mcp_server/auth/bearer_auth.py +108 -0
  15. awslabs/openapi_mcp_server/auth/cognito_auth.py +538 -0
  16. awslabs/openapi_mcp_server/auth/register.py +100 -0
  17. awslabs/openapi_mcp_server/patch/__init__.py +17 -0
  18. awslabs/openapi_mcp_server/prompts/__init__.py +18 -0
  19. awslabs/openapi_mcp_server/prompts/generators/__init__.py +22 -0
  20. awslabs/openapi_mcp_server/prompts/generators/operation_prompts.py +642 -0
  21. awslabs/openapi_mcp_server/prompts/generators/workflow_prompts.py +257 -0
  22. awslabs/openapi_mcp_server/prompts/models.py +70 -0
  23. awslabs/openapi_mcp_server/prompts/prompt_manager.py +150 -0
  24. awslabs/openapi_mcp_server/server.py +511 -0
  25. awslabs/openapi_mcp_server/utils/__init__.py +18 -0
  26. awslabs/openapi_mcp_server/utils/cache_provider.py +249 -0
  27. awslabs/openapi_mcp_server/utils/config.py +35 -0
  28. awslabs/openapi_mcp_server/utils/error_handler.py +349 -0
  29. awslabs/openapi_mcp_server/utils/http_client.py +263 -0
  30. awslabs/openapi_mcp_server/utils/metrics_provider.py +503 -0
  31. awslabs/openapi_mcp_server/utils/openapi.py +217 -0
  32. awslabs/openapi_mcp_server/utils/openapi_validator.py +253 -0
  33. awslabs_openapi_mcp_server-0.1.1.dist-info/METADATA +418 -0
  34. awslabs_openapi_mcp_server-0.1.1.dist-info/RECORD +38 -0
  35. awslabs_openapi_mcp_server-0.1.1.dist-info/WHEEL +4 -0
  36. awslabs_openapi_mcp_server-0.1.1.dist-info/entry_points.txt +2 -0
  37. awslabs_openapi_mcp_server-0.1.1.dist-info/licenses/LICENSE +175 -0
  38. awslabs_openapi_mcp_server-0.1.1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,160 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Base authentication provider interface."""
15
+
16
+ import abc
17
+ import httpx
18
+ from typing import Any, Dict, Optional
19
+
20
+
21
+ class AuthProvider(abc.ABC):
22
+ """Abstract base class for authentication providers.
23
+
24
+ Authentication providers handle different authentication methods for APIs.
25
+ Implementing classes must provide methods for setting up authentication
26
+ for HTTP requests.
27
+ """
28
+
29
+ @abc.abstractmethod
30
+ def get_auth_headers(self) -> Dict[str, str]:
31
+ """Get authentication headers for HTTP requests.
32
+
33
+ Returns:
34
+ Dict[str, str]: Headers to include in HTTP requests
35
+
36
+ """
37
+ pass
38
+
39
+ @abc.abstractmethod
40
+ def get_auth_params(self) -> Dict[str, str]:
41
+ """Get authentication query parameters for HTTP requests.
42
+
43
+ Returns:
44
+ Dict[str, str]: Query parameters to include in HTTP requests
45
+
46
+ """
47
+ pass
48
+
49
+ @abc.abstractmethod
50
+ def get_auth_cookies(self) -> Dict[str, str]:
51
+ """Get authentication cookies for HTTP requests.
52
+
53
+ Returns:
54
+ Dict[str, str]: Cookies to include in HTTP requests
55
+
56
+ """
57
+ pass
58
+
59
+ @abc.abstractmethod
60
+ def get_httpx_auth(self) -> Optional[httpx.Auth]:
61
+ """Get authentication object for HTTPX.
62
+
63
+ Returns:
64
+ Optional[httpx.Auth]: Authentication object for HTTPX client or None
65
+
66
+ """
67
+ pass
68
+
69
+ @abc.abstractmethod
70
+ def is_configured(self) -> bool:
71
+ """Check if the authentication provider is properly configured.
72
+
73
+ Returns:
74
+ bool: True if configured, False otherwise
75
+
76
+ """
77
+ pass
78
+
79
+ @property
80
+ @abc.abstractmethod
81
+ def provider_name(self) -> str:
82
+ """Get the name of the authentication provider.
83
+
84
+ Returns:
85
+ str: Name of the authentication provider
86
+
87
+ """
88
+ pass
89
+
90
+
91
+ class NullAuthProvider(AuthProvider):
92
+ """No-op authentication provider.
93
+
94
+ This provider is used when authentication is disabled or not configured.
95
+ """
96
+
97
+ def __init__(self, config: Any = None):
98
+ """Initialize with optional configuration.
99
+
100
+ Args:
101
+ config: Optional configuration object (ignored by this provider)
102
+
103
+ """
104
+ # Config is ignored by this provider
105
+ pass
106
+
107
+ def get_auth_headers(self) -> Dict[str, str]:
108
+ """Get authentication headers for HTTP requests.
109
+
110
+ Returns:
111
+ Dict[str, str]: Empty dict as no authentication is provided
112
+
113
+ """
114
+ return {}
115
+
116
+ def get_auth_params(self) -> Dict[str, str]:
117
+ """Get authentication query parameters for HTTP requests.
118
+
119
+ Returns:
120
+ Dict[str, str]: Empty dict as no authentication is provided
121
+
122
+ """
123
+ return {}
124
+
125
+ def get_auth_cookies(self) -> Dict[str, str]:
126
+ """Get authentication cookies for HTTP requests.
127
+
128
+ Returns:
129
+ Dict[str, str]: Empty dict as no authentication is provided
130
+
131
+ """
132
+ return {}
133
+
134
+ def get_httpx_auth(self) -> Optional[httpx.Auth]:
135
+ """Get authentication object for HTTPX.
136
+
137
+ Returns:
138
+ Optional[httpx.Auth]: None as no authentication is provided
139
+
140
+ """
141
+ return None
142
+
143
+ def is_configured(self) -> bool:
144
+ """Check if the authentication provider is properly configured.
145
+
146
+ Returns:
147
+ bool: Always True as null provider requires no configuration
148
+
149
+ """
150
+ return True
151
+
152
+ @property
153
+ def provider_name(self) -> str:
154
+ """Get the name of the authentication provider.
155
+
156
+ Returns:
157
+ str: Name of the authentication provider
158
+
159
+ """
160
+ return 'none'
@@ -0,0 +1,218 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Base authentication provider."""
15
+
16
+ import functools
17
+ import httpx
18
+ from abc import ABC, abstractmethod
19
+ from awslabs.openapi_mcp_server import logger
20
+ from awslabs.openapi_mcp_server.api.config import Config
21
+ from awslabs.openapi_mcp_server.auth.auth_errors import (
22
+ AuthError,
23
+ ConfigurationError,
24
+ format_error_message,
25
+ )
26
+ from awslabs.openapi_mcp_server.auth.auth_provider import AuthProvider
27
+ from typing import Any, Callable, Dict, Optional, TypeVar, cast
28
+
29
+
30
+ # Type variable for method return types
31
+ T = TypeVar('T')
32
+
33
+
34
+ class BaseAuthProvider(AuthProvider, ABC):
35
+ """Base authentication provider.
36
+
37
+ This abstract base class provides common functionality for all authentication providers.
38
+ It implements the Template Method pattern for configuration validation and error handling.
39
+ """
40
+
41
+ def __init__(self, config: Config):
42
+ """Initialize with configuration.
43
+
44
+ Args:
45
+ config: Application configuration
46
+
47
+ """
48
+ self._config = config
49
+ self._is_valid = False
50
+ self._auth_headers: Dict[str, str] = {}
51
+ self._auth_params: Dict[str, str] = {}
52
+ self._auth_cookies: Dict[str, str] = {}
53
+ self._validation_error: Optional[AuthError] = None
54
+
55
+ # Template method pattern: validate and initialize
56
+ try:
57
+ self._is_valid = self._validate_config()
58
+ if self._is_valid:
59
+ self._initialize_auth()
60
+ else:
61
+ self._handle_validation_error()
62
+ except AuthError as e:
63
+ self._validation_error = e
64
+ self._is_valid = False
65
+ self._log_auth_error(e)
66
+ # Re-raise the exception for test cases to catch
67
+ raise e
68
+ except Exception as e:
69
+ self._validation_error = ConfigurationError(
70
+ f'Unexpected error during authentication provider initialization: {str(e)}'
71
+ )
72
+ self._is_valid = False
73
+ self._log_auth_error(self._validation_error)
74
+ # Re-raise the exception for test cases to catch
75
+ raise self._validation_error
76
+
77
+ def _initialize_auth(self) -> None:
78
+ """Initialize authentication data after validation.
79
+
80
+ This method is called after successful validation to set up
81
+ headers, params, and cookies. Override in subclasses if needed.
82
+ """
83
+ pass
84
+
85
+ @abstractmethod
86
+ def _validate_config(self) -> bool:
87
+ """Validate the configuration.
88
+
89
+ Returns:
90
+ bool: True if configuration is valid, False otherwise
91
+
92
+ Raises:
93
+ AuthError: If validation fails with a specific error
94
+
95
+ """
96
+ pass
97
+
98
+ def _handle_validation_error(self) -> None:
99
+ """Handle validation error.
100
+
101
+ This method is called when validation fails but no exception is raised.
102
+ It should create and log an appropriate error. Override in subclasses.
103
+ """
104
+ self._validation_error = ConfigurationError(
105
+ f'Invalid configuration for {self.provider_name} authentication provider'
106
+ )
107
+ self._log_auth_error(self._validation_error)
108
+
109
+ def _log_auth_error(self, error: AuthError) -> None:
110
+ """Log an authentication error.
111
+
112
+ Args:
113
+ error: The authentication error
114
+
115
+ """
116
+ message = format_error_message(self.provider_name, error.error_type, error.message)
117
+ logger.error(message)
118
+
119
+ # Log additional details at debug level
120
+ if error.details:
121
+ logger.debug(f'Error details: {error.details}')
122
+
123
+ def _log_validation_error(self) -> None:
124
+ """Log validation error messages.
125
+
126
+ This method is kept for backward compatibility.
127
+ New implementations should use _handle_validation_error instead.
128
+ """
129
+ self._handle_validation_error()
130
+
131
+ def _requires_valid_config(method: Callable[..., T]) -> Callable[..., T]: # type: ignore
132
+ """Ensure a method is only called with valid configuration.
133
+
134
+ If the configuration is not valid, returns an empty result.
135
+ """
136
+
137
+ @functools.wraps(method)
138
+ def wrapper(self: 'BaseAuthProvider', *args: Any, **kwargs: Any) -> T:
139
+ if not self._is_valid:
140
+ # Return empty result based on return type annotation
141
+ return_type = method.__annotations__.get('return')
142
+ if return_type == Dict[str, str]:
143
+ return cast(T, {})
144
+ elif return_type == Optional[httpx.Auth]:
145
+ return cast(T, None)
146
+ return cast(T, None)
147
+ return method(self, *args, **kwargs)
148
+
149
+ return wrapper
150
+
151
+ @_requires_valid_config
152
+ def get_auth_headers(self) -> Dict[str, str]:
153
+ """Get authentication headers for HTTP requests.
154
+
155
+ Returns:
156
+ Dict[str, str]: Authentication headers
157
+
158
+ """
159
+ return self._auth_headers
160
+
161
+ @_requires_valid_config
162
+ def get_auth_params(self) -> Dict[str, str]:
163
+ """Get authentication query parameters for HTTP requests.
164
+
165
+ Returns:
166
+ Dict[str, str]: Authentication query parameters
167
+
168
+ """
169
+ return self._auth_params
170
+
171
+ @_requires_valid_config
172
+ def get_auth_cookies(self) -> Dict[str, str]:
173
+ """Get authentication cookies for HTTP requests.
174
+
175
+ Returns:
176
+ Dict[str, str]: Authentication cookies
177
+
178
+ """
179
+ return self._auth_cookies
180
+
181
+ @_requires_valid_config
182
+ def get_httpx_auth(self) -> Optional[httpx.Auth]:
183
+ """Get authentication object for HTTPX.
184
+
185
+ Returns:
186
+ Optional[httpx.Auth]: Authentication object for HTTPX client
187
+
188
+ """
189
+ return None
190
+
191
+ def is_configured(self) -> bool:
192
+ """Check if the authentication provider is properly configured.
193
+
194
+ Returns:
195
+ bool: True if properly configured, False otherwise
196
+
197
+ """
198
+ return self._is_valid
199
+
200
+ def get_validation_error(self) -> Optional[AuthError]:
201
+ """Get the validation error if configuration is invalid.
202
+
203
+ Returns:
204
+ Optional[AuthError]: The validation error or None if configuration is valid
205
+
206
+ """
207
+ return self._validation_error
208
+
209
+ @property
210
+ @abstractmethod
211
+ def provider_name(self) -> str:
212
+ """Get the name of the authentication provider.
213
+
214
+ Returns:
215
+ str: Name of the authentication provider
216
+
217
+ """
218
+ pass
@@ -0,0 +1,171 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Basic authentication provider."""
15
+
16
+ import base64
17
+ import httpx
18
+ from awslabs.openapi_mcp_server import logger
19
+ from awslabs.openapi_mcp_server.api.config import Config
20
+ from awslabs.openapi_mcp_server.auth.auth_cache import cached_auth_data
21
+ from awslabs.openapi_mcp_server.auth.auth_errors import MissingCredentialsError
22
+ from awslabs.openapi_mcp_server.auth.base_auth import BaseAuthProvider
23
+ from typing import Dict, Optional
24
+
25
+
26
+ class BasicAuthProvider(BaseAuthProvider):
27
+ """Basic authentication provider.
28
+
29
+ This provider adds an Authorization header with Basic authentication
30
+ to all HTTP requests.
31
+ """
32
+
33
+ def __init__(self, config: Config):
34
+ """Initialize with configuration.
35
+
36
+ Args:
37
+ config: Application configuration
38
+
39
+ """
40
+ # Store credentials before calling super().__init__
41
+ self._username = config.auth_username
42
+ self._password = config.auth_password
43
+ self._httpx_auth: Optional[httpx.Auth] = None
44
+ self._credentials_hash = None
45
+
46
+ # Call parent initializer which will validate and initialize auth
47
+ super().__init__(config)
48
+
49
+ def _validate_config(self) -> bool:
50
+ """Validate the configuration.
51
+
52
+ Returns:
53
+ bool: True if username and password are provided, False otherwise
54
+
55
+ Raises:
56
+ MissingCredentialsError: If username or password is missing
57
+
58
+ """
59
+ if not self._username:
60
+ raise MissingCredentialsError(
61
+ 'Basic authentication requires a username',
62
+ {
63
+ 'help': 'Provide a username using --auth-username command line argument or AUTH_USERNAME environment variable'
64
+ },
65
+ )
66
+
67
+ if not self._password:
68
+ raise MissingCredentialsError(
69
+ 'Basic authentication requires a password',
70
+ {
71
+ 'help': 'Provide a password using --auth-password command line argument or AUTH_PASSWORD environment variable'
72
+ },
73
+ )
74
+
75
+ # Create a hash of the credentials for caching
76
+ self._credentials_hash = self._hash_credentials(self._username, self._password)
77
+ return True
78
+
79
+ def _log_validation_error(self) -> None:
80
+ """Log validation error messages."""
81
+ logger.error(
82
+ 'Basic authentication requires both username and password. Please provide them using --auth-username and --auth-password command line arguments or AUTH_USERNAME and AUTH_PASSWORD environment variables.'
83
+ )
84
+
85
+ def _initialize_auth(self) -> None:
86
+ """Initialize authentication data after validation."""
87
+ # Use cached methods to generate auth data
88
+ self._auth_headers = self._generate_auth_headers(self._credentials_hash)
89
+ self._httpx_auth = self._generate_httpx_auth(self._username, self._password)
90
+
91
+ @staticmethod
92
+ def _hash_credentials(username: str, password: str) -> str:
93
+ """Create a hash of the credentials for caching.
94
+
95
+ Args:
96
+ username: Username
97
+ password: Password
98
+
99
+ Returns:
100
+ str: Hash of the credentials
101
+
102
+ """
103
+ # Create a hash of the credentials to use as a cache key
104
+ # This avoids storing the actual credentials in the cache key
105
+ # Using bcrypt for stronger security
106
+ import bcrypt
107
+
108
+ credentials = f'{username}:{password}'
109
+ # Generate a salt and hash the credentials
110
+ # We only need a string representation for caching, so we'll use the hexdigest of the hash
111
+ hashed = bcrypt.hashpw(credentials.encode('utf-8'), bcrypt.gensalt(rounds=10))
112
+ # Convert to hex string for consistent cache key format
113
+ return hashed.hex()
114
+
115
+ @cached_auth_data(ttl=3600) # Cache for 1 hour by default
116
+ def _generate_auth_headers(self, credentials_hash: str) -> Dict[str, str]:
117
+ """Generate authentication headers.
118
+
119
+ This method is cached to avoid regenerating headers for the same credentials.
120
+
121
+ Args:
122
+ credentials_hash: Hash of the credentials
123
+
124
+ Returns:
125
+ Dict[str, str]: Authentication headers
126
+
127
+ """
128
+ logger.debug(f'Generating new basic auth headers for user: {self._username}')
129
+
130
+ # Create the basic auth header
131
+ auth_string = f'{self._username}:{self._password}'
132
+ auth_bytes = auth_string.encode('utf-8')
133
+ encoded_auth = base64.b64encode(auth_bytes).decode('utf-8')
134
+
135
+ return {'Authorization': f'Basic {encoded_auth}'}
136
+
137
+ @cached_auth_data(ttl=3600) # Cache for 1 hour by default
138
+ def _generate_httpx_auth(self, username: str, password: str) -> httpx.BasicAuth:
139
+ """Generate HTTPX auth object.
140
+
141
+ This method is cached to avoid regenerating auth objects for the same credentials.
142
+
143
+ Args:
144
+ username: Username
145
+ password: Password
146
+
147
+ Returns:
148
+ httpx.BasicAuth: HTTPX auth object
149
+
150
+ """
151
+ logger.debug(f'Generating new HTTPX basic auth object for user: {username}')
152
+ return httpx.BasicAuth(username=username, password=password)
153
+
154
+ def get_httpx_auth(self) -> Optional[httpx.Auth]:
155
+ """Get authentication object for HTTPX.
156
+
157
+ Returns:
158
+ Optional[httpx.Auth]: Basic auth object for HTTPX client
159
+
160
+ """
161
+ return self._httpx_auth
162
+
163
+ @property
164
+ def provider_name(self) -> str:
165
+ """Get the name of the authentication provider.
166
+
167
+ Returns:
168
+ str: Name of the authentication provider
169
+
170
+ """
171
+ return 'basic'
@@ -0,0 +1,108 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Bearer token authentication provider."""
15
+
16
+ from awslabs.openapi_mcp_server import logger
17
+ from awslabs.openapi_mcp_server.api.config import Config
18
+ from awslabs.openapi_mcp_server.auth.auth_cache import cached_auth_data
19
+ from awslabs.openapi_mcp_server.auth.auth_errors import MissingCredentialsError
20
+ from awslabs.openapi_mcp_server.auth.base_auth import BaseAuthProvider
21
+ from typing import Dict
22
+
23
+
24
+ class BearerAuthProvider(BaseAuthProvider):
25
+ """Bearer token authentication provider.
26
+
27
+ This provider adds an Authorization header with a Bearer token
28
+ to all HTTP requests.
29
+ """
30
+
31
+ def __init__(self, config: Config):
32
+ """Initialize with configuration.
33
+
34
+ Args:
35
+ config: Application configuration
36
+
37
+ """
38
+ # Store token before calling super().__init__
39
+ self._token = config.auth_token
40
+ self._token_ttl = getattr(config, 'auth_token_ttl', 3600) # Default 1 hour
41
+
42
+ # Call parent initializer which will validate and initialize auth
43
+ super().__init__(config)
44
+
45
+ def _validate_config(self) -> bool:
46
+ """Validate the configuration.
47
+
48
+ Returns:
49
+ bool: True if token is provided, False otherwise
50
+
51
+ Raises:
52
+ MissingCredentialsError: If token is missing
53
+
54
+ """
55
+ if not self._token:
56
+ raise MissingCredentialsError(
57
+ 'Bearer authentication requires a valid token',
58
+ {
59
+ 'help': 'Provide a token using --auth-token command line argument or AUTH_TOKEN environment variable'
60
+ },
61
+ )
62
+ return True
63
+
64
+ def _log_validation_error(self) -> None:
65
+ """Log validation error messages."""
66
+ logger.error(
67
+ 'Bearer authentication requires a valid token. When using bearer authentication, a token must be provided.'
68
+ )
69
+ logger.error(
70
+ 'Please provide a token using --auth-token command line argument or AUTH_TOKEN environment variable.'
71
+ )
72
+
73
+ def _initialize_auth(self) -> None:
74
+ """Initialize authentication data after validation."""
75
+ # We'll use the cached method to generate headers
76
+ self._auth_headers = self._generate_auth_headers(self._token)
77
+
78
+ @cached_auth_data(ttl=3600) # Cache for 1 hour by default
79
+ def _generate_auth_headers(self, token: str) -> Dict[str, str]:
80
+ """Generate authentication headers.
81
+
82
+ This method is cached to avoid regenerating headers for the same token.
83
+
84
+ Args:
85
+ token: Bearer token
86
+
87
+ Returns:
88
+ Dict[str, str]: Authentication headers
89
+
90
+ """
91
+ # Log without including the token
92
+ logger.debug('Generating new bearer token headers')
93
+
94
+ # Calculate token length for debugging purposes
95
+ token_length = len(token) if token else 0
96
+ logger.debug(f'Token length: {token_length} characters')
97
+
98
+ return {'Authorization': f'Bearer {token}'}
99
+
100
+ @property
101
+ def provider_name(self) -> str:
102
+ """Get the name of the authentication provider.
103
+
104
+ Returns:
105
+ str: Name of the authentication provider
106
+
107
+ """
108
+ return 'bearer'