agentr 0.1.2__tar.gz → 0.1.3__tar.gz

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.
@@ -8,3 +8,7 @@ wheels/
8
8
 
9
9
  # Virtual environments
10
10
  .venv
11
+
12
+ # Environment variables
13
+ .env
14
+ .env.local
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentr
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Add your description here
5
5
  Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
6
6
  Requires-Python: >=3.11
7
+ Requires-Dist: loguru>=0.7.3
7
8
  Requires-Dist: mcp>=1.5.0
8
9
  Requires-Dist: pyyaml>=6.0.2
9
10
  Requires-Dist: typer>=0.15.2
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentr"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -8,6 +8,7 @@ authors = [
8
8
  ]
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
11
+ "loguru>=0.7.3",
11
12
  "mcp>=1.5.0",
12
13
  "pyyaml>=6.0.2",
13
14
  "typer>=0.15.2",
@@ -1,8 +1,11 @@
1
1
  from abc import ABC, abstractmethod
2
- from agentr.store import Store
2
+ from agentr.integration import Integration
3
3
  import httpx
4
4
 
5
5
  class Application(ABC):
6
+ """
7
+ Application is collection of tools that can be used by an agent.
8
+ """
6
9
  def __init__(self, name: str, **kwargs):
7
10
  self.name = name
8
11
 
@@ -12,9 +15,12 @@ class Application(ABC):
12
15
 
13
16
 
14
17
  class APIApplication(Application):
15
- def __init__(self, name: str, store: Store, **kwargs):
18
+ """
19
+ APIApplication is an application that uses an API to interact with the world.
20
+ """
21
+ def __init__(self, name: str, integration: Integration = None, **kwargs):
16
22
  super().__init__(name, **kwargs)
17
- self.store = store
23
+ self.integration = integration
18
24
 
19
25
  def _get_headers(self):
20
26
  return {}
@@ -22,29 +28,30 @@ class APIApplication(Application):
22
28
  def _get(self, url, params=None):
23
29
  headers = self._get_headers()
24
30
  response = httpx.get(url, headers=headers, params=params)
31
+ response.raise_for_status()
25
32
  return response
26
33
 
27
34
  def _post(self, url, data):
28
35
  headers = self._get_headers()
29
- response = httpx.post(url, headers=headers, data=data)
36
+ response = httpx.post(url, headers=headers, json=data)
37
+ response.raise_for_status()
30
38
  return response
31
39
 
32
40
  def _put(self, url, data):
33
41
  headers = self._get_headers()
34
- response = httpx.put(url, headers=headers, data=data)
42
+ response = httpx.put(url, headers=headers, json=data)
43
+ response.raise_for_status()
35
44
  return response
36
45
 
37
46
  def _delete(self, url):
38
47
  headers = self._get_headers()
39
48
  response = httpx.delete(url, headers=headers)
49
+ response.raise_for_status()
40
50
  return response
41
51
 
42
52
  def validate(self):
43
53
  pass
44
54
 
45
- def authorize(self):
46
- pass
47
-
48
55
  @abstractmethod
49
56
  def list_tools(self):
50
57
  pass
@@ -0,0 +1,52 @@
1
+ from agentr.integration import Integration
2
+ from agentr.application import APIApplication
3
+ from loguru import logger
4
+
5
+
6
+ class GithubApp(APIApplication):
7
+ def __init__(self, integration: Integration) -> None:
8
+ super().__init__(name="github", integration=integration)
9
+
10
+ def _get_headers(self):
11
+ if not self.integration:
12
+ raise ValueError("Integration not configured")
13
+ credentials = self.integration.get_credentials()
14
+ if not credentials:
15
+ logger.warning("No credentials found")
16
+ return self.integration.authorize()
17
+ if "headers" in credentials:
18
+ return credentials["headers"]
19
+ return {
20
+ "Authorization": f"Bearer {credentials['access_token']}",
21
+ "Accept": "application/vnd.github.v3+json"
22
+ }
23
+
24
+
25
+ def star_repository(self, repo_full_name: str) -> str:
26
+ """Star a GitHub repository
27
+
28
+ Args:
29
+ repo_full_name: The full name of the repository (e.g. 'owner/repo')
30
+
31
+ Returns:
32
+
33
+ A confirmation message
34
+ """
35
+ try:
36
+ url = f"https://api.github.com/user/starred/{repo_full_name}"
37
+ response = self._put(url, data={})
38
+
39
+ if response.status_code == 204:
40
+ return f"Successfully starred repository {repo_full_name}"
41
+ elif response.status_code == 404:
42
+ return f"Repository {repo_full_name} not found"
43
+ else:
44
+ logger.error(response.text)
45
+ return f"Error starring repository: {response.text}"
46
+ except Exception as e:
47
+ logger.error(e)
48
+ return f"Error starring repository: {e}"
49
+
50
+
51
+ def list_tools(self):
52
+ return [self.star_repository]
@@ -0,0 +1,74 @@
1
+ from agentr.application import APIApplication
2
+ from agentr.integration import Integration
3
+ from loguru import logger
4
+ from datetime import datetime, timedelta
5
+
6
+ class GoogleCalendarApp(APIApplication):
7
+ def __init__(self, integration: Integration) -> None:
8
+ super().__init__(name="google-calendar", integration=integration)
9
+
10
+ def _get_headers(self):
11
+ credentials = self.integration.get_credentials()
12
+ if "headers" in credentials:
13
+ return credentials["headers"]
14
+ return {
15
+ "Authorization": f"Bearer {credentials['access_token']}",
16
+ "Accept": "application/json"
17
+ }
18
+
19
+
20
+ def get_today_events(self) -> str:
21
+ """Get events from your Google Calendar for today
22
+
23
+ Returns:
24
+ A formatted list of today's events or an error message
25
+ """
26
+ if not self.validate():
27
+ logger.warning("Connection not configured correctly")
28
+ return self.authorize()
29
+
30
+ try:
31
+ # Get today's date in ISO format
32
+ today = datetime.now().date()
33
+ tomorrow = today + timedelta(days=1)
34
+
35
+ # Format dates for API
36
+ time_min = f"{today.isoformat()}T00:00:00Z"
37
+ time_max = f"{tomorrow.isoformat()}T00:00:00Z"
38
+
39
+ url = "https://www.googleapis.com/calendar/v3/calendars/primary/events"
40
+ params = {
41
+ "timeMin": time_min,
42
+ "timeMax": time_max,
43
+ "singleEvents": "true",
44
+ "orderBy": "startTime"
45
+ }
46
+
47
+ response = self._get(url, params=params)
48
+
49
+ if response.status_code == 200:
50
+ events = response.json().get("items", [])
51
+ if not events:
52
+ return "No events scheduled for today."
53
+
54
+ result = "Today's events:\n\n"
55
+ for event in events:
56
+ start = event.get("start", {})
57
+ start_time = start.get("dateTime", start.get("date", "All day"))
58
+ if "T" in start_time: # Format datetime
59
+ start_dt = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
60
+ start_time = start_dt.strftime("%I:%M %p")
61
+
62
+ summary = event.get("summary", "Untitled event")
63
+ result += f"- {start_time}: {summary}\n"
64
+
65
+ return result
66
+ else:
67
+ logger.error(response.text)
68
+ return f"Error retrieving calendar events: {response.text}"
69
+ except Exception as e:
70
+ logger.error(e)
71
+ return f"Error retrieving calendar events: {e}"
72
+
73
+ def list_tools(self):
74
+ return [self.get_today_events]
@@ -0,0 +1,68 @@
1
+ from agentr.application import APIApplication
2
+ from agentr.integration import Integration
3
+ from loguru import logger
4
+ import base64
5
+ from email.message import EmailMessage
6
+
7
+ class GmailApp(APIApplication):
8
+ def __init__(self, integration: Integration) -> None:
9
+ super().__init__(name="gmail", integration=integration)
10
+
11
+ def _get_headers(self):
12
+ credentials = self.integration.get_credentials()
13
+ if "headers" in credentials:
14
+ return credentials["headers"]
15
+ return {
16
+ "Authorization": f"Bearer {credentials['access_token']}",
17
+ 'Content-Type': 'application/json'
18
+ }
19
+
20
+ def send_email(self, to: str, subject: str, body: str) -> str:
21
+ """Send an email
22
+
23
+ Args:
24
+ to: The email address of the recipient
25
+ subject: The subject of the email
26
+ body: The body of the email
27
+
28
+ Returns:
29
+
30
+ A confirmation message
31
+ """
32
+ if not self.validate():
33
+ logger.warning("Connection not configured correctly")
34
+ return self.authorize()
35
+ try:
36
+ url = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
37
+
38
+ # Create email in base64 encoded format
39
+ email_message = {
40
+ "raw": self._create_message(to, subject, body)
41
+ }
42
+ logger.info(email_message)
43
+
44
+ # Use json parameter instead of data to properly format JSON
45
+ response = self._post(url, email_message)
46
+
47
+ if response.status_code == 200:
48
+ return f"Successfully sent email to {to}"
49
+ else:
50
+ logger.error(response.text)
51
+ return f"Error sending email: {response.text}"
52
+ except Exception as e:
53
+ logger.error(e)
54
+ return f"Error sending email: {e}"
55
+
56
+ def _create_message(self, to, subject, body):
57
+ message = EmailMessage()
58
+ message['to'] = to
59
+ message['from'] = "manojbajaj95@gmail.com"
60
+ message['subject'] = subject
61
+ message.set_content(body)
62
+
63
+ # Encode as base64 string
64
+ raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
65
+ return raw
66
+
67
+ def list_tools(self):
68
+ return [self.send_email]
@@ -0,0 +1,29 @@
1
+ from agentr.application import APIApplication
2
+ from agentr.integration import Integration
3
+
4
+ class RedditApp(APIApplication):
5
+ def __init__(self, integration: Integration) -> None:
6
+ super().__init__(name="reddit", integration=integration)
7
+
8
+ def _get_headers(self):
9
+ credentials = self.integration.get_credentials()
10
+ if "headers" in credentials:
11
+ return credentials["headers"]
12
+ return {
13
+ "Authorization": f"Bearer {credentials['access_token']}",
14
+ }
15
+
16
+ def get_subreddit_posts(self, subreddit: str) -> str:
17
+ """Get the latest posts from a subreddit
18
+
19
+ Args:
20
+ subreddit: The subreddit to get posts from
21
+
22
+ Returns:
23
+ A list of posts from the subreddit
24
+ """
25
+
26
+
27
+ def list_tools(self):
28
+ return []
29
+
@@ -0,0 +1,43 @@
1
+ from agentr.application import APIApplication
2
+ from agentr.integration import Integration
3
+
4
+ class ResendApp(APIApplication):
5
+ def __init__(self, integration: Integration) -> None:
6
+ super().__init__(name="resend", integration=integration)
7
+
8
+ def _get_headers(self):
9
+ credentials = self.integration.get_credentials()
10
+ if not credentials:
11
+ raise ValueError("No credentials found")
12
+ return {
13
+ "Authorization": f"Bearer {credentials['api_key']}",
14
+ }
15
+
16
+ def send_email(self, to: str, subject: str, content: str) -> str:
17
+ """Send an email using the Resend API
18
+
19
+ Args:
20
+ to: The email address to send the email to
21
+ subject: The subject of the email
22
+ content: The content of the email
23
+
24
+ Returns:
25
+ A message indicating that the email was sent successfully
26
+ """
27
+ credentials = self.integration.get_credentials()
28
+ if not credentials:
29
+ raise ValueError("No credentials found")
30
+ from_email = credentials.get("from_email", "Manoj <manoj@agentr.dev>")
31
+ url = "https://api.resend.com/emails"
32
+ body = {
33
+ "from": from_email,
34
+ "to": [to],
35
+ "subject": subject,
36
+ "text": content
37
+ }
38
+ self._post(url, body)
39
+ return "Sent Successfully"
40
+
41
+ def list_tools(self):
42
+ return [self.send_email]
43
+
@@ -0,0 +1,57 @@
1
+ from agentr.application import APIApplication
2
+ from agentr.integration import Integration
3
+
4
+ class TavilyApp(APIApplication):
5
+ def __init__(self, integration: Integration) -> None:
6
+ name = "tavily"
7
+ self.base_url = "https://api.tavily.com"
8
+ super().__init__(name=name, integration=integration)
9
+
10
+ def _get_headers(self):
11
+ credentials = self.integration.get_credentials()
12
+ if not credentials:
13
+ raise ValueError("No credentials found")
14
+ return {
15
+ "Authorization": f"Bearer {credentials['api_key']}",
16
+ "Content-Type": "application/json"
17
+ }
18
+
19
+ def search(self, query: str) -> str:
20
+ """Search the web using Tavily's search API
21
+
22
+ Args:
23
+ query: The search query
24
+
25
+ Returns:
26
+ str: A summary of search results
27
+ """
28
+ self.validate()
29
+ url = f"{self.base_url}/search"
30
+ payload = {
31
+ "query": query,
32
+ "topic": "general",
33
+ "search_depth": "basic",
34
+ "max_results": 3,
35
+ "include_answer": True,
36
+ "include_raw_content": False,
37
+ "include_images": False,
38
+ "include_image_descriptions": False,
39
+ "include_domains": [],
40
+ "exclude_domains": []
41
+ }
42
+
43
+ response = self._post(url, payload)
44
+ result = response.json()
45
+
46
+ if "answer" in result:
47
+ return result["answer"]
48
+
49
+ # Fallback to combining top results if no direct answer
50
+ summaries = []
51
+ for item in result.get("results", [])[:3]:
52
+ summaries.append(f"• {item['title']}: {item['snippet']}")
53
+
54
+ return "\n".join(summaries)
55
+
56
+ def list_tools(self):
57
+ return [self.search]
@@ -0,0 +1,58 @@
1
+ import typer
2
+ from pathlib import Path
3
+
4
+ app = typer.Typer()
5
+
6
+ @app.command()
7
+ def version():
8
+ """Show the version of the CLI"""
9
+ print("agentr version 0.1.0")
10
+
11
+ @app.command()
12
+ def generate(schema_path: Path = typer.Option(..., "--schema", "-s")):
13
+ """Generate API client from OpenAPI schema"""
14
+ if not schema_path.exists():
15
+ typer.echo(f"Error: Schema file {schema_path} does not exist", err=True)
16
+ raise typer.Exit(1)
17
+ from .utils.openapi import generate_api_client, load_schema
18
+
19
+ try:
20
+ schema = load_schema(schema_path)
21
+ except Exception as e:
22
+ typer.echo(f"Error loading schema: {e}", err=True)
23
+ raise typer.Exit(1)
24
+ code = generate_api_client(schema)
25
+ print(code)
26
+
27
+ @app.command()
28
+ def run():
29
+ """Run the MCP server"""
30
+ from agentr.test import mcp
31
+ mcp.run()
32
+
33
+ @app.command()
34
+ def install(app_name: str):
35
+ """Install an app"""
36
+ import json
37
+ api_key = typer.prompt("Enter your API key", hide_input=True)
38
+ if app_name == "claude":
39
+ typer.echo(f"Installing mcp server for: {app_name}")
40
+ config_path = '~/Library/Application Support/Claude/claude_desktop_config.json'
41
+ with open(config_path, 'r') as f:
42
+ config = json.load(f)
43
+ config['mcpServers']['agentr-r'] = {
44
+ "command": "uvx",
45
+ "args": ["agentr@latest", "run"],
46
+ "env": {
47
+ "AGENTR_API_KEY": api_key
48
+ }
49
+ }
50
+ with open(config_path, 'w') as f:
51
+ json.dump(config, f, indent=4)
52
+ typer.echo("App installed successfully")
53
+ else:
54
+ typer.echo(f"App {app_name} not supported")
55
+
56
+
57
+ if __name__ == "__main__":
58
+ app()
@@ -0,0 +1,91 @@
1
+ from abc import ABC, abstractmethod
2
+ import os
3
+ from agentr.store import Store
4
+ import httpx
5
+
6
+ """
7
+ Integration defines how a Application needs to authorize.
8
+ It is responsible for authenticating application with the service provider.
9
+ Supported integrations:
10
+ - AgentR Integration
11
+ - API Key Integration
12
+ """
13
+
14
+ class Integration(ABC):
15
+ def __init__(self, name: str, store: Store = None):
16
+ self.name = name
17
+ self.store = store
18
+
19
+ @abstractmethod
20
+ def get_credentials(self):
21
+ pass
22
+
23
+ @abstractmethod
24
+ def set_credentials(self, credentials: dict):
25
+ pass
26
+
27
+ class ApiKeyIntegration(Integration):
28
+ def __init__(self, name: str, store: Store = None, **kwargs):
29
+ super().__init__(name, store, **kwargs)
30
+
31
+ def get_credentials(self):
32
+ credentials = self.store.get(self.name)
33
+ return credentials
34
+
35
+ def set_credentials(self, credentials: dict):
36
+ self.store.set(self.name, credentials)
37
+
38
+ def authorize(self):
39
+ return {"text": "Please configure the environment variable {self.name}_API_KEY"}
40
+
41
+
42
+
43
+ class NangoIntegration(Integration):
44
+ def __init__(self, user_id, integration_id):
45
+ self.integration_id = integration_id
46
+ self.user_id = user_id
47
+ self.nango_secret_key = os.getenv("NANGO_SECRET_KEY")
48
+
49
+ def _create_session_token(self):
50
+ url = "https://api.nango.dev/connect/sessions"
51
+ body = {
52
+ "end_user": {
53
+ "id": self.user_id,
54
+ },
55
+ "allowed_integrations": [self.integration_id]
56
+ }
57
+ response = httpx.post(url, headers={"Authorization": f"Bearer {self.nango_secret_key}"}, json=body)
58
+ data = response.json()
59
+ return data["data"]["token"]
60
+
61
+ def get_authorize_url(self):
62
+ session_token = self._create_session_token()
63
+ return f"https://api.nango.dev/oauth/connect/{self.integration_id}?connect_session_token={session_token}"
64
+
65
+ def get_connection_by_owner(self, user_id):
66
+ url = f"https://api.nango.dev/connection?endUserId={user_id}"
67
+ response = httpx.get(url, headers={"Authorization": f"Bearer {self.nango_secret_key}"})
68
+ if response.status_code == 200:
69
+ connections = response.json()["connections"]
70
+ for connection in connections:
71
+ if connection["provider_config_key"] == self.integration_id:
72
+ return connection["connection_id"]
73
+ return None
74
+
75
+ class AgentRIntegration(Integration):
76
+ def __init__(self, name: str, api_key: str = None, **kwargs):
77
+ super().__init__(name, **kwargs)
78
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
79
+ if not self.api_key:
80
+ raise ValueError("api_key is required")
81
+ self.base_url = "https://api.agentr.dev"
82
+ self.user_id = "default"
83
+
84
+ def get_credentials(self):
85
+ response = httpx.get(f"{self.base_url}/integrations/{self.name}/credentials", headers={"Authorization": f"Bearer {self.api_key}"})
86
+ return response.json()
87
+
88
+ def authorize(self):
89
+ response = httpx.post(f"{self.base_url}/integrations/{self.name}/authorize", headers={"Authorization": f"Bearer {self.api_key}"})
90
+ url = response.json()["url"]
91
+ return {"url": url, "text": "Please authorize the application by clicking the link {url}"}
@@ -0,0 +1,105 @@
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
+
@@ -0,0 +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):
71
+ self.redis.delete(key)
@@ -0,0 +1,38 @@
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("search", {"query": "python"})
34
+ print(result)
35
+
36
+ if __name__ == "__main__":
37
+ import asyncio
38
+ asyncio.run(test())
File without changes
@@ -1,12 +1,13 @@
1
1
  version = 1
2
2
  revision = 1
3
- requires-python = ">=3.13"
3
+ requires-python = ">=3.11"
4
4
 
5
5
  [[package]]
6
6
  name = "agentr"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  source = { editable = "." }
9
9
  dependencies = [
10
+ { name = "loguru" },
10
11
  { name = "mcp" },
11
12
  { name = "pyyaml" },
12
13
  { name = "typer" },
@@ -14,6 +15,7 @@ dependencies = [
14
15
 
15
16
  [package.metadata]
16
17
  requires-dist = [
18
+ { name = "loguru", specifier = ">=0.7.3" },
17
19
  { name = "mcp", specifier = ">=1.5.0" },
18
20
  { name = "pyyaml", specifier = ">=6.0.2" },
19
21
  { name = "typer", specifier = ">=0.15.2" },
@@ -35,6 +37,7 @@ source = { registry = "https://pypi.org/simple" }
35
37
  dependencies = [
36
38
  { name = "idna" },
37
39
  { name = "sniffio" },
40
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
38
41
  ]
39
42
  sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
40
43
  wheels = [
@@ -126,6 +129,19 @@ wheels = [
126
129
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
127
130
  ]
128
131
 
132
+ [[package]]
133
+ name = "loguru"
134
+ version = "0.7.3"
135
+ source = { registry = "https://pypi.org/simple" }
136
+ dependencies = [
137
+ { name = "colorama", marker = "sys_platform == 'win32'" },
138
+ { name = "win32-setctime", marker = "sys_platform == 'win32'" },
139
+ ]
140
+ sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 }
141
+ wheels = [
142
+ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 },
143
+ ]
144
+
129
145
  [[package]]
130
146
  name = "markdown-it-py"
131
147
  version = "3.0.0"
@@ -189,6 +205,34 @@ dependencies = [
189
205
  ]
190
206
  sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
191
207
  wheels = [
208
+ { url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 },
209
+ { url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 },
210
+ { url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 },
211
+ { url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 },
212
+ { url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 },
213
+ { url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 },
214
+ { url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 },
215
+ { url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 },
216
+ { url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 },
217
+ { url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 },
218
+ { url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 },
219
+ { url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 },
220
+ { url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 },
221
+ { url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 },
222
+ { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
223
+ { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
224
+ { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
225
+ { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
226
+ { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
227
+ { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
228
+ { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
229
+ { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
230
+ { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
231
+ { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
232
+ { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
233
+ { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
234
+ { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
235
+ { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
192
236
  { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
193
237
  { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
194
238
  { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
@@ -242,6 +286,24 @@ version = "6.0.2"
242
286
  source = { registry = "https://pypi.org/simple" }
243
287
  sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
244
288
  wheels = [
289
+ { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
290
+ { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
291
+ { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
292
+ { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
293
+ { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
294
+ { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
295
+ { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
296
+ { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
297
+ { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
298
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
299
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
300
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
301
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
302
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
303
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
304
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
305
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
306
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
245
307
  { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
246
308
  { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
247
309
  { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
@@ -345,3 +407,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec
345
407
  wheels = [
346
408
  { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
347
409
  ]
410
+
411
+ [[package]]
412
+ name = "win32-setctime"
413
+ version = "1.2.0"
414
+ source = { registry = "https://pypi.org/simple" }
415
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 }
416
+ wheels = [
417
+ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 },
418
+ ]
@@ -1,35 +0,0 @@
1
- import typer
2
- import yaml
3
- from pathlib import Path
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.mcp import mcp
32
- mcp.run()
33
-
34
- if __name__ == "__main__":
35
- app()
@@ -1,9 +0,0 @@
1
- from agentr.server import TestServer
2
- from agentr.store import MemoryStore
3
-
4
- store = MemoryStore()
5
-
6
- mcp = TestServer(name="Test Server", description="Test Server", store=store)
7
-
8
- if __name__ == "__main__":
9
- mcp.run()
@@ -1,33 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from mcp.server.fastmcp import FastMCP
3
-
4
- from agentr.applications.zenquotes.app import ZenQuoteApp
5
- from agentr.store import Store
6
-
7
- class Server(FastMCP, ABC):
8
- """
9
- Server is responsible for managing the applications and the store
10
- It also acts as a router for the applications, and exposed to the client
11
-
12
- """
13
- def __init__(self, name: str, description: str, store: Store, **kwargs):
14
- self.store = store
15
- super().__init__(name, description, **kwargs)
16
-
17
-
18
- class TestServer(Server):
19
- """
20
- Test server for development purposes
21
- """
22
- def __init__(self, **kwargs):
23
- super().__init__(**kwargs)
24
- self.apps_list = ["zenquotes"]
25
- self.__load_apps()
26
-
27
- def __load_apps(self):
28
- self.apps = []
29
- for app in self.apps_list:
30
- if app == "zenquotes":
31
- app = ZenQuoteApp(store=self.store)
32
- tools = app.list_tools()
33
- self.add_tool(tools[0])
@@ -1,35 +0,0 @@
1
-
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
-
File without changes
File without changes
File without changes
File without changes