universal-mcp 0.1.23rc2__py3-none-any.whl → 0.1.24rc3__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 (69) hide show
  1. universal_mcp/agentr/__init__.py +6 -0
  2. universal_mcp/agentr/agentr.py +30 -0
  3. universal_mcp/{utils/agentr.py → agentr/client.py} +22 -7
  4. universal_mcp/agentr/integration.py +104 -0
  5. universal_mcp/agentr/registry.py +91 -0
  6. universal_mcp/agentr/server.py +51 -0
  7. universal_mcp/agents/__init__.py +6 -0
  8. universal_mcp/agents/auto.py +576 -0
  9. universal_mcp/agents/base.py +88 -0
  10. universal_mcp/agents/cli.py +27 -0
  11. universal_mcp/agents/codeact/__init__.py +243 -0
  12. universal_mcp/agents/codeact/sandbox.py +27 -0
  13. universal_mcp/agents/codeact/test.py +15 -0
  14. universal_mcp/agents/codeact/utils.py +61 -0
  15. universal_mcp/agents/hil.py +104 -0
  16. universal_mcp/agents/llm.py +10 -0
  17. universal_mcp/agents/react.py +58 -0
  18. universal_mcp/agents/simple.py +40 -0
  19. universal_mcp/agents/utils.py +111 -0
  20. universal_mcp/analytics.py +44 -14
  21. universal_mcp/applications/__init__.py +42 -75
  22. universal_mcp/applications/application.py +187 -133
  23. universal_mcp/applications/sample/app.py +245 -0
  24. universal_mcp/cli.py +14 -231
  25. universal_mcp/client/oauth.py +122 -18
  26. universal_mcp/client/token_store.py +62 -3
  27. universal_mcp/client/{client.py → transport.py} +127 -48
  28. universal_mcp/config.py +189 -49
  29. universal_mcp/exceptions.py +54 -6
  30. universal_mcp/integrations/__init__.py +0 -18
  31. universal_mcp/integrations/integration.py +185 -168
  32. universal_mcp/servers/__init__.py +2 -14
  33. universal_mcp/servers/server.py +84 -258
  34. universal_mcp/stores/store.py +126 -93
  35. universal_mcp/tools/__init__.py +3 -0
  36. universal_mcp/tools/adapters.py +20 -11
  37. universal_mcp/tools/func_metadata.py +1 -1
  38. universal_mcp/tools/manager.py +38 -53
  39. universal_mcp/tools/registry.py +41 -0
  40. universal_mcp/tools/tools.py +24 -3
  41. universal_mcp/types.py +10 -0
  42. universal_mcp/utils/common.py +245 -0
  43. universal_mcp/utils/installation.py +3 -4
  44. universal_mcp/utils/openapi/api_generator.py +71 -17
  45. universal_mcp/utils/openapi/api_splitter.py +0 -1
  46. universal_mcp/utils/openapi/cli.py +669 -0
  47. universal_mcp/utils/openapi/filters.py +114 -0
  48. universal_mcp/utils/openapi/openapi.py +315 -23
  49. universal_mcp/utils/openapi/postprocessor.py +275 -0
  50. universal_mcp/utils/openapi/preprocessor.py +63 -8
  51. universal_mcp/utils/openapi/test_generator.py +287 -0
  52. universal_mcp/utils/prompts.py +634 -0
  53. universal_mcp/utils/singleton.py +4 -1
  54. universal_mcp/utils/testing.py +196 -8
  55. universal_mcp-0.1.24rc3.dist-info/METADATA +68 -0
  56. universal_mcp-0.1.24rc3.dist-info/RECORD +70 -0
  57. universal_mcp/applications/README.md +0 -122
  58. universal_mcp/client/__main__.py +0 -30
  59. universal_mcp/client/agent.py +0 -96
  60. universal_mcp/integrations/README.md +0 -25
  61. universal_mcp/servers/README.md +0 -79
  62. universal_mcp/stores/README.md +0 -74
  63. universal_mcp/tools/README.md +0 -86
  64. universal_mcp-0.1.23rc2.dist-info/METADATA +0 -283
  65. universal_mcp-0.1.23rc2.dist-info/RECORD +0 -51
  66. /universal_mcp/{utils → tools}/docstring_parser.py +0 -0
  67. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/WHEEL +0 -0
  68. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/entry_points.txt +0 -0
  69. {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,6 @@ from loguru import logger
5
5
 
6
6
  from universal_mcp.exceptions import KeyNotFoundError, NotAuthorizedError
7
7
  from universal_mcp.stores import BaseStore, MemoryStore
8
- from universal_mcp.utils.agentr import AgentrClient
9
8
 
10
9
 
11
10
  def sanitize_api_key_name(name: str) -> str:
@@ -19,75 +18,124 @@ def sanitize_api_key_name(name: str) -> str:
19
18
  class Integration:
20
19
  """Abstract base class for handling application integrations and authentication.
21
20
 
22
- This class defines the interface for different types of integrations that handle
23
- authentication and authorization with external services.
21
+ This class defines a common interface for various authentication and
22
+ authorization strategies an application might use to connect with
23
+ external services. Subclasses implement specific mechanisms like
24
+ API key handling, OAuth 2.0 flows, or delegation to platforms like AgentR.
24
25
 
25
- Args:
26
- name: The name identifier for this integration
27
- store: Optional Store instance for persisting credentials and other data
26
+ Each integration is associated with a name and can use a `BaseStore`
27
+ instance for persisting credentials or other relevant data.
28
28
 
29
29
  Attributes:
30
- name: The name identifier for this integration
31
- store: Store instance for persisting credentials and other data
30
+ name (str): The unique name identifying this integration instance
31
+ (e.g., "my_app_api_key", "github_oauth").
32
+ store (BaseStore): The storage backend (e.g., `MemoryStore`,
33
+ `KeyringStore`) used for persisting credentials.
34
+ Defaults to `MemoryStore` if not provided.
32
35
  """
33
36
 
34
37
  def __init__(self, name: str, store: BaseStore | None = None):
38
+ """Initializes the Integration.
39
+
40
+ Args:
41
+ name (str): The unique name/identifier for this integration instance.
42
+ store (BaseStore | None, optional): A store instance for
43
+ persisting credentials. Defaults to `MemoryStore()`.
44
+ """
35
45
  self.name = name
36
46
  self.store = store or MemoryStore()
47
+ self.type = ""
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
 
106
+ def __str__(self) -> str:
107
+ return f"Integration(name={self.name}, type={self.type})"
72
108
 
73
- class ApiKeyIntegration(Integration):
74
- """Integration class for API key based authentication.
109
+ def __repr__(self) -> str:
110
+ return self.__str__()
75
111
 
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.
79
112
 
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
113
+ class ApiKeyIntegration(Integration):
114
+ """Handles integrations that use a simple API key for authentication.
115
+
116
+ This class manages storing and retrieving an API key. The key name is
117
+ automatically sanitized (e.g., uppercased and suffixed with `_API_KEY`)
118
+ before being used with the store.
84
119
 
85
120
  Attributes:
86
- name: The name identifier for this integration
87
- store: Store instance for persisting credentials and other data
121
+ name (str): The sanitized name used as the key for storing the API key.
122
+ store (BaseStore): Store for persisting the API key.
123
+ type (str): Set to "api_key".
124
+ _api_key (str | None): Cached API key.
88
125
  """
89
126
 
90
127
  def __init__(self, name: str, store: BaseStore | None = None, **kwargs):
128
+ """Initializes ApiKeyIntegration.
129
+
130
+ The provided `name` is sanitized (e.g., 'mykey' becomes 'MYKEY_API_KEY')
131
+ to form the actual key used for storage.
132
+
133
+ Args:
134
+ name (str): The base name for the API key (e.g., "TAVILY").
135
+ store (BaseStore | None, optional): Store for credentials.
136
+ Defaults to `MemoryStore()`.
137
+ **kwargs: Additional arguments passed to the parent `Integration`.
138
+ """
91
139
  self.type = "api_key"
92
140
  sanitized_name = sanitize_api_key_name(name)
93
141
  super().__init__(sanitized_name, store, **kwargs)
@@ -95,25 +143,39 @@ class ApiKeyIntegration(Integration):
95
143
  self._api_key: str | None = None
96
144
 
97
145
  @property
98
- def api_key(self) -> str | None:
146
+ def api_key(self) -> str: # Changed to str, as it raises if None effectively
147
+ """Retrieves the API key, loading it from the store if necessary.
148
+
149
+ If the API key is not already cached in `_api_key`, it attempts
150
+ to load it from `self.store` using `self.name` as the key.
151
+
152
+ Returns:
153
+ str: The API key.
154
+
155
+ Raises:
156
+ NotAuthorizedError: If the API key is not found in the store.
157
+ The original `KeyNotFoundError` is chained.
158
+ """
99
159
  if not self._api_key:
100
160
  try:
101
- credentials = self.store.get(self.name)
161
+ credentials = self.store.get(self.name) # type: ignore
102
162
  self._api_key = credentials
103
163
  except KeyNotFoundError as e:
104
164
  action = self.authorize()
105
165
  raise NotAuthorizedError(action) from e
106
- return self._api_key
166
+ return self._api_key # type: ignore
107
167
 
108
168
  @api_key.setter
109
169
  def api_key(self, value: str | None) -> None:
110
- """Set the API key.
170
+ """Sets and stores the API key.
111
171
 
112
172
  Args:
113
- value: The API key value to set.
173
+ value (str | None): The API key value. If None, `_api_key` is set
174
+ to None, but nothing is stored (or cleared from store).
175
+ Consider if None should clear the store.
114
176
 
115
177
  Raises:
116
- ValueError: If the API key is invalid.
178
+ ValueError: If `value` is provided and is not a string.
117
179
  """
118
180
  if value is not None and not isinstance(value, str):
119
181
  raise ValueError("API key must be a string")
@@ -122,63 +184,63 @@ class ApiKeyIntegration(Integration):
122
184
  self.store.set(self.name, value)
123
185
 
124
186
  def get_credentials(self) -> dict[str, str]:
125
- """Get API key credentials.
187
+ """Retrieves the API key and returns it in a standard dictionary format.
126
188
 
127
189
  Returns:
128
- Dict[str, str]: Dictionary containing the API key.
190
+ dict[str, str]: A dictionary like `{"api_key": "your_api_key_value"}`.
129
191
 
130
192
  Raises:
131
- NotAuthorizedError: If API key is not found.
193
+ NotAuthorizedError: If the API key cannot be retrieved.
132
194
  """
133
195
  return {"api_key": self.api_key}
134
196
 
135
197
  def set_credentials(self, credentials: dict[str, Any]) -> None:
136
- """Set API key credentials.
198
+ """Sets the API key from a dictionary.
199
+
200
+ Expects `credentials` to be a dictionary, typically containing
201
+ an 'api_key' field, but it stores the entire dictionary as is
202
+ under `self.name`. For direct API key setting, use the `api_key` property.
137
203
 
138
204
  Args:
139
- credentials: Dictionary containing the API key.
205
+ credentials (dict[str, Any]): A dictionary containing the API key
206
+ or related credential information.
140
207
 
141
208
  Raises:
142
- ValueError: If credentials are invalid or missing API key.
209
+ ValueError: If `credentials` is not a dictionary.
143
210
  """
144
211
  if not credentials or not isinstance(credentials, dict):
145
212
  raise ValueError("Invalid credentials format")
146
213
  self.store.set(self.name, credentials)
147
214
 
148
215
  def authorize(self) -> str:
149
- """Get authorization instructions for API key.
216
+ """Provides instructions for setting the API key.
217
+
218
+ Since API key setup is typically manual, this method returns a
219
+ message guiding the user on how to provide the key.
150
220
 
151
221
  Returns:
152
- str: Instructions for setting up API key.
222
+ str: A message instructing the user to provide the API key
223
+ for `self.name`.
153
224
  """
154
225
  return f"Please ask the user for api key and set the API Key for {self.name} in the store"
155
226
 
156
227
 
157
228
  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
229
+ """Manages OAuth 2.0 authentication and authorization flows.
230
+
231
+ This class implements the necessary steps for an OAuth 2.0 client,
232
+ including generating authorization request parameters, handling the
233
+ redirect callback from the authorization server, exchanging the
234
+ authorization code for access/refresh tokens, and refreshing tokens.
173
235
 
174
236
  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
237
+ name (str): Name of the integration.
238
+ store (BaseStore): Store for OAuth tokens.
239
+ client_id (str | None): The OAuth 2.0 Client ID.
240
+ client_secret (str | None): The OAuth 2.0 Client Secret.
241
+ auth_url (str | None): The authorization server's endpoint URL.
242
+ token_url (str | None): The token server's endpoint URL.
243
+ scope (str | None): The requested OAuth scopes, space-separated.
182
244
  """
183
245
 
184
246
  def __init__(
@@ -192,7 +254,21 @@ class OAuthIntegration(Integration):
192
254
  scope: str | None = None,
193
255
  **kwargs,
194
256
  ):
257
+ """Initializes the OAuthIntegration.
258
+
259
+ Args:
260
+ name (str): The unique name for this integration instance.
261
+ store (BaseStore | None, optional): Store for credentials.
262
+ Defaults to `MemoryStore()`.
263
+ client_id (str | None, optional): The OAuth 2.0 Client ID.
264
+ client_secret (str | None, optional): The OAuth 2.0 Client Secret.
265
+ auth_url (str | None, optional): The authorization server's endpoint URL.
266
+ token_url (str | None, optional): The token server's endpoint URL.
267
+ scope (str | None, optional): The requested OAuth scopes, space-separated.
268
+ **kwargs: Additional arguments passed to the parent `Integration`.
269
+ """
195
270
  super().__init__(name, store, **kwargs)
271
+ self.type = "oauth"
196
272
  self.client_id = client_id
197
273
  self.client_secret = client_secret
198
274
  self.auth_url = auth_url
@@ -200,24 +276,30 @@ class OAuthIntegration(Integration):
200
276
  self.scope = scope
201
277
 
202
278
  def get_credentials(self) -> dict[str, Any] | None:
203
- """Get OAuth credentials.
279
+ """Retrieves stored OAuth tokens for this integration.
204
280
 
205
281
  Returns:
206
- Optional[Dict[str, Any]]: Dictionary containing OAuth tokens if found, None otherwise.
282
+ dict[str, Any] | None: A dictionary containing the OAuth tokens
283
+ (e.g., `access_token`, `refresh_token`) if found,
284
+ otherwise None.
207
285
  """
208
286
  credentials = self.store.get(self.name)
209
287
  if not credentials:
210
288
  return None
211
- return credentials
289
+ return credentials # type: ignore
212
290
 
213
291
  def set_credentials(self, credentials: dict[str, Any]) -> None:
214
- """Set OAuth credentials.
292
+ """Stores OAuth tokens for this integration.
293
+
294
+ Validates that essential fields like 'access_token' are present.
215
295
 
216
296
  Args:
217
- credentials: Dictionary containing OAuth tokens.
297
+ credentials (dict[str, Any]): A dictionary containing OAuth tokens.
298
+ Must include at least 'access_token'.
218
299
 
219
300
  Raises:
220
- ValueError: If credentials are invalid or missing required tokens.
301
+ ValueError: If `credentials` is not a dictionary or if 'access_token'
302
+ is missing.
221
303
  """
222
304
  if not credentials or not isinstance(credentials, dict):
223
305
  raise ValueError("Invalid credentials format")
@@ -226,13 +308,19 @@ class OAuthIntegration(Integration):
226
308
  self.store.set(self.name, credentials)
227
309
 
228
310
  def authorize(self) -> dict[str, Any]:
229
- """Get OAuth authorization parameters.
311
+ """Constructs parameters required for the OAuth authorization request.
312
+
313
+ These parameters are typically used to build the URL to which the
314
+ user must be redirected to grant authorization.
230
315
 
231
316
  Returns:
232
- Dict[str, Any]: Dictionary containing OAuth authorization parameters.
317
+ dict[str, Any]: A dictionary containing the authorization endpoint URL
318
+ (`url`), query parameters (`params`), client secret,
319
+ and token URL.
233
320
 
234
321
  Raises:
235
- ValueError: If required OAuth configuration is missing.
322
+ ValueError: If essential OAuth configuration like `client_id`,
323
+ `client_secret`, `auth_url`, or `token_url` is missing.
236
324
  """
237
325
  if not all([self.client_id, self.client_secret, self.auth_url, self.token_url]):
238
326
  raise ValueError("Missing required OAuth configuration")
@@ -251,19 +339,24 @@ class OAuthIntegration(Integration):
251
339
  }
252
340
 
253
341
  def handle_callback(self, code: str) -> dict[str, Any]:
254
- """Handle OAuth callback and exchange code for tokens.
342
+ """Handles the OAuth callback by exchanging the authorization code for tokens.
343
+
344
+ This method is called after the user authorizes the application and the
345
+ authorization server redirects back with an authorization code.
255
346
 
256
347
  Args:
257
- code: Authorization code from OAuth callback.
348
+ code (str): The authorization code received from the OAuth server.
258
349
 
259
350
  Returns:
260
- Dict[str, Any]: Dictionary containing OAuth tokens.
351
+ dict[str, Any]: A dictionary containing the access token, refresh token
352
+ (if any), and other token response data. These are also
353
+ stored via `set_credentials`.
261
354
 
262
355
  Raises:
263
- ValueError: If required OAuth configuration is missing.
264
- httpx.HTTPError: If token exchange request fails.
356
+ ValueError: If essential OAuth configuration is missing.
357
+ httpx.HTTPStatusError: If the token exchange request to `token_url` fails.
265
358
  """
266
- if not all([self.client_id, self.client_secret, self.token_url]):
359
+ if not all([self.client_id, self.client_secret, self.token_url]): # type: ignore
267
360
  raise ValueError("Missing required OAuth configuration")
268
361
 
269
362
  token_params = {
@@ -273,24 +366,26 @@ class OAuthIntegration(Integration):
273
366
  "grant_type": "authorization_code",
274
367
  }
275
368
 
276
- response = httpx.post(self.token_url, data=token_params)
369
+ response = httpx.post(self.token_url, data=token_params) # type: ignore
277
370
  response.raise_for_status()
278
371
  credentials = response.json()
279
372
  self.store.set(self.name, credentials)
280
373
  return credentials
281
374
 
282
375
  def refresh_token(self) -> dict[str, Any]:
283
- """Refresh OAuth access token using refresh token.
376
+ """Refreshes an expired access token using a stored refresh token.
284
377
 
285
378
  Returns:
286
- Dict[str, Any]: Dictionary containing new OAuth tokens.
379
+ dict[str, Any]: A dictionary containing the new access token,
380
+ refresh token, and other token response data.
381
+ These are also stored.
287
382
 
288
383
  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.
384
+ ValueError: If essential OAuth configuration is missing.
385
+ KeyError: If a refresh token is not found in the stored credentials.
386
+ httpx.HTTPStatusError: If the token refresh request fails.
292
387
  """
293
- if not all([self.client_id, self.client_secret, self.token_url]):
388
+ if not all([self.client_id, self.client_secret, self.token_url]): # type: ignore
294
389
  raise ValueError("Missing required OAuth configuration")
295
390
 
296
391
  credentials = self.get_credentials()
@@ -304,86 +399,8 @@ class OAuthIntegration(Integration):
304
399
  "refresh_token": credentials["refresh_token"],
305
400
  }
306
401
 
307
- response = httpx.post(self.token_url, data=token_params)
402
+ response = httpx.post(self.token_url, data=token_params) # type: ignore
308
403
  response.raise_for_status()
309
404
  credentials = response.json()
310
405
  self.store.set(self.name, credentials)
311
406
  return credentials
312
-
313
-
314
- 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.
318
-
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
323
-
324
- Raises:
325
- ValueError: If no API key is provided or found in environment variables
326
- """
327
-
328
- def __init__(self, name: str, api_key: str | None = None, base_url: str | None = None, **kwargs):
329
- super().__init__(name, **kwargs)
330
- self.client = AgentrClient(api_key=api_key, base_url=base_url)
331
- self._credentials = None
332
-
333
- def set_credentials(self, credentials: dict | None = None):
334
- """Set credentials for the integration.
335
-
336
- This method is not implemented for AgentR integration. Instead it redirects to the authorize flow.
337
-
338
- Args:
339
- credentials (dict | None, optional): Credentials dict (not used). Defaults to None.
340
-
341
- Returns:
342
- str: Authorization URL from authorize() method
343
- """
344
- return self.authorize()
345
-
346
- @property
347
- def credentials(self):
348
- """Get credentials for the integration from the AgentR API.
349
-
350
- Makes API request to retrieve stored credentials for this integration.
351
-
352
- Returns:
353
- dict: Credentials data from API response
354
-
355
- Raises:
356
- NotAuthorizedError: If credentials are not found (404 response)
357
- HTTPError: For other API errors
358
- """
359
- if self._credentials is not None:
360
- return self._credentials
361
- self._credentials = self.client.get_credentials(self.name)
362
- return self._credentials
363
-
364
- 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.
368
-
369
- Returns:
370
- dict: Credentials data from API response
371
-
372
- Raises:
373
- NotAuthorizedError: If credentials are not found (404 response)
374
- HTTPError: For other API errors
375
- """
376
- return self.credentials
377
-
378
- def authorize(self):
379
- """Get authorization URL for the integration.
380
-
381
- Makes API request to get OAuth authorization URL.
382
-
383
- Returns:
384
- str: Message containing authorization URL
385
-
386
- Raises:
387
- HTTPError: If API request fails
388
- """
389
- return self.client.get_authorization_url(self.name)
@@ -1,15 +1,3 @@
1
- from universal_mcp.config import ServerConfig
2
- from universal_mcp.servers.server import AgentRServer, BaseServer, LocalServer, SingleMCPServer
1
+ from universal_mcp.servers.server import BaseServer, LocalServer, SingleMCPServer
3
2
 
4
-
5
- def server_from_config(config: ServerConfig):
6
- if config.type == "agentr":
7
- return AgentRServer(config=config, api_key=config.api_key)
8
-
9
- elif config.type == "local":
10
- return LocalServer(config=config)
11
- else:
12
- raise ValueError(f"Unsupported server type: {config.type}")
13
-
14
-
15
- __all__ = [AgentRServer, LocalServer, SingleMCPServer, BaseServer, server_from_config]
3
+ __all__ = ["LocalServer", "SingleMCPServer", "BaseServer"]