agentr 0.1.6__py3-none-any.whl → 0.1.8__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.
agentr/cli.py CHANGED
@@ -1,75 +1,76 @@
1
- import typer
2
- from pathlib import Path
3
- import sys
4
-
5
- app = typer.Typer()
6
-
7
- @app.command()
8
- def version():
9
- """Show the version of the CLI"""
10
- print("agentr version 0.1.0")
11
-
12
- @app.command()
13
- def generate(schema_path: Path = typer.Option(..., "--schema", "-s")):
14
- """Generate API client from OpenAPI schema"""
15
- if not schema_path.exists():
16
- typer.echo(f"Error: Schema file {schema_path} does not exist", err=True)
17
- raise typer.Exit(1)
18
- from .utils.openapi import generate_api_client, load_schema
19
-
20
- try:
21
- schema = load_schema(schema_path)
22
- except Exception as e:
23
- typer.echo(f"Error loading schema: {e}", err=True)
24
- raise typer.Exit(1)
25
- code = generate_api_client(schema)
26
- print(code)
27
-
28
- @app.command()
29
- def run():
30
- """Run the MCP server"""
31
- from agentr.test import mcp
32
- mcp.run()
33
-
34
- @app.command()
35
- def install(app_name: str):
36
- """Install an app"""
37
- import json
38
-
39
- # Print instructions before asking for API key
40
- typer.echo("╭─ Instruction ─────────────────────────────────────────────────────────────────╮")
41
- typer.echo(" API key is required. Visit https://agentr.dev to create an API key. │")
42
- typer.echo("╰───────────────────────────────────────────────────────────────────────────────╯")
43
- # Prompt for API key
44
- api_key = typer.prompt("Enter your AgentR API key", hide_input=True)
45
-
46
- if app_name == "claude":
47
- typer.echo(f"Installing mcp server for: {app_name}")
48
-
49
- # Determine platform-specific config path
50
- if sys.platform == "darwin": # macOS
51
- config_path = Path.home() / "Library/Application Support/Claude/claude_desktop_config.json"
52
- elif sys.platform == "win32": # Windows
53
- config_path = Path.home() / "AppData/Roaming/Claude/claude_desktop_config.json"
54
- else:
55
- typer.echo("Unsupported platform. Only macOS and Windows are currently supported.", err=True)
56
- raise typer.Exit(1)
57
-
58
-
59
- with open(config_path, 'r') as f:
60
- config = json.load(f)
61
- config['mcpServers']['agentr-r'] = {
62
- "command": "uvx",
63
- "args": ["agentr@latest", "run"],
64
- "env": {
65
- "AGENTR_API_KEY": api_key
66
- }
67
- }
68
- with open(config_path, 'w') as f:
69
- json.dump(config, f, indent=4)
70
- typer.echo("App installed successfully")
71
- else:
72
- typer.echo(f"App {app_name} not supported")
73
-
74
- if __name__ == "__main__":
75
- app()
1
+ import typer
2
+ from pathlib import Path
3
+ import sys
4
+
5
+ app = typer.Typer()
6
+
7
+ @app.command()
8
+ def version():
9
+ """Show the version of the CLI"""
10
+ print("agentr version 0.1.0")
11
+
12
+ @app.command()
13
+ def generate(schema_path: Path = typer.Option(..., "--schema", "-s")):
14
+ """Generate API client from OpenAPI schema"""
15
+ if not schema_path.exists():
16
+ typer.echo(f"Error: Schema file {schema_path} does not exist", err=True)
17
+ raise typer.Exit(1)
18
+ from .utils.openapi import generate_api_client, load_schema
19
+
20
+ try:
21
+ schema = load_schema(schema_path)
22
+ except Exception as e:
23
+ typer.echo(f"Error loading schema: {e}", err=True)
24
+ raise typer.Exit(1)
25
+ code = generate_api_client(schema)
26
+ print(code)
27
+
28
+ @app.command()
29
+ def run():
30
+ """Run the MCP server"""
31
+ from agentr.server import AgentRServer
32
+ mcp = AgentRServer(name="AgentR Server", description="AgentR Server")
33
+ mcp.run()
34
+
35
+ @app.command()
36
+ def install(app_name: str):
37
+ """Install an app"""
38
+ import json
39
+
40
+ # Print instructions before asking for API key
41
+ typer.echo("╭─ Instruction ─────────────────────────────────────────────────────────────────╮")
42
+ typer.echo("│ API key is required. Visit https://agentr.dev to create an API key. │")
43
+ typer.echo("╰───────────────────────────────────────────────────────────────────────────────╯")
44
+ # Prompt for API key
45
+ api_key = typer.prompt("Enter your AgentR API key", hide_input=True)
46
+
47
+ if app_name == "claude":
48
+ typer.echo(f"Installing mcp server for: {app_name}")
49
+
50
+ # Determine platform-specific config path
51
+ if sys.platform == "darwin": # macOS
52
+ config_path = Path.home() / "Library/Application Support/Claude/claude_desktop_config.json"
53
+ elif sys.platform == "win32": # Windows
54
+ config_path = Path.home() / "AppData/Roaming/Claude/claude_desktop_config.json"
55
+ else:
56
+ typer.echo("Unsupported platform. Only macOS and Windows are currently supported.", err=True)
57
+ raise typer.Exit(1)
58
+
59
+
60
+ with open(config_path, 'r') as f:
61
+ config = json.load(f)
62
+ config['mcpServers']['agentr'] = {
63
+ "command": "uvx",
64
+ "args": ["agentr@latest", "run"],
65
+ "env": {
66
+ "AGENTR_API_KEY": api_key
67
+ }
68
+ }
69
+ with open(config_path, 'w') as f:
70
+ json.dump(config, f, indent=4)
71
+ typer.echo("App installed successfully")
72
+ else:
73
+ typer.echo(f"App {app_name} not supported")
74
+
75
+ if __name__ == "__main__":
76
+ app()
agentr/config.py ADDED
@@ -0,0 +1,15 @@
1
+ from pydantic import BaseModel
2
+ from typing import Literal
3
+
4
+ class StoreConfig(BaseModel):
5
+ type: Literal["memory", "environment"]
6
+
7
+ class IntegrationConfig(BaseModel):
8
+ name: str
9
+ type: Literal["api_key", "agentr"]
10
+ credentials: dict | None = None
11
+ store: StoreConfig | None = None
12
+
13
+ class AppConfig(BaseModel):
14
+ name: str
15
+ integration: IntegrationConfig | None = None
agentr/exceptions.py CHANGED
@@ -1,5 +1,6 @@
1
- class NotAuthorizedError(Exception):
2
- """Raised when a user is not authorized to access a resource or perform an action."""
3
- def __init__(self, message="Not authorized to perform this action"):
4
- self.message = message
5
- super().__init__(self.message)
1
+ class NotAuthorizedError(Exception):
2
+ """Raised when a user is not authorized to access a resource or perform an action."""
3
+ def __init__(self, message="Not authorized to perform this action"):
4
+ self.message = message
5
+ super().__init__(self.message)
6
+
agentr/integration.py CHANGED
@@ -1,98 +1,161 @@
1
- from abc import ABC, abstractmethod
2
- import os
3
- import sys
4
-
5
- from loguru import logger
6
- from agentr.store import Store
7
- import httpx
8
-
9
- """
10
- Integration defines how a Application needs to authorize.
11
- It is responsible for authenticating application with the service provider.
12
- Supported integrations:
13
- - AgentR Integration
14
- - API Key Integration
15
- """
16
-
17
- class Integration(ABC):
18
- def __init__(self, name: str, store: Store = None):
19
- self.name = name
20
- self.store = store
21
-
22
- @abstractmethod
23
- def get_credentials(self):
24
- pass
25
-
26
- @abstractmethod
27
- def set_credentials(self, credentials: dict):
28
- pass
29
-
30
- class ApiKeyIntegration(Integration):
31
- def __init__(self, name: str, store: Store = None, **kwargs):
32
- super().__init__(name, store, **kwargs)
33
-
34
- def get_credentials(self):
35
- credentials = self.store.get(self.name)
36
- return credentials
37
-
38
- def set_credentials(self, credentials: dict):
39
- self.store.set(self.name, credentials)
40
-
41
- def authorize(self):
42
- return {"text": "Please configure the environment variable {self.name}_API_KEY"}
43
-
44
-
45
-
46
- class AgentRIntegration(Integration):
47
- def __init__(self, name: str, api_key: str = None, **kwargs):
48
- super().__init__(name, **kwargs)
49
- self.api_key = api_key or os.getenv("AGENTR_API_KEY")
50
- if not self.api_key:
51
- logger.error("API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable.")
52
- raise ValueError("AgentR API key required - get one at https://agentr.dev")
53
-
54
- self.base_url = "https://auth.agentr.dev"
55
- self.user_id = "default"
56
-
57
- def _create_session_token(self):
58
- url = "https://auth.agentr.dev/connect/sessions"
59
- body = {
60
- "end_user": {
61
- "id": self.user_id,
62
- },
63
- "allowed_integrations": [self.name]
64
- }
65
- response = httpx.post(url, headers={"Authorization": f"Bearer {self.api_key}"}, json=body)
66
- data = response.json()
67
- print(data)
68
- return data["data"]["token"]
69
-
70
- def _get_authorize_url(self):
71
- session_token = self._create_session_token()
72
- return f"https://auth.agentr.dev/oauth/connect/{self.name}?connect_session_token={session_token}"
73
-
74
- def get_connection_by_owner(self):
75
- url = f"https://auth.agentr.dev/connection?endUserId={self.user_id}"
76
- response = httpx.get(url, headers={"Authorization": f"Bearer {self.api_key}"})
77
- if response.status_code == 200:
78
- connections = response.json()["connections"]
79
- for connection in connections:
80
- if connection["provider_config_key"] == self.name:
81
- return connection["connection_id"]
82
- return None
83
-
84
- def set_credentials(self, credentials: dict):
85
- raise NotImplementedError("AgentR Integration does not support setting credentials. Visit the authorize url to set credentials.")
86
-
87
- def get_credentials(self):
88
- connection_id = self.get_connection_by_owner()
89
- logger.info(f"Connection ID: {connection_id}")
90
- if connection_id:
91
- response = httpx.get(f"{self.base_url}/connection/{connection_id}?provider_config_key={self.name}", headers={"Authorization": f"Bearer {self.api_key}"})
92
- data = response.json()
93
- return data.get("credentials")
94
- return None
95
-
96
- def authorize(self):
97
- url = self._get_authorize_url()
98
- return f"Please authorize the application by clicking the link {url}"
1
+ from abc import ABC, abstractmethod
2
+ import os
3
+
4
+ from loguru import logger
5
+ from agentr.exceptions import NotAuthorizedError
6
+ from agentr.store import Store
7
+ import httpx
8
+
9
+ """
10
+ Integration defines how a Application needs to authorize.
11
+ It is responsible for authenticating application with the service provider.
12
+ Supported integrations:
13
+ - AgentR Integration
14
+ - API Key Integration
15
+ """
16
+
17
+
18
+ class Integration(ABC):
19
+ """Abstract base class for handling application integrations and authentication.
20
+
21
+ This class defines the interface for different types of integrations that handle
22
+ authentication and authorization with external services.
23
+
24
+ Args:
25
+ name: The name identifier for this integration
26
+ store: Optional Store instance for persisting credentials and other data
27
+
28
+ Attributes:
29
+ name: The name identifier for this integration
30
+ store: Store instance for persisting credentials and other data
31
+ """
32
+ def __init__(self, name: str, store: Store = None):
33
+ self.name = name
34
+ self.store = store
35
+
36
+ @abstractmethod
37
+ def authorize(self):
38
+ """Authorize the integration.
39
+
40
+ Returns:
41
+ str: Authorization URL.
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ def get_credentials(self):
47
+ """Get credentials for the integration.
48
+
49
+ Returns:
50
+ dict: Credentials for the integration.
51
+
52
+ Raises:
53
+ NotAuthorizedError: If credentials are not found.
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ def set_credentials(self, credentials: dict):
59
+ """Set credentials for the integration.
60
+
61
+ Args:
62
+ credentials: Credentials for the integration.
63
+ """
64
+ pass
65
+
66
+ class ApiKeyIntegration(Integration):
67
+ def __init__(self, name: str, store: Store = None, **kwargs):
68
+ super().__init__(name, store, **kwargs)
69
+
70
+ def get_credentials(self):
71
+ credentials = self.store.get(self.name)
72
+ return credentials
73
+
74
+ def set_credentials(self, credentials: dict):
75
+ self.store.set(self.name, credentials)
76
+
77
+ def authorize(self):
78
+ return {"text": "Please configure the environment variable {self.name}_API_KEY"}
79
+
80
+
81
+ class AgentRIntegration(Integration):
82
+ """Integration class for AgentR API authentication and authorization.
83
+
84
+ This class handles API key authentication and OAuth authorization flow for AgentR services.
85
+
86
+ Args:
87
+ name (str): Name of the integration
88
+ api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var
89
+ **kwargs: Additional keyword arguments passed to parent Integration class
90
+
91
+ Raises:
92
+ ValueError: If no API key is provided or found in environment variables
93
+ """
94
+ def __init__(self, name: str, api_key: str = None, **kwargs):
95
+ super().__init__(name, **kwargs)
96
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
97
+ if not self.api_key:
98
+ logger.error("API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable.")
99
+ raise ValueError("AgentR API key required - get one at https://agentr.dev")
100
+ self.base_url = os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
101
+
102
+ def set_credentials(self, credentials: dict| None = None):
103
+ """Set credentials for the integration.
104
+
105
+ This method is not implemented for AgentR integration. Instead it redirects to the authorize flow.
106
+
107
+ Args:
108
+ credentials (dict | None, optional): Credentials dict (not used). Defaults to None.
109
+
110
+ Returns:
111
+ str: Authorization URL from authorize() method
112
+ """
113
+ return self.authorize()
114
+ # raise NotImplementedError("AgentR Integration does not support setting credentials. Visit the authorize url to set credentials.")
115
+
116
+ def get_credentials(self):
117
+ """Get credentials for the integration from the AgentR API.
118
+
119
+ Makes API request to retrieve stored credentials for this integration.
120
+
121
+ Returns:
122
+ dict: Credentials data from API response
123
+
124
+ Raises:
125
+ NotAuthorizedError: If credentials are not found (404 response)
126
+ HTTPError: For other API errors
127
+ """
128
+ response = httpx.get(
129
+ f"{self.base_url}/api/{self.name}/credentials/",
130
+ headers={
131
+ "accept": "application/json",
132
+ "X-API-KEY": self.api_key
133
+ }
134
+ )
135
+ if response.status_code == 404:
136
+ action = self.authorize()
137
+ raise NotAuthorizedError(action)
138
+ response.raise_for_status()
139
+ data = response.json()
140
+ return data
141
+
142
+ def authorize(self):
143
+ """Get authorization URL for the integration.
144
+
145
+ Makes API request to get OAuth authorization URL.
146
+
147
+ Returns:
148
+ str: Message containing authorization URL
149
+
150
+ Raises:
151
+ HTTPError: If API request fails
152
+ """
153
+ response = httpx.get(
154
+ f"{self.base_url}/api/{self.name}/authorize/",
155
+ headers={
156
+ "X-API-KEY": self.api_key
157
+ }
158
+ )
159
+ response.raise_for_status()
160
+ url = response.json()
161
+ return f"Please authorize the application by clicking the link {url}"
@@ -0,0 +1,25 @@
1
+ # Integrations
2
+
3
+ This package provides integration classes for handling authentication and authorization with external services.
4
+
5
+ ## Overview
6
+
7
+ An Integration defines how an application authenticates and authorizes with a service provider. The base `Integration` class provides an interface that all integrations must implement.
8
+
9
+ ## Supported Integrations
10
+
11
+ ### AgentR Integration
12
+ The `AgentRIntegration` class handles OAuth-based authentication flow with the AgentR API. It requires an API key which can be obtained from [agentr.dev](https://agentr.dev).
13
+
14
+ ### API Key Integration
15
+ The `ApiKeyIntegration` class provides a simple API key based authentication mechanism. API keys are configured via environment variables.
16
+
17
+ ## Usage
18
+
19
+ Each integration implements three key methods:
20
+
21
+ - `authorize()` - Initiates the authorization flow
22
+ - `get_credentials()` - Retrieves stored credentials
23
+ - `set_credentials()` - Stores new credentials
24
+
25
+ See the individual integration classes for specific usage details.
@@ -0,0 +1,5 @@
1
+ from agentr.integrations.agentr import AgentRIntegration
2
+ from agentr.integrations.base import Integration
3
+ from agentr.integrations.api_key import ApiKeyIntegration
4
+
5
+ __all__ = ["AgentRIntegration", "Integration", "ApiKeyIntegration"]
@@ -0,0 +1,87 @@
1
+ from agentr.integrations.base import Integration
2
+ import os
3
+ import httpx
4
+ from loguru import logger
5
+ from agentr.exceptions import NotAuthorizedError
6
+
7
+ class AgentRIntegration(Integration):
8
+ """Integration class for AgentR API authentication and authorization.
9
+
10
+ This class handles API key authentication and OAuth authorization flow for AgentR services.
11
+
12
+ Args:
13
+ name (str): Name of the integration
14
+ api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var
15
+ **kwargs: Additional keyword arguments passed to parent Integration class
16
+
17
+ Raises:
18
+ ValueError: If no API key is provided or found in environment variables
19
+ """
20
+ def __init__(self, name: str, api_key: str = None, **kwargs):
21
+ super().__init__(name, **kwargs)
22
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
23
+ if not self.api_key:
24
+ logger.error("API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable.")
25
+ raise ValueError("AgentR API key required - get one at https://agentr.dev")
26
+ self.base_url = os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
27
+
28
+ def set_credentials(self, credentials: dict| None = None):
29
+ """Set credentials for the integration.
30
+
31
+ This method is not implemented for AgentR integration. Instead it redirects to the authorize flow.
32
+
33
+ Args:
34
+ credentials (dict | None, optional): Credentials dict (not used). Defaults to None.
35
+
36
+ Returns:
37
+ str: Authorization URL from authorize() method
38
+ """
39
+ return self.authorize()
40
+ # raise NotImplementedError("AgentR Integration does not support setting credentials. Visit the authorize url to set credentials.")
41
+
42
+ def get_credentials(self):
43
+ """Get credentials for the integration from the AgentR API.
44
+
45
+ Makes API request to retrieve stored credentials for this integration.
46
+
47
+ Returns:
48
+ dict: Credentials data from API response
49
+
50
+ Raises:
51
+ NotAuthorizedError: If credentials are not found (404 response)
52
+ HTTPError: For other API errors
53
+ """
54
+ response = httpx.get(
55
+ f"{self.base_url}/api/{self.name}/credentials/",
56
+ headers={
57
+ "accept": "application/json",
58
+ "X-API-KEY": self.api_key
59
+ }
60
+ )
61
+ if response.status_code == 404:
62
+ action = self.authorize()
63
+ raise NotAuthorizedError(action)
64
+ response.raise_for_status()
65
+ data = response.json()
66
+ return data
67
+
68
+ def authorize(self):
69
+ """Get authorization URL for the integration.
70
+
71
+ Makes API request to get OAuth authorization URL.
72
+
73
+ Returns:
74
+ str: Message containing authorization URL
75
+
76
+ Raises:
77
+ HTTPError: If API request fails
78
+ """
79
+ response = httpx.get(
80
+ f"{self.base_url}/api/{self.name}/authorize/",
81
+ headers={
82
+ "X-API-KEY": self.api_key
83
+ }
84
+ )
85
+ response.raise_for_status()
86
+ url = response.json()
87
+ return f"Please authorize the application by clicking the link {url}"
@@ -0,0 +1,16 @@
1
+ from agentr.integrations.base import Integration
2
+ from agentr.store import Store
3
+
4
+ class ApiKeyIntegration(Integration):
5
+ def __init__(self, name: str, store: Store = None, **kwargs):
6
+ super().__init__(name, store, **kwargs)
7
+
8
+ def get_credentials(self):
9
+ credentials = self.store.get(self.name)
10
+ return credentials
11
+
12
+ def set_credentials(self, credentials: dict):
13
+ self.store.set(self.name, credentials)
14
+
15
+ def authorize(self):
16
+ return {"text": "Please configure the environment variable {self.name}_API_KEY"}
@@ -0,0 +1,60 @@
1
+ from abc import ABC, abstractmethod
2
+ import os
3
+
4
+ from loguru import logger
5
+ from agentr.exceptions import NotAuthorizedError
6
+ from agentr.store import Store
7
+ import httpx
8
+
9
+
10
+
11
+
12
+ class Integration(ABC):
13
+ """Abstract base class for handling application integrations and authentication.
14
+
15
+ This class defines the interface for different types of integrations that handle
16
+ authentication and authorization with external services.
17
+
18
+ Args:
19
+ name: The name identifier for this integration
20
+ store: Optional Store instance for persisting credentials and other data
21
+
22
+ Attributes:
23
+ name: The name identifier for this integration
24
+ store: Store instance for persisting credentials and other data
25
+ """
26
+ def __init__(self, name: str, store: Store = None):
27
+ self.name = name
28
+ self.store = store
29
+
30
+ @abstractmethod
31
+ def authorize(self):
32
+ """Authorize the integration.
33
+
34
+ Returns:
35
+ str: Authorization URL.
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ def get_credentials(self):
41
+ """Get credentials for the integration.
42
+
43
+ Returns:
44
+ dict: Credentials for the integration.
45
+
46
+ Raises:
47
+ NotAuthorizedError: If credentials are not found.
48
+ """
49
+ pass
50
+
51
+ @abstractmethod
52
+ def set_credentials(self, credentials: dict):
53
+ """Set credentials for the integration.
54
+
55
+ Args:
56
+ credentials: Credentials for the integration.
57
+ """
58
+ pass
59
+
60
+