agentr 0.1.6__py3-none-any.whl → 0.1.7__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/integration.py CHANGED
@@ -1,98 +1,85 @@
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
+ 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 authorize(self):
24
+ pass
25
+
26
+ @abstractmethod
27
+ def get_credentials(self):
28
+ pass
29
+
30
+ @abstractmethod
31
+ def set_credentials(self, credentials: dict):
32
+ pass
33
+
34
+ class ApiKeyIntegration(Integration):
35
+ def __init__(self, name: str, store: Store = None, **kwargs):
36
+ super().__init__(name, store, **kwargs)
37
+
38
+ def get_credentials(self):
39
+ credentials = self.store.get(self.name)
40
+ return credentials
41
+
42
+ def set_credentials(self, credentials: dict):
43
+ self.store.set(self.name, credentials)
44
+
45
+ def authorize(self):
46
+ return {"text": "Please configure the environment variable {self.name}_API_KEY"}
47
+
48
+
49
+
50
+ class AgentRIntegration(Integration):
51
+ def __init__(self, name: str, api_key: str = None, **kwargs):
52
+ super().__init__(name, **kwargs)
53
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
54
+ if not self.api_key:
55
+ 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.")
56
+ raise ValueError("AgentR API key required - get one at https://agentr.dev")
57
+ self.base_url = os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
58
+
59
+
60
+ def set_credentials(self, credentials: dict| None = None):
61
+ return self.authorize()
62
+ # raise NotImplementedError("AgentR Integration does not support setting credentials. Visit the authorize url to set credentials.")
63
+
64
+ def get_credentials(self):
65
+ response = httpx.get(
66
+ f"{self.base_url}/api/{self.name}/credentials/",
67
+ headers={
68
+ "accept": "application/json",
69
+ "X-API-KEY": self.api_key
70
+ }
71
+ )
72
+ response.raise_for_status()
73
+ data = response.json()
74
+ return data
75
+
76
+ def authorize(self):
77
+ response = httpx.get(
78
+ f"{self.base_url}/api/{self.name}/authorize/",
79
+ headers={
80
+ "X-API-KEY": self.api_key
81
+ }
82
+ )
83
+ response.raise_for_status()
84
+ url = response.json()
85
+ return f"Please authorize the application by clicking the link {url}"
agentr/server.py CHANGED
@@ -1,105 +1,113 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Literal
3
- from mcp.server.fastmcp import FastMCP
4
- from agentr.integration import AgentRIntegration, ApiKeyIntegration
5
- from agentr.store import EnvironmentStore, MemoryStore
6
- from pydantic import BaseModel
7
- from loguru import logger
8
-
9
- class StoreConfig(BaseModel):
10
- type: Literal["memory", "environment"]
11
-
12
- class IntegrationConfig(BaseModel):
13
- name: str
14
- type: Literal["api_key", "agentr"]
15
- credentials: dict | None = None
16
- store: StoreConfig | None = None
17
-
18
- class AppConfig(BaseModel):
19
- name: str
20
- integration: IntegrationConfig | None = None
21
-
22
- class Server(FastMCP, ABC):
23
- """
24
- Server is responsible for managing the applications and the store
25
- It also acts as a router for the applications, and exposed to the client
26
-
27
- """
28
- def __init__(self, name: str, description: str, **kwargs):
29
- super().__init__(name, description, **kwargs)
30
-
31
- @abstractmethod
32
- def _load_apps(self):
33
- pass
34
-
35
-
36
- class TestServer(Server):
37
- """
38
- Test server for development purposes
39
- """
40
- def __init__(self, name: str, description: str, apps_list: list[AppConfig] = [], **kwargs):
41
- super().__init__(name, description=description, **kwargs)
42
- self.apps_list = [AppConfig.model_validate(app) for app in apps_list]
43
- self._load_apps()
44
-
45
- def _get_store(self, store_config: StoreConfig):
46
- if store_config.type == "memory":
47
- return MemoryStore()
48
- elif store_config.type == "environment":
49
- return EnvironmentStore()
50
- return None
51
-
52
- def _get_integration(self, integration_config: IntegrationConfig):
53
- if integration_config.type == "api_key":
54
- store = self._get_store(integration_config.store)
55
- integration = ApiKeyIntegration(integration_config.name, store=store)
56
- if integration_config.credentials:
57
- integration.set_credentials(integration_config.credentials)
58
- return integration
59
- elif integration_config.type == "agentr":
60
- integration = AgentRIntegration(integration_config.name, api_key=integration_config.credentials.get("api_key") if integration_config.credentials else None)
61
- return integration
62
- return None
63
-
64
- def _load_app(self, app_config: AppConfig):
65
- name = app_config.name
66
- if name == "zenquotes":
67
- from agentr.applications.zenquotes.app import ZenQuoteApp
68
- return ZenQuoteApp()
69
- elif name == "tavily":
70
- from agentr.applications.tavily.app import TavilyApp
71
- integration = self._get_integration(app_config.integration)
72
- return TavilyApp(integration=integration)
73
- elif name == "github":
74
- from agentr.applications.github.app import GithubApp
75
- integration = self._get_integration(app_config.integration)
76
- return GithubApp(integration=integration)
77
- elif name == "google-calendar":
78
- from agentr.applications.google_calendar.app import GoogleCalendarApp
79
- integration = self._get_integration(app_config.integration)
80
- return GoogleCalendarApp(integration=integration)
81
- elif name == "gmail":
82
- from agentr.applications.google_mail.app import GmailApp
83
- integration = self._get_integration(app_config.integration)
84
- return GmailApp(integration=integration)
85
- elif name == "resend":
86
- from agentr.applications.resend.app import ResendApp
87
- integration = self._get_integration(app_config.integration)
88
- return ResendApp(integration=integration)
89
- elif name == "reddit":
90
- from agentr.applications.reddit.app import RedditApp
91
- integration = self._get_integration(app_config.integration)
92
- return RedditApp(integration=integration)
93
- else:
94
- return None
95
-
96
- def _load_apps(self):
97
- logger.info(f"Loading apps: {self.apps_list}")
98
- for app_config in self.apps_list:
99
- app = self._load_app(app_config)
100
- if app:
101
- tools = app.list_tools()
102
- for tool in tools:
103
- self.add_tool(tool)
104
-
105
-
1
+ from abc import ABC, abstractmethod
2
+ import httpx
3
+ from mcp.server.fastmcp import FastMCP
4
+ from agentr.applications import app_from_name
5
+ from agentr.integration import AgentRIntegration, ApiKeyIntegration
6
+ from agentr.store import EnvironmentStore, MemoryStore
7
+ from agentr.config import AppConfig, IntegrationConfig, StoreConfig
8
+ from loguru import logger
9
+ import os
10
+
11
+ class Server(FastMCP, ABC):
12
+ """
13
+ Server is responsible for managing the applications and the store
14
+ It also acts as a router for the applications, and exposed to the client
15
+
16
+ """
17
+ def __init__(self, name: str, description: str, **kwargs):
18
+ super().__init__(name, description, **kwargs)
19
+
20
+ @abstractmethod
21
+ def _load_apps(self):
22
+ pass
23
+
24
+
25
+ class LocalServer(Server):
26
+ """
27
+ Local server for development purposes
28
+ """
29
+ def __init__(self, name: str, description: str, apps_list: list[AppConfig] = [], **kwargs):
30
+ super().__init__(name, description=description, **kwargs)
31
+ self.apps_list = [AppConfig.model_validate(app) for app in apps_list]
32
+ self._load_apps()
33
+
34
+ def _get_store(self, store_config: StoreConfig):
35
+ if store_config.type == "memory":
36
+ return MemoryStore()
37
+ elif store_config.type == "environment":
38
+ return EnvironmentStore()
39
+ return None
40
+
41
+ def _get_integration(self, integration_config: IntegrationConfig):
42
+ if not integration_config:
43
+ return None
44
+ if integration_config.type == "api_key":
45
+ store = self._get_store(integration_config.store)
46
+ integration = ApiKeyIntegration(integration_config.name, store=store)
47
+ if integration_config.credentials:
48
+ integration.set_credentials(integration_config.credentials)
49
+ return integration
50
+ elif integration_config.type == "agentr":
51
+ integration = AgentRIntegration(integration_config.name, api_key=integration_config.credentials.get("api_key") if integration_config.credentials else None)
52
+ return integration
53
+ return None
54
+
55
+ def _load_app(self, app_config: AppConfig):
56
+ name = app_config.name
57
+ integration = self._get_integration(app_config.integration)
58
+ app = app_from_name(name)(integration=integration)
59
+ return app
60
+
61
+ def _load_apps(self):
62
+ logger.info(f"Loading apps: {self.apps_list}")
63
+ for app_config in self.apps_list:
64
+ app = self._load_app(app_config)
65
+ if app:
66
+ tools = app.list_tools()
67
+ for tool in tools:
68
+ self.add_tool(tool)
69
+
70
+
71
+
72
+ class AgentRServer(Server):
73
+ """
74
+ AgentR server. Connects to the AgentR API to get the apps and tools. Only supports agentr integrations.
75
+ """
76
+ def __init__(self, name: str, description: str, api_key: str | None = None, **kwargs):
77
+ super().__init__(name, description=description, **kwargs)
78
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
79
+ self.base_url = os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
80
+ if not self.api_key:
81
+ raise ValueError("API key required - get one at https://agentr.dev")
82
+ self._load_apps()
83
+
84
+ def _load_app(self, app_config: AppConfig):
85
+ name = app_config.name
86
+ if app_config.integration:
87
+ integration_name = app_config.integration.name
88
+ integration = AgentRIntegration(integration_name, api_key=self.api_key)
89
+ else:
90
+ integration = None
91
+ app = app_from_name(name)(integration=integration)
92
+ return app
93
+
94
+ def _list_apps_with_integrations(self):
95
+ # TODO: get this from the API
96
+ response = httpx.get(
97
+ f"{self.base_url}/api/apps/",
98
+ headers={
99
+ "X-API-KEY": self.api_key
100
+ }
101
+ )
102
+ apps = response.json()
103
+ logger.info(f"Apps: {apps}")
104
+ return [AppConfig.model_validate(app) for app in apps]
105
+
106
+ def _load_apps(self):
107
+ apps = self._list_apps_with_integrations()
108
+ for app in apps:
109
+ app = self._load_app(app)
110
+ if app:
111
+ tools = app.list_tools()
112
+ for tool in tools:
113
+ self.add_tool(tool)
agentr/store.py CHANGED
@@ -1,71 +1,71 @@
1
- import os
2
- from abc import ABC, abstractmethod
3
-
4
-
5
- class Store(ABC):
6
- @abstractmethod
7
- def get(self, key: str):
8
- pass
9
-
10
- @abstractmethod
11
- def set(self, key: str, value: str):
12
- pass
13
-
14
- @abstractmethod
15
- def delete(self, key: str):
16
- pass
17
-
18
- class MemoryStore:
19
- """
20
- Acts as credential store for the applications.
21
- Responsible for storing and retrieving credentials.
22
- Ideally should be a key value store
23
- """
24
- def __init__(self):
25
- self.data = {}
26
-
27
- def get(self, key: str):
28
- return self.data.get(key)
29
-
30
- def set(self, key: str, value: str):
31
- self.data[key] = value
32
-
33
- def delete(self, key: str):
34
- del self.data[key]
35
-
36
-
37
- class EnvironmentStore(Store):
38
- """
39
- Store that uses environment variables to store credentials.
40
- """
41
- def __init__(self):
42
- pass
43
-
44
- def get(self, key: str):
45
- return {"api_key": os.getenv(key)}
46
-
47
- def set(self, key: str, value: str):
48
- os.environ[key] = value
49
-
50
- def delete(self, key: str):
51
- del os.environ[key]
52
-
53
- class RedisStore(Store):
54
- """
55
- Store that uses a redis database to store credentials.
56
- """
57
- def __init__(self, host: str, port: int, db: int):
58
- import redis
59
- self.host = host
60
- self.port = port
61
- self.db = db
62
- self.redis = redis.Redis(host=self.host, port=self.port, db=self.db)
63
-
64
- def get(self, key: str):
65
- return self.redis.get(key)
66
-
67
- def set(self, key: str, value: str):
68
- self.redis.set(key, value)
69
-
70
- def delete(self, key: str):
1
+ import os
2
+ from abc import ABC, abstractmethod
3
+
4
+
5
+ class Store(ABC):
6
+ @abstractmethod
7
+ def get(self, key: str):
8
+ pass
9
+
10
+ @abstractmethod
11
+ def set(self, key: str, value: str):
12
+ pass
13
+
14
+ @abstractmethod
15
+ def delete(self, key: str):
16
+ pass
17
+
18
+ class MemoryStore:
19
+ """
20
+ Acts as credential store for the applications.
21
+ Responsible for storing and retrieving credentials.
22
+ Ideally should be a key value store
23
+ """
24
+ def __init__(self):
25
+ self.data = {}
26
+
27
+ def get(self, key: str):
28
+ return self.data.get(key)
29
+
30
+ def set(self, key: str, value: str):
31
+ self.data[key] = value
32
+
33
+ def delete(self, key: str):
34
+ del self.data[key]
35
+
36
+
37
+ class EnvironmentStore(Store):
38
+ """
39
+ Store that uses environment variables to store credentials.
40
+ """
41
+ def __init__(self):
42
+ pass
43
+
44
+ def get(self, key: str):
45
+ return {"api_key": os.getenv(key)}
46
+
47
+ def set(self, key: str, value: str):
48
+ os.environ[key] = value
49
+
50
+ def delete(self, key: str):
51
+ del os.environ[key]
52
+
53
+ class RedisStore(Store):
54
+ """
55
+ Store that uses a redis database to store credentials.
56
+ """
57
+ def __init__(self, host: str, port: int, db: int):
58
+ import redis
59
+ self.host = host
60
+ self.port = port
61
+ self.db = db
62
+ self.redis = redis.Redis(host=self.host, port=self.port, db=self.db)
63
+
64
+ def get(self, key: str):
65
+ return self.redis.get(key)
66
+
67
+ def set(self, key: str, value: str):
68
+ self.redis.set(key, value)
69
+
70
+ def delete(self, key: str):
71
71
  self.redis.delete(key)
agentr/test.py CHANGED
@@ -1,38 +1,40 @@
1
- from agentr.server import TestServer
2
- from agentr.store import MemoryStore
3
-
4
- store = MemoryStore()
5
- apps_list = [
6
- {
7
- "name": "tavily",
8
- "integration": {
9
- "name": "tavily_api_key",
10
- "type": "api_key",
11
- "store": {
12
- "type": "environment",
13
- }
14
- },
15
- },
16
- {
17
- "name": "zenquotes",
18
- "integration": None
19
- },
20
- {
21
- "name": "github",
22
- "integration": {
23
- "name": "github",
24
- "type": "agentr",
25
- }
26
- }
27
- ]
28
- mcp = TestServer(name="Test Server", description="Test Server", apps_list=apps_list)
29
-
30
- async def test():
31
- tools = await mcp.list_tools()
32
- print(tools)
33
- result = await mcp.call_tool("star_repository", {"repo_full_name": "manojbajaj95/config"})
34
- print(result)
35
-
36
- if __name__ == "__main__":
37
- import asyncio
1
+ from agentr.server import LocalServer
2
+ from agentr.store import MemoryStore
3
+
4
+ store = MemoryStore()
5
+ apps_list = [
6
+ {
7
+ "name": "tavily",
8
+ "integration": {
9
+ "name": "tavily_api_key",
10
+ "type": "api_key",
11
+ "store": {
12
+ "type": "environment",
13
+ }
14
+ },
15
+ },
16
+ {
17
+ "name": "zenquotes",
18
+ "integration": None
19
+ },
20
+ {
21
+ "name": "github",
22
+ "integration": {
23
+ "name": "github",
24
+ "type": "agentr",
25
+ }
26
+ }
27
+ ]
28
+ mcp = LocalServer(name="Test Server", description="Test Server", apps_list=apps_list)
29
+
30
+
31
+ async def test():
32
+ tools = await mcp.list_tools()
33
+ from pprint import pprint
34
+ pprint(tools)
35
+ result = await mcp.call_tool("star_repository", {"repo_full_name": "manojbajaj95/config"})
36
+ print(result)
37
+
38
+ if __name__ == "__main__":
39
+ import asyncio
38
40
  asyncio.run(test())