universal-mcp 0.1.23rc2__py3-none-any.whl → 0.1.24rc2__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 (48) hide show
  1. universal_mcp/analytics.py +43 -11
  2. universal_mcp/applications/application.py +186 -132
  3. universal_mcp/applications/sample_tool_app.py +80 -0
  4. universal_mcp/cli.py +5 -229
  5. universal_mcp/client/agents/__init__.py +4 -0
  6. universal_mcp/client/agents/base.py +38 -0
  7. universal_mcp/client/agents/llm.py +115 -0
  8. universal_mcp/client/agents/react.py +67 -0
  9. universal_mcp/client/cli.py +181 -0
  10. universal_mcp/client/oauth.py +122 -18
  11. universal_mcp/client/token_store.py +62 -3
  12. universal_mcp/client/{client.py → transport.py} +127 -48
  13. universal_mcp/config.py +160 -46
  14. universal_mcp/exceptions.py +50 -6
  15. universal_mcp/integrations/__init__.py +1 -4
  16. universal_mcp/integrations/integration.py +220 -121
  17. universal_mcp/servers/__init__.py +1 -1
  18. universal_mcp/servers/server.py +114 -247
  19. universal_mcp/stores/store.py +126 -93
  20. universal_mcp/tools/func_metadata.py +1 -1
  21. universal_mcp/tools/manager.py +15 -3
  22. universal_mcp/tools/tools.py +2 -2
  23. universal_mcp/utils/agentr.py +3 -4
  24. universal_mcp/utils/installation.py +3 -4
  25. universal_mcp/utils/openapi/api_generator.py +28 -2
  26. universal_mcp/utils/openapi/api_splitter.py +0 -1
  27. universal_mcp/utils/openapi/cli.py +243 -0
  28. universal_mcp/utils/openapi/filters.py +114 -0
  29. universal_mcp/utils/openapi/openapi.py +31 -2
  30. universal_mcp/utils/openapi/preprocessor.py +62 -7
  31. universal_mcp/utils/prompts.py +787 -0
  32. universal_mcp/utils/singleton.py +4 -1
  33. universal_mcp/utils/testing.py +6 -6
  34. universal_mcp-0.1.24rc2.dist-info/METADATA +54 -0
  35. universal_mcp-0.1.24rc2.dist-info/RECORD +53 -0
  36. universal_mcp/applications/README.md +0 -122
  37. universal_mcp/client/__main__.py +0 -30
  38. universal_mcp/client/agent.py +0 -96
  39. universal_mcp/integrations/README.md +0 -25
  40. universal_mcp/servers/README.md +0 -79
  41. universal_mcp/stores/README.md +0 -74
  42. universal_mcp/tools/README.md +0 -86
  43. universal_mcp-0.1.23rc2.dist-info/METADATA +0 -283
  44. universal_mcp-0.1.23rc2.dist-info/RECORD +0 -51
  45. /universal_mcp/{utils → tools}/docstring_parser.py +0 -0
  46. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc2.dist-info}/WHEEL +0 -0
  47. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc2.dist-info}/entry_points.txt +0 -0
  48. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc2.dist-info}/licenses/LICENSE +0 -0
@@ -12,10 +12,7 @@ def integration_from_config(config: IntegrationConfig, store: BaseStore | None =
12
12
  if config.type == "api_key":
13
13
  return ApiKeyIntegration(config.name, store=store, **kwargs)
14
14
  elif config.type == "agentr":
15
- api_key = kwargs.get("api_key")
16
- if not api_key:
17
- raise ValueError("api_key is required for AgentR integration")
18
- return AgentRIntegration(config.name, api_key=api_key)
15
+ return AgentRIntegration(config.name, **kwargs)
19
16
  else:
20
17
  raise ValueError(f"Unsupported integration type: {config.type}")
21
18
 
@@ -19,75 +19,117 @@ def sanitize_api_key_name(name: str) -> str:
19
19
  class Integration:
20
20
  """Abstract base class for handling application integrations and authentication.
21
21
 
22
- This class defines the interface for different types of integrations that handle
23
- authentication and authorization with external services.
22
+ This class defines a common interface for various authentication and
23
+ authorization strategies an application might use to connect with
24
+ external services. Subclasses implement specific mechanisms like
25
+ API key handling, OAuth 2.0 flows, or delegation to platforms like AgentR.
24
26
 
25
- Args:
26
- name: The name identifier for this integration
27
- store: Optional Store instance for persisting credentials and other data
27
+ Each integration is associated with a name and can use a `BaseStore`
28
+ instance for persisting credentials or other relevant data.
28
29
 
29
30
  Attributes:
30
- name: The name identifier for this integration
31
- store: Store instance for persisting credentials and other data
31
+ name (str): The unique name identifying this integration instance
32
+ (e.g., "my_app_api_key", "github_oauth").
33
+ store (BaseStore): The storage backend (e.g., `MemoryStore`,
34
+ `KeyringStore`) used for persisting credentials.
35
+ Defaults to `MemoryStore` if not provided.
32
36
  """
33
37
 
34
38
  def __init__(self, name: str, store: BaseStore | None = None):
39
+ """Initializes the Integration.
40
+
41
+ Args:
42
+ name (str): The unique name for this integration instance.
43
+ store (BaseStore | None, optional): A store instance for
44
+ persisting credentials. Defaults to `MemoryStore()`.
45
+ """
35
46
  self.name = name
36
47
  self.store = store or MemoryStore()
37
48
 
38
49
  def authorize(self) -> str | dict[str, Any]:
39
- """Authorize the integration.
50
+ """Initiates or provides details for the authorization process.
51
+
52
+ The exact behavior and return type of this method depend on the
53
+ specific integration subclass. It might return an authorization URL
54
+ for the user to visit, parameters needed to construct such a URL,
55
+ or instructions on how to manually provide credentials.
40
56
 
41
57
  Returns:
42
- Union[str, Dict[str, Any]]: Authorization URL or parameters needed for authorization.
58
+ str | dict[str, Any]: Typically, an authorization URL (str) or a
59
+ dictionary containing parameters needed for
60
+ the authorization flow.
43
61
 
44
62
  Raises:
45
- ValueError: If required configuration is missing.
63
+ ValueError: If essential configuration for authorization is missing.
64
+ NotImplementedError: If the subclass does not implement this method.
46
65
  """
47
- pass
66
+ raise NotImplementedError("Subclasses must implement the authorize method.")
48
67
 
49
68
  def get_credentials(self) -> dict[str, Any]:
50
- """Get credentials for the integration.
69
+ """Retrieves the stored credentials for this integration.
70
+
71
+ Fetches credentials associated with `self.name` from the `self.store`.
51
72
 
52
73
  Returns:
53
- Dict[str, Any]: Credentials for the integration.
74
+ dict[str, Any]: A dictionary containing the credentials. The structure
75
+ of this dictionary is specific to the integration type.
54
76
 
55
77
  Raises:
56
- NotAuthorizedError: If credentials are not found or invalid.
78
+ NotAuthorizedError: If credentials are not found in the store
79
+ or are otherwise invalid/inaccessible.
80
+ KeyNotFoundError: If the key (self.name) is not found in the store.
57
81
  """
58
- credentials = self.store.get(self.name)
59
- return credentials
82
+ try:
83
+ credentials = self.store.get(self.name)
84
+ if credentials is None: # Explicitly check for None if store can return it
85
+ raise NotAuthorizedError(f"No credentials found for {self.name}")
86
+ return credentials
87
+ except KeyNotFoundError as e:
88
+ raise NotAuthorizedError(f"Credentials not found for {self.name}: {e}") from e
60
89
 
61
90
  def set_credentials(self, credentials: dict[str, Any]) -> None:
62
- """Set credentials for the integration.
91
+ """Stores the provided credentials for this integration.
92
+
93
+ Saves the given credentials dictionary into `self.store` associated
94
+ with `self.name`.
63
95
 
64
96
  Args:
65
- credentials: Dictionary containing credentials for the integration.
97
+ credentials (dict[str, Any]): A dictionary containing the credentials
98
+ to be stored. The required structure depends on the integration.
66
99
 
67
100
  Raises:
68
- ValueError: If credentials are invalid or missing required fields.
101
+ ValueError: If the provided credentials are invalid or missing
102
+ required fields for the specific integration type.
69
103
  """
70
104
  self.store.set(self.name, credentials)
71
105
 
72
106
 
73
107
  class ApiKeyIntegration(Integration):
74
- """Integration class for API key based authentication.
75
-
76
- This class implements the Integration interface for services that use API key
77
- authentication. It handles storing and retrieving API keys using the provided
78
- store.
108
+ """Handles integrations that use a simple API key for authentication.
79
109
 
80
- Args:
81
- name: The name identifier for this integration
82
- store: Optional Store instance for persisting credentials and other data
83
- **kwargs: Additional keyword arguments passed to parent class
110
+ This class manages storing and retrieving an API key. The key name is
111
+ automatically sanitized (e.g., uppercased and suffixed with `_API_KEY`)
112
+ before being used with the store.
84
113
 
85
114
  Attributes:
86
- name: The name identifier for this integration
87
- store: Store instance for persisting credentials and other data
115
+ name (str): The sanitized name used as the key for storing the API key.
116
+ store (BaseStore): Store for persisting the API key.
117
+ type (str): Set to "api_key".
118
+ _api_key (str | None): Cached API key.
88
119
  """
89
120
 
90
121
  def __init__(self, name: str, store: BaseStore | None = None, **kwargs):
122
+ """Initializes ApiKeyIntegration.
123
+
124
+ The provided `name` is sanitized (e.g., 'mykey' becomes 'MYKEY_API_KEY')
125
+ to form the actual key used for storage.
126
+
127
+ Args:
128
+ name (str): The base name for the API key (e.g., "TAVILY").
129
+ store (BaseStore | None, optional): Store for credentials.
130
+ Defaults to `MemoryStore()`.
131
+ **kwargs: Additional arguments passed to the parent `Integration`.
132
+ """
91
133
  self.type = "api_key"
92
134
  sanitized_name = sanitize_api_key_name(name)
93
135
  super().__init__(sanitized_name, store, **kwargs)
@@ -95,25 +137,39 @@ class ApiKeyIntegration(Integration):
95
137
  self._api_key: str | None = None
96
138
 
97
139
  @property
98
- def api_key(self) -> str | None:
140
+ def api_key(self) -> str: # Changed to str, as it raises if None effectively
141
+ """Retrieves the API key, loading it from the store if necessary.
142
+
143
+ If the API key is not already cached in `_api_key`, it attempts
144
+ to load it from `self.store` using `self.name` as the key.
145
+
146
+ Returns:
147
+ str: The API key.
148
+
149
+ Raises:
150
+ NotAuthorizedError: If the API key is not found in the store.
151
+ The original `KeyNotFoundError` is chained.
152
+ """
99
153
  if not self._api_key:
100
154
  try:
101
- credentials = self.store.get(self.name)
155
+ credentials = self.store.get(self.name) # type: ignore
102
156
  self._api_key = credentials
103
157
  except KeyNotFoundError as e:
104
158
  action = self.authorize()
105
159
  raise NotAuthorizedError(action) from e
106
- return self._api_key
160
+ return self._api_key # type: ignore
107
161
 
108
162
  @api_key.setter
109
163
  def api_key(self, value: str | None) -> None:
110
- """Set the API key.
164
+ """Sets and stores the API key.
111
165
 
112
166
  Args:
113
- value: The API key value to set.
167
+ value (str | None): The API key value. If None, `_api_key` is set
168
+ to None, but nothing is stored (or cleared from store).
169
+ Consider if None should clear the store.
114
170
 
115
171
  Raises:
116
- ValueError: If the API key is invalid.
172
+ ValueError: If `value` is provided and is not a string.
117
173
  """
118
174
  if value is not None and not isinstance(value, str):
119
175
  raise ValueError("API key must be a string")
@@ -122,63 +178,63 @@ class ApiKeyIntegration(Integration):
122
178
  self.store.set(self.name, value)
123
179
 
124
180
  def get_credentials(self) -> dict[str, str]:
125
- """Get API key credentials.
181
+ """Retrieves the API key and returns it in a standard dictionary format.
126
182
 
127
183
  Returns:
128
- Dict[str, str]: Dictionary containing the API key.
184
+ dict[str, str]: A dictionary like `{"api_key": "your_api_key_value"}`.
129
185
 
130
186
  Raises:
131
- NotAuthorizedError: If API key is not found.
187
+ NotAuthorizedError: If the API key cannot be retrieved.
132
188
  """
133
189
  return {"api_key": self.api_key}
134
190
 
135
191
  def set_credentials(self, credentials: dict[str, Any]) -> None:
136
- """Set API key credentials.
192
+ """Sets the API key from a dictionary.
193
+
194
+ Expects `credentials` to be a dictionary, typically containing
195
+ an 'api_key' field, but it stores the entire dictionary as is
196
+ under `self.name`. For direct API key setting, use the `api_key` property.
137
197
 
138
198
  Args:
139
- credentials: Dictionary containing the API key.
199
+ credentials (dict[str, Any]): A dictionary containing the API key
200
+ or related credential information.
140
201
 
141
202
  Raises:
142
- ValueError: If credentials are invalid or missing API key.
203
+ ValueError: If `credentials` is not a dictionary.
143
204
  """
144
205
  if not credentials or not isinstance(credentials, dict):
145
206
  raise ValueError("Invalid credentials format")
146
207
  self.store.set(self.name, credentials)
147
208
 
148
209
  def authorize(self) -> str:
149
- """Get authorization instructions for API key.
210
+ """Provides instructions for setting the API key.
211
+
212
+ Since API key setup is typically manual, this method returns a
213
+ message guiding the user on how to provide the key.
150
214
 
151
215
  Returns:
152
- str: Instructions for setting up API key.
216
+ str: A message instructing the user to provide the API key
217
+ for `self.name`.
153
218
  """
154
219
  return f"Please ask the user for api key and set the API Key for {self.name} in the store"
155
220
 
156
221
 
157
222
  class OAuthIntegration(Integration):
158
- """Integration class for OAuth based authentication.
159
-
160
- This class implements the Integration interface for services that use OAuth
161
- authentication. It handles the OAuth flow including authorization, token exchange,
162
- and token refresh.
163
-
164
- Args:
165
- name: The name identifier for this integration
166
- store: Optional Store instance for persisting credentials and other data
167
- client_id: OAuth client ID
168
- client_secret: OAuth client secret
169
- auth_url: OAuth authorization URL
170
- token_url: OAuth token exchange URL
171
- scope: OAuth scope string
172
- **kwargs: Additional keyword arguments passed to parent class
223
+ """Manages OAuth 2.0 authentication and authorization flows.
224
+
225
+ This class implements the necessary steps for an OAuth 2.0 client,
226
+ including generating authorization request parameters, handling the
227
+ redirect callback from the authorization server, exchanging the
228
+ authorization code for access/refresh tokens, and refreshing tokens.
173
229
 
174
230
  Attributes:
175
- name: The name identifier for this integration
176
- store: Store instance for persisting credentials and other data
177
- client_id: OAuth client ID
178
- client_secret: OAuth client secret
179
- auth_url: OAuth authorization URL
180
- token_url: OAuth token exchange URL
181
- scope: OAuth scope string
231
+ name (str): Name of the integration.
232
+ store (BaseStore): Store for OAuth tokens.
233
+ client_id (str | None): The OAuth 2.0 Client ID.
234
+ client_secret (str | None): The OAuth 2.0 Client Secret.
235
+ auth_url (str | None): The authorization server's endpoint URL.
236
+ token_url (str | None): The token server's endpoint URL.
237
+ scope (str | None): The requested OAuth scopes, space-separated.
182
238
  """
183
239
 
184
240
  def __init__(
@@ -192,6 +248,19 @@ class OAuthIntegration(Integration):
192
248
  scope: str | None = None,
193
249
  **kwargs,
194
250
  ):
251
+ """Initializes the OAuthIntegration.
252
+
253
+ Args:
254
+ name (str): The unique name for this integration instance.
255
+ store (BaseStore | None, optional): Store for credentials.
256
+ Defaults to `MemoryStore()`.
257
+ client_id (str | None, optional): The OAuth 2.0 Client ID.
258
+ client_secret (str | None, optional): The OAuth 2.0 Client Secret.
259
+ auth_url (str | None, optional): The authorization server's endpoint URL.
260
+ token_url (str | None, optional): The token server's endpoint URL.
261
+ scope (str | None, optional): The requested OAuth scopes, space-separated.
262
+ **kwargs: Additional arguments passed to the parent `Integration`.
263
+ """
195
264
  super().__init__(name, store, **kwargs)
196
265
  self.client_id = client_id
197
266
  self.client_secret = client_secret
@@ -200,24 +269,30 @@ class OAuthIntegration(Integration):
200
269
  self.scope = scope
201
270
 
202
271
  def get_credentials(self) -> dict[str, Any] | None:
203
- """Get OAuth credentials.
272
+ """Retrieves stored OAuth tokens for this integration.
204
273
 
205
274
  Returns:
206
- Optional[Dict[str, Any]]: Dictionary containing OAuth tokens if found, None otherwise.
275
+ dict[str, Any] | None: A dictionary containing the OAuth tokens
276
+ (e.g., `access_token`, `refresh_token`) if found,
277
+ otherwise None.
207
278
  """
208
279
  credentials = self.store.get(self.name)
209
280
  if not credentials:
210
281
  return None
211
- return credentials
282
+ return credentials # type: ignore
212
283
 
213
284
  def set_credentials(self, credentials: dict[str, Any]) -> None:
214
- """Set OAuth credentials.
285
+ """Stores OAuth tokens for this integration.
286
+
287
+ Validates that essential fields like 'access_token' are present.
215
288
 
216
289
  Args:
217
- credentials: Dictionary containing OAuth tokens.
290
+ credentials (dict[str, Any]): A dictionary containing OAuth tokens.
291
+ Must include at least 'access_token'.
218
292
 
219
293
  Raises:
220
- ValueError: If credentials are invalid or missing required tokens.
294
+ ValueError: If `credentials` is not a dictionary or if 'access_token'
295
+ is missing.
221
296
  """
222
297
  if not credentials or not isinstance(credentials, dict):
223
298
  raise ValueError("Invalid credentials format")
@@ -226,13 +301,19 @@ class OAuthIntegration(Integration):
226
301
  self.store.set(self.name, credentials)
227
302
 
228
303
  def authorize(self) -> dict[str, Any]:
229
- """Get OAuth authorization parameters.
304
+ """Constructs parameters required for the OAuth authorization request.
305
+
306
+ These parameters are typically used to build the URL to which the
307
+ user must be redirected to grant authorization.
230
308
 
231
309
  Returns:
232
- Dict[str, Any]: Dictionary containing OAuth authorization parameters.
310
+ dict[str, Any]: A dictionary containing the authorization endpoint URL
311
+ (`url`), query parameters (`params`), client secret,
312
+ and token URL.
233
313
 
234
314
  Raises:
235
- ValueError: If required OAuth configuration is missing.
315
+ ValueError: If essential OAuth configuration like `client_id`,
316
+ `client_secret`, `auth_url`, or `token_url` is missing.
236
317
  """
237
318
  if not all([self.client_id, self.client_secret, self.auth_url, self.token_url]):
238
319
  raise ValueError("Missing required OAuth configuration")
@@ -251,19 +332,24 @@ class OAuthIntegration(Integration):
251
332
  }
252
333
 
253
334
  def handle_callback(self, code: str) -> dict[str, Any]:
254
- """Handle OAuth callback and exchange code for tokens.
335
+ """Handles the OAuth callback by exchanging the authorization code for tokens.
336
+
337
+ This method is called after the user authorizes the application and the
338
+ authorization server redirects back with an authorization code.
255
339
 
256
340
  Args:
257
- code: Authorization code from OAuth callback.
341
+ code (str): The authorization code received from the OAuth server.
258
342
 
259
343
  Returns:
260
- Dict[str, Any]: Dictionary containing OAuth tokens.
344
+ dict[str, Any]: A dictionary containing the access token, refresh token
345
+ (if any), and other token response data. These are also
346
+ stored via `set_credentials`.
261
347
 
262
348
  Raises:
263
- ValueError: If required OAuth configuration is missing.
264
- httpx.HTTPError: If token exchange request fails.
349
+ ValueError: If essential OAuth configuration is missing.
350
+ httpx.HTTPStatusError: If the token exchange request to `token_url` fails.
265
351
  """
266
- if not all([self.client_id, self.client_secret, self.token_url]):
352
+ if not all([self.client_id, self.client_secret, self.token_url]): # type: ignore
267
353
  raise ValueError("Missing required OAuth configuration")
268
354
 
269
355
  token_params = {
@@ -273,24 +359,26 @@ class OAuthIntegration(Integration):
273
359
  "grant_type": "authorization_code",
274
360
  }
275
361
 
276
- response = httpx.post(self.token_url, data=token_params)
362
+ response = httpx.post(self.token_url, data=token_params) # type: ignore
277
363
  response.raise_for_status()
278
364
  credentials = response.json()
279
365
  self.store.set(self.name, credentials)
280
366
  return credentials
281
367
 
282
368
  def refresh_token(self) -> dict[str, Any]:
283
- """Refresh OAuth access token using refresh token.
369
+ """Refreshes an expired access token using a stored refresh token.
284
370
 
285
371
  Returns:
286
- Dict[str, Any]: Dictionary containing new OAuth tokens.
372
+ dict[str, Any]: A dictionary containing the new access token,
373
+ refresh token, and other token response data.
374
+ These are also stored.
287
375
 
288
376
  Raises:
289
- ValueError: If required OAuth configuration is missing.
290
- httpx.HTTPError: If token refresh request fails.
291
- KeyError: If refresh token is not found in current credentials.
377
+ ValueError: If essential OAuth configuration is missing.
378
+ KeyError: If a refresh token is not found in the stored credentials.
379
+ httpx.HTTPStatusError: If the token refresh request fails.
292
380
  """
293
- if not all([self.client_id, self.client_secret, self.token_url]):
381
+ if not all([self.client_id, self.client_secret, self.token_url]): # type: ignore
294
382
  raise ValueError("Missing required OAuth configuration")
295
383
 
296
384
  credentials = self.get_credentials()
@@ -304,7 +392,7 @@ class OAuthIntegration(Integration):
304
392
  "refresh_token": credentials["refresh_token"],
305
393
  }
306
394
 
307
- response = httpx.post(self.token_url, data=token_params)
395
+ response = httpx.post(self.token_url, data=token_params) # type: ignore
308
396
  response.raise_for_status()
309
397
  credentials = response.json()
310
398
  self.store.set(self.name, credentials)
@@ -312,49 +400,61 @@ class OAuthIntegration(Integration):
312
400
 
313
401
 
314
402
  class AgentRIntegration(Integration):
315
- """Integration class for AgentR API authentication and authorization.
316
-
317
- This class handles API key authentication and OAuth authorization flow for AgentR services.
403
+ """Manages authentication and authorization via the AgentR platform.
318
404
 
319
- Args:
320
- name (str): Name of the integration
321
- api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var
322
- **kwargs: Additional keyword arguments passed to parent Integration class
405
+ This integration uses an `AgentrClient` to interact with the AgentR API
406
+ for operations like retrieving authorization URLs and fetching stored
407
+ credentials. It simplifies integration with services supported by AgentR.
323
408
 
324
- Raises:
325
- ValueError: If no API key is provided or found in environment variables
409
+ Attributes:
410
+ name (str): Name of the integration (e.g., "github", "google").
411
+ store (BaseStore): Store, typically not used directly by this class
412
+ as AgentR manages the primary credential storage.
413
+ client (AgentrClient): Client for communicating with the AgentR API.
414
+ _credentials (dict | None): Cached credentials.
326
415
  """
327
416
 
328
- def __init__(self, name: str, api_key: str | None = None, base_url: str | None = None, **kwargs):
417
+ def __init__(self, name: str, client: AgentrClient | None = None, **kwargs):
418
+ """Initializes the AgentRIntegration.
419
+
420
+ Args:
421
+ name (str): The name of the service integration as configured on
422
+ the AgentR platform (e.g., "github").
423
+ client (AgentrClient | None, optional): The AgentR client. If not provided,
424
+ a new `AgentrClient` will be created.
425
+ **kwargs: Additional arguments passed to the parent `Integration`.
426
+ """
329
427
  super().__init__(name, **kwargs)
330
- self.client = AgentrClient(api_key=api_key, base_url=base_url)
428
+ self.client = client or AgentrClient()
331
429
  self._credentials = None
332
430
 
333
- def set_credentials(self, credentials: dict | None = None):
334
- """Set credentials for the integration.
431
+ def set_credentials(self, credentials: dict[str, Any] | None = None) -> str:
432
+ """Not used for direct credential setting; initiates authorization instead.
335
433
 
336
- This method is not implemented for AgentR integration. Instead it redirects to the authorize flow.
434
+ For AgentR integrations, credentials are set via the AgentR platform's
435
+ OAuth flow. This method effectively redirects to the `authorize` flow.
337
436
 
338
437
  Args:
339
- credentials (dict | None, optional): Credentials dict (not used). Defaults to None.
438
+ credentials (dict | None, optional): Not used by this implementation.
340
439
 
341
440
  Returns:
342
- str: Authorization URL from authorize() method
441
+ str: The authorization URL or message from the `authorize()` method.
343
442
  """
344
443
  return self.authorize()
345
444
 
346
445
  @property
347
446
  def credentials(self):
348
- """Get credentials for the integration from the AgentR API.
447
+ """Retrieves credentials from the AgentR API, with caching.
349
448
 
350
- Makes API request to retrieve stored credentials for this integration.
449
+ If credentials are not cached locally (in `_credentials`), this property
450
+ fetches them from the AgentR platform using `self.client.get_credentials`.
351
451
 
352
452
  Returns:
353
- dict: Credentials data from API response
453
+ dict: The credentials dictionary obtained from AgentR.
354
454
 
355
455
  Raises:
356
- NotAuthorizedError: If credentials are not found (404 response)
357
- HTTPError: For other API errors
456
+ NotAuthorizedError: If credentials are not found (e.g., 404 from AgentR).
457
+ httpx.HTTPStatusError: For other API errors from AgentR.
358
458
  """
359
459
  if self._credentials is not None:
360
460
  return self._credentials
@@ -362,28 +462,27 @@ class AgentRIntegration(Integration):
362
462
  return self._credentials
363
463
 
364
464
  def get_credentials(self):
365
- """Get credentials for the integration from the AgentR API.
366
-
367
- Makes API request to retrieve stored credentials for this integration.
465
+ """Retrieves credentials from the AgentR API. Alias for `credentials` property.
368
466
 
369
467
  Returns:
370
- dict: Credentials data from API response
468
+ dict: The credentials dictionary obtained from AgentR.
371
469
 
372
470
  Raises:
373
- NotAuthorizedError: If credentials are not found (404 response)
374
- HTTPError: For other API errors
471
+ NotAuthorizedError: If credentials are not found.
472
+ httpx.HTTPStatusError: For other API errors.
375
473
  """
376
474
  return self.credentials
377
475
 
378
- def authorize(self):
379
- """Get authorization URL for the integration.
476
+ def authorize(self) -> str:
477
+ """Retrieves the authorization URL from the AgentR platform.
380
478
 
381
- Makes API request to get OAuth authorization URL.
479
+ This URL should be presented to the user to initiate the OAuth flow
480
+ managed by AgentR for the service associated with `self.name`.
382
481
 
383
482
  Returns:
384
- str: Message containing authorization URL
483
+ str: The authorization URL.
385
484
 
386
485
  Raises:
387
- HTTPError: If API request fails
486
+ httpx.HTTPStatusError: If the API request to AgentR fails.
388
487
  """
389
488
  return self.client.get_authorization_url(self.name)
@@ -12,4 +12,4 @@ def server_from_config(config: ServerConfig):
12
12
  raise ValueError(f"Unsupported server type: {config.type}")
13
13
 
14
14
 
15
- __all__ = [AgentRServer, LocalServer, SingleMCPServer, BaseServer, server_from_config]
15
+ __all__ = ["AgentRServer", "LocalServer", "SingleMCPServer", "BaseServer", "server_from_config"]