universal-mcp 0.1.24rc6__py3-none-any.whl → 0.1.24rc7__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.
- universal_mcp/agentr/README.md +43 -34
- universal_mcp/agentr/agentr.py +7 -0
- universal_mcp/agentr/client.py +96 -42
- universal_mcp/agentr/registry.py +21 -25
- universal_mcp/agents/__init__.py +4 -4
- universal_mcp/agents/auto.py +8 -8
- universal_mcp/agents/autoagent/__init__.py +35 -0
- universal_mcp/agents/autoagent/__main__.py +21 -0
- universal_mcp/agents/autoagent/context.py +25 -0
- universal_mcp/agents/autoagent/graph.py +119 -0
- universal_mcp/agents/autoagent/prompts.py +5 -0
- universal_mcp/agents/autoagent/state.py +27 -0
- universal_mcp/agents/autoagent/studio.py +25 -0
- universal_mcp/agents/autoagent/utils.py +13 -0
- universal_mcp/agents/base.py +3 -2
- universal_mcp/agents/codeact/test.py +2 -2
- universal_mcp/agents/hil.py +2 -2
- universal_mcp/agents/llm.py +21 -3
- universal_mcp/agents/react.py +3 -1
- universal_mcp/agents/simple.py +3 -3
- universal_mcp/tools/registry.py +13 -3
- {universal_mcp-0.1.24rc6.dist-info → universal_mcp-0.1.24rc7.dist-info}/METADATA +4 -1
- {universal_mcp-0.1.24rc6.dist-info → universal_mcp-0.1.24rc7.dist-info}/RECORD +26 -18
- {universal_mcp-0.1.24rc6.dist-info → universal_mcp-0.1.24rc7.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.24rc6.dist-info → universal_mcp-0.1.24rc7.dist-info}/entry_points.txt +0 -0
- {universal_mcp-0.1.24rc6.dist-info → universal_mcp-0.1.24rc7.dist-info}/licenses/LICENSE +0 -0
universal_mcp/agentr/README.md
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
# AgentR Python SDK
|
2
2
|
|
3
3
|
The official Python SDK for the AgentR platform, a component of the Universal MCP framework.
|
4
|
-
Currently in beta, breaking changes are expected.
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
*Currently in beta, breaking changes are expected.*
|
6
|
+
|
7
|
+
The AgentR Python SDK provides convenient access to the AgentR REST API from any Python 3.10+ application, allowing for dynamic loading and management of tools and integrations.
|
8
8
|
|
9
9
|
## Installation
|
10
|
+
|
10
11
|
```bash
|
11
12
|
pip install universal-mcp
|
12
13
|
```
|
13
14
|
|
14
15
|
## Usage
|
16
|
+
|
15
17
|
The AgentR platform is designed to seamlessly integrate a wide array of tools into your agentic applications. The primary entry point for this is the `Agentr` class, which provides a high-level interface for loading and listing tools.
|
16
18
|
|
17
19
|
### High-Level Client (`Agentr`)
|
20
|
+
|
18
21
|
This is the recommended way to get started. It abstracts away the details of the registry and tool management.
|
19
22
|
|
20
23
|
```python
|
@@ -29,7 +32,7 @@ agentr = Agentr(
|
|
29
32
|
)
|
30
33
|
|
31
34
|
# Load specific tools from the AgentR server into the tool manager
|
32
|
-
agentr.load_tools(["
|
35
|
+
agentr.load_tools(["reddit__search_subreddits", "google-drive__list_files"])
|
33
36
|
|
34
37
|
# List the tools that are now loaded and ready to be used
|
35
38
|
# You can specify a format compatible with your LLM (e.g., OPENAI)
|
@@ -41,10 +44,10 @@ print(tools)
|
|
41
44
|
|
42
45
|
For more granular control over the AgentR platform, you can use the lower-level components directly.
|
43
46
|
|
44
|
-
|
45
|
-
|
47
|
+
#### AgentrClient
|
48
|
+
|
49
|
+
The `AgentrClient` provides direct, one-to-one access to the AgentR REST API endpoints. The following examples have been updated to reflect the latest API structure.
|
46
50
|
|
47
|
-
#### Methods
|
48
51
|
```python
|
49
52
|
import os
|
50
53
|
from universal_mcp.agentr import AgentrClient
|
@@ -55,39 +58,43 @@ client = AgentrClient(
|
|
55
58
|
api_key=os.environ.get("AGENTR_API_KEY")
|
56
59
|
)
|
57
60
|
|
58
|
-
# Fetch
|
59
|
-
apps = client.
|
60
|
-
print(apps)
|
61
|
+
# Fetch a list of available applications from the AgentR server
|
62
|
+
apps = client.list_apps()
|
63
|
+
print("Available Apps:", apps)
|
61
64
|
|
62
|
-
# Get credentials for a specific
|
63
|
-
# This will raise a NotAuthorizedError if the user needs to authenticate
|
65
|
+
# Get credentials for a specific app by its ID (e.g., 'reddit')
|
66
|
+
# This will raise a NotAuthorizedError if the user needs to authenticate.
|
64
67
|
try:
|
65
|
-
credentials = client.get_credentials("reddit")
|
68
|
+
credentials = client.get_credentials(app_id="reddit")
|
66
69
|
print("Reddit credentials found.")
|
67
70
|
except NotAuthorizedError as e:
|
68
|
-
print(e)
|
69
|
-
|
70
|
-
# Example of fetching a single app and its actions
|
71
|
-
if apps:
|
72
|
-
app_id = apps[0].id # Assuming AppConfig has an 'id' attribute
|
71
|
+
print(e) # "Please ask the user to visit the following url to authorize..."
|
73
72
|
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
# List all available tools globally
|
74
|
+
all_tools = client.list_tools()
|
75
|
+
print("All Available Tools:", all_tools)
|
77
76
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
77
|
+
# Example of fetching a single app and a single tool
|
78
|
+
if apps:
|
79
|
+
# Note: We access dictionary keys, not attributes
|
80
|
+
app_id = apps[0]['id']
|
81
|
+
|
82
|
+
# Fetch a single app's details
|
83
|
+
app_details = client.get_app(app_id)
|
84
|
+
print(f"Fetched details for app '{app_id}':", app_details)
|
85
|
+
|
86
|
+
if all_tools:
|
87
|
+
tool_id = all_tools[0]['id']
|
88
|
+
|
89
|
+
# Fetch a single tool's details
|
90
|
+
tool_details = client.get_tool(tool_id)
|
91
|
+
print(f"Fetched details for tool '{tool_id}':", tool_details)
|
85
92
|
```
|
86
93
|
|
87
|
-
|
94
|
+
#### AgentrIntegration
|
95
|
+
|
88
96
|
This class handles the authentication and authorization flow for a single integration (e.g., "reddit"). It's used under the hood by applications to acquire credentials.
|
89
97
|
|
90
|
-
#### Methods
|
91
98
|
```python
|
92
99
|
from universal_mcp.agentr import AgentrIntegration, AgentrClient
|
93
100
|
from universal_mcp.exceptions import NotAuthorizedError
|
@@ -114,10 +121,10 @@ except NotAuthorizedError:
|
|
114
121
|
print("Still not authorized.")
|
115
122
|
```
|
116
123
|
|
117
|
-
|
124
|
+
#### AgentrRegistry
|
125
|
+
|
118
126
|
The registry is responsible for discovering which tools are available on the AgentR platform.
|
119
127
|
|
120
|
-
#### Methods
|
121
128
|
```python
|
122
129
|
import asyncio
|
123
130
|
from universal_mcp.agentr import AgentrRegistry, AgentrClient
|
@@ -148,7 +155,8 @@ if __name__ == "__main__":
|
|
148
155
|
asyncio.run(main())
|
149
156
|
```
|
150
157
|
|
151
|
-
|
158
|
+
#### AgentrServer
|
159
|
+
|
152
160
|
For server-side deployments, `AgentrServer` can be used to load all configured applications and their tools from an AgentR instance on startup.
|
153
161
|
|
154
162
|
```python
|
@@ -170,6 +178,7 @@ print(tool_manager.list_tools())
|
|
170
178
|
```
|
171
179
|
|
172
180
|
## Executing Tools
|
181
|
+
|
173
182
|
Once tools are loaded, you can execute them using the `call_tool` method on the `ToolManager` instance, which is available via `agentr.manager`.
|
174
183
|
|
175
184
|
```python
|
@@ -182,7 +191,7 @@ async def main():
|
|
182
191
|
agentr = Agentr(api_key=os.environ.get("AGENTR_API_KEY"))
|
183
192
|
|
184
193
|
# 2. Load the tool(s) you want to use
|
185
|
-
tool_name = "
|
194
|
+
tool_name = "reddit__search_subreddits"
|
186
195
|
agentr.load_tools([tool_name])
|
187
196
|
|
188
197
|
# 3. Execute the tool using the tool manager
|
universal_mcp/agentr/agentr.py
CHANGED
@@ -28,3 +28,10 @@ class Agentr:
|
|
28
28
|
|
29
29
|
def list_tools(self, format: ToolFormat | None = None) -> list[Tool]:
|
30
30
|
return self.manager.list_tools(format=format or self.format)
|
31
|
+
|
32
|
+
def search_tools(
|
33
|
+
self,
|
34
|
+
query: str,
|
35
|
+
) -> list[str]:
|
36
|
+
"""Retrieve a tool to use, given a search query."""
|
37
|
+
return self.registry.search_tools(query)
|
universal_mcp/agentr/client.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import os
|
2
|
+
from typing import Any
|
2
3
|
|
3
4
|
import httpx
|
4
5
|
from loguru import logger
|
5
6
|
|
6
|
-
from universal_mcp.config import AppConfig
|
7
7
|
from universal_mcp.exceptions import NotAuthorizedError
|
8
8
|
|
9
9
|
|
@@ -14,110 +14,164 @@ class AgentrClient:
|
|
14
14
|
including authentication, authorization, and credential management.
|
15
15
|
|
16
16
|
Args:
|
17
|
-
api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var
|
18
|
-
base_url (str, optional): Base URL for AgentR API. Defaults to https://api.agentr.dev
|
17
|
+
api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var.
|
18
|
+
base_url (str, optional): Base URL for AgentR API. Defaults to https://api.agentr.dev.
|
19
19
|
"""
|
20
20
|
|
21
21
|
def __init__(self, api_key: str | None = None, base_url: str | None = None):
|
22
22
|
base_url = base_url or os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")
|
23
|
-
self.base_url = base_url.rstrip(
|
23
|
+
self.base_url = f"{base_url.rstrip('/')}/v1"
|
24
24
|
self.api_key = api_key or os.getenv("AGENTR_API_KEY")
|
25
25
|
if not self.api_key:
|
26
26
|
raise ValueError("No API key provided and AGENTR_API_KEY not found in environment variables")
|
27
27
|
self.client = httpx.Client(
|
28
|
-
base_url=self.base_url,
|
28
|
+
base_url=self.base_url,
|
29
|
+
headers={"X-API-KEY": self.api_key},
|
30
|
+
timeout=30,
|
31
|
+
follow_redirects=True,
|
32
|
+
verify=False,
|
29
33
|
)
|
30
34
|
|
31
|
-
def get_credentials(self,
|
35
|
+
def get_credentials(self, app_id: str) -> dict[str, Any]:
|
32
36
|
"""Get credentials for an integration from the AgentR API.
|
33
37
|
|
34
38
|
Args:
|
35
|
-
|
39
|
+
app_id (str): The ID of the app (e.g., 'asana', 'google-drive').
|
36
40
|
|
37
41
|
Returns:
|
38
|
-
dict: Credentials data from API response
|
42
|
+
dict: Credentials data from API response.
|
39
43
|
|
40
44
|
Raises:
|
41
|
-
NotAuthorizedError: If credentials are not found (404 response)
|
42
|
-
HTTPError: For other API errors
|
45
|
+
NotAuthorizedError: If credentials are not found (404 response).
|
46
|
+
HTTPError: For other API errors.
|
43
47
|
"""
|
44
48
|
response = self.client.get(
|
45
|
-
|
49
|
+
"/credentials/",
|
50
|
+
params={"app_id": app_id},
|
46
51
|
)
|
47
52
|
if response.status_code == 404:
|
48
|
-
logger.warning(f"No credentials found for {
|
49
|
-
|
50
|
-
raise NotAuthorizedError(
|
53
|
+
logger.warning(f"No credentials found for app '{app_id}'. Requesting authorization...")
|
54
|
+
action_url = self.get_authorization_url(app_id)
|
55
|
+
raise NotAuthorizedError(action_url)
|
51
56
|
response.raise_for_status()
|
52
57
|
return response.json()
|
53
58
|
|
54
|
-
def get_authorization_url(self,
|
55
|
-
"""Get authorization URL
|
59
|
+
def get_authorization_url(self, app_id: str) -> str:
|
60
|
+
"""Get the authorization URL to connect an app.
|
56
61
|
|
57
62
|
Args:
|
58
|
-
|
63
|
+
app_id (str): The ID of the app to authorize.
|
59
64
|
|
60
65
|
Returns:
|
61
|
-
str:
|
66
|
+
str: A message containing the authorization URL.
|
62
67
|
|
63
68
|
Raises:
|
64
|
-
HTTPError: If API request fails
|
69
|
+
HTTPError: If the API request fails.
|
65
70
|
"""
|
66
|
-
response = self.client.
|
71
|
+
response = self.client.post("/connections/authorize", json={"app_id": app_id})
|
67
72
|
response.raise_for_status()
|
68
|
-
url = response.json()
|
73
|
+
url = response.json().get("authorize_url")
|
69
74
|
return f"Please ask the user to visit the following url to authorize the application: {url}. Render the url in proper markdown format with a clickable link."
|
70
75
|
|
71
|
-
def
|
76
|
+
def list_all_apps(self) -> list[dict[str, Any]]:
|
72
77
|
"""Fetch available apps from AgentR API.
|
73
78
|
|
74
79
|
Returns:
|
75
|
-
List of application
|
80
|
+
List[Dict[str, Any]]: A list of application data dictionaries.
|
76
81
|
|
77
82
|
Raises:
|
78
|
-
httpx.HTTPError: If API request fails
|
83
|
+
httpx.HTTPError: If the API request fails.
|
79
84
|
"""
|
80
|
-
response = self.client.get("/
|
85
|
+
response = self.client.get("/apps/")
|
86
|
+
response.raise_for_status()
|
87
|
+
return response.json().get("items", [])
|
88
|
+
|
89
|
+
def list_my_apps(self) -> list[dict[str, Any]]:
|
90
|
+
"""Fetch user apps from AgentR API.
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
List[Dict[str, Any]]: A list of user app data dictionaries.
|
94
|
+
"""
|
95
|
+
response = self.client.get("/apps/me/")
|
96
|
+
response.raise_for_status()
|
97
|
+
return response.json().get("items", [])
|
98
|
+
|
99
|
+
def list_my_connections(self) -> list[dict[str, Any]]:
|
100
|
+
"""Fetch user connections from AgentR API.
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
List[Dict[str, Any]]: A list of user connection data dictionaries.
|
104
|
+
"""
|
105
|
+
response = self.client.get("/connections/")
|
81
106
|
response.raise_for_status()
|
82
|
-
|
83
|
-
return [AppConfig.model_validate(app) for app in data]
|
107
|
+
return response.json().get("items", [])
|
84
108
|
|
85
|
-
def
|
109
|
+
def get_app_details(self, app_id: str) -> dict[str, Any]:
|
86
110
|
"""Fetch a specific app from AgentR API.
|
87
111
|
|
88
112
|
Args:
|
89
|
-
app_id (str): ID of the app to fetch
|
113
|
+
app_id (str): ID of the app to fetch.
|
90
114
|
|
91
115
|
Returns:
|
92
|
-
dict: App configuration data
|
116
|
+
dict: App configuration data.
|
93
117
|
|
94
118
|
Raises:
|
95
|
-
httpx.HTTPError: If API request fails
|
119
|
+
httpx.HTTPError: If the API request fails.
|
96
120
|
"""
|
97
|
-
response = self.client.get(f"/apps/{app_id}
|
121
|
+
response = self.client.get(f"/apps/{app_id}")
|
98
122
|
response.raise_for_status()
|
99
123
|
return response.json()
|
100
124
|
|
101
|
-
def
|
102
|
-
"""List all
|
125
|
+
def list_all_tools(self) -> list[dict[str, Any]]:
|
126
|
+
"""List all available tools from the AgentR API.
|
127
|
+
|
128
|
+
Note: In the backend, tools are globally listed and not tied to a
|
129
|
+
specific app at this endpoint.
|
103
130
|
|
104
131
|
Returns:
|
105
|
-
List of
|
132
|
+
List[Dict[str, Any]]: A list of tool configurations.
|
106
133
|
"""
|
107
|
-
response = self.client.get("/
|
134
|
+
response = self.client.get("/tools/")
|
135
|
+
response.raise_for_status()
|
136
|
+
return response.json().get("items", [])
|
137
|
+
|
138
|
+
def get_tool_details(self, tool_id: str) -> dict[str, Any]:
|
139
|
+
"""Fetch a specific tool configuration from the AgentR API.
|
140
|
+
|
141
|
+
Args:
|
142
|
+
tool_id (str): ID of the tool to fetch.
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
dict: Tool configuration data.
|
146
|
+
|
147
|
+
Raises:
|
148
|
+
httpx.HTTPError: If the API request fails.
|
149
|
+
"""
|
150
|
+
response = self.client.get(f"/tools/{tool_id}")
|
108
151
|
response.raise_for_status()
|
109
152
|
return response.json()
|
110
153
|
|
111
|
-
def
|
112
|
-
"""
|
154
|
+
def search_all_apps(self, query: str, limit: int = 2) -> list[dict[str, Any]]:
|
155
|
+
"""Search for apps from the AgentR API.
|
113
156
|
|
114
157
|
Args:
|
115
|
-
|
158
|
+
query (str): The query to search for.
|
159
|
+
limit (int, optional): The number of apps to return. Defaults to 2.
|
116
160
|
|
117
161
|
Returns:
|
118
|
-
List of
|
162
|
+
List[Dict[str, Any]]: A list of app data dictionaries.
|
119
163
|
"""
|
164
|
+
response = self.client.get("/apps/", params={"search": query, "limit": limit})
|
165
|
+
response.raise_for_status()
|
166
|
+
return response.json().get("items", [])
|
167
|
+
|
168
|
+
def search_all_tools(self, query: str, limit: int = 2) -> list[dict[str, Any]]:
|
169
|
+
"""Search for tools from the AgentR API.
|
120
170
|
|
121
|
-
|
171
|
+
Args:
|
172
|
+
query (str): The query to search for.
|
173
|
+
limit (int, optional): The number of tools to return. Defaults to 2.
|
174
|
+
"""
|
175
|
+
response = self.client.get("/tools/", params={"search": query, "limit": limit})
|
122
176
|
response.raise_for_status()
|
123
|
-
return response.json()
|
177
|
+
return response.json().get("items", [])
|
universal_mcp/agentr/registry.py
CHANGED
@@ -17,26 +17,22 @@ class AgentrRegistry(ToolRegistry):
|
|
17
17
|
self.client = client or AgentrClient(**kwargs)
|
18
18
|
logger.debug("AgentrRegistry initialized successfully")
|
19
19
|
|
20
|
-
|
20
|
+
def list_apps(self) -> list[dict[str, str]]:
|
21
21
|
"""Get list of available apps from AgentR.
|
22
22
|
|
23
23
|
Returns:
|
24
24
|
List of app dictionaries with id, name, description, and available fields
|
25
25
|
"""
|
26
|
+
if self.client is None:
|
27
|
+
raise ValueError("Client is not initialized")
|
26
28
|
try:
|
27
|
-
all_apps =
|
28
|
-
|
29
|
-
{"id": app["id"], "name": app["name"], "description": app.get("description", "")}
|
30
|
-
for app in all_apps
|
31
|
-
if app.get("available", False)
|
32
|
-
]
|
33
|
-
logger.info(f"Found {len(available_apps)} available apps from AgentR")
|
34
|
-
return available_apps
|
29
|
+
all_apps = self.client.list_all_apps()
|
30
|
+
return all_apps
|
35
31
|
except Exception as e:
|
36
32
|
logger.error(f"Error fetching apps from AgentR: {e}")
|
37
33
|
return []
|
38
34
|
|
39
|
-
|
35
|
+
def get_app_details(self, app_id: str) -> dict[str, str]:
|
40
36
|
"""Get detailed information about a specific app from AgentR.
|
41
37
|
|
42
38
|
Args:
|
@@ -46,23 +42,11 @@ class AgentrRegistry(ToolRegistry):
|
|
46
42
|
Dictionary containing app details
|
47
43
|
"""
|
48
44
|
try:
|
49
|
-
app_info =
|
50
|
-
return
|
51
|
-
"id": app_info.get("id"),
|
52
|
-
"name": app_info.get("name"),
|
53
|
-
"description": app_info.get("description"),
|
54
|
-
"category": app_info.get("category"),
|
55
|
-
"available": app_info.get("available", True),
|
56
|
-
}
|
45
|
+
app_info = self.client.get_app_details(app_id)
|
46
|
+
return app_info
|
57
47
|
except Exception as e:
|
58
48
|
logger.error(f"Error getting details for app {app_id}: {e}")
|
59
|
-
return {
|
60
|
-
"id": app_id,
|
61
|
-
"name": app_id,
|
62
|
-
"description": "Error loading details",
|
63
|
-
"category": "Unknown",
|
64
|
-
"available": True,
|
65
|
-
}
|
49
|
+
return {}
|
66
50
|
|
67
51
|
def load_tools(self, tools: list[str] | None, tool_manager: ToolManager) -> None:
|
68
52
|
"""Load tools from AgentR and register them as tools.
|
@@ -89,3 +73,15 @@ class AgentrRegistry(ToolRegistry):
|
|
89
73
|
app_instance = app(integration=integration)
|
90
74
|
tool_manager.register_tools_from_app(app_instance, tool_names=tool_names)
|
91
75
|
return
|
76
|
+
|
77
|
+
def search_tools(self, query: str, limit: int = 20) -> list[str]:
|
78
|
+
"""Search for tools in AgentR.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
query: The query to search for
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
List of tool names
|
85
|
+
"""
|
86
|
+
return self.client.search_all_tools(query, limit)
|
87
|
+
|
universal_mcp/agents/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
from .auto import AutoAgent
|
2
|
-
from .base import BaseAgent
|
3
|
-
from .react import ReactAgent
|
4
|
-
from .simple import SimpleAgent
|
1
|
+
from universal_mcp.agents.auto import AutoAgent
|
2
|
+
from universal_mcp.agents.base import BaseAgent
|
3
|
+
from universal_mcp.agents.react import ReactAgent
|
4
|
+
from universal_mcp.agents.simple import SimpleAgent
|
5
5
|
|
6
6
|
__all__ = ["BaseAgent", "ReactAgent", "SimpleAgent", "AutoAgent"]
|
universal_mcp/agents/auto.py
CHANGED
@@ -16,8 +16,8 @@ from universal_mcp.tools import ToolManager
|
|
16
16
|
from universal_mcp.tools.adapters import ToolFormat
|
17
17
|
from universal_mcp.tools.registry import ToolRegistry
|
18
18
|
|
19
|
-
from .base import BaseAgent
|
20
|
-
from .llm import
|
19
|
+
from universal_mcp.agents.base import BaseAgent
|
20
|
+
from universal_mcp.agents.llm import load_chat_model
|
21
21
|
|
22
22
|
# Auto Agent
|
23
23
|
# Working
|
@@ -61,9 +61,9 @@ class AutoAgent(BaseAgent):
|
|
61
61
|
def __init__(self, name: str, instructions: str, model: str, app_registry: ToolRegistry):
|
62
62
|
super().__init__(name, instructions, model)
|
63
63
|
self.app_registry = app_registry
|
64
|
-
self.llm_tools =
|
65
|
-
self.llm_choice =
|
66
|
-
self.llm_quiet =
|
64
|
+
self.llm_tools = load_chat_model(model, tags=["tools"])
|
65
|
+
self.llm_choice = load_chat_model(model, tags=["choice"])
|
66
|
+
self.llm_quiet = load_chat_model(model, tags=["quiet"])
|
67
67
|
self.tool_manager = ToolManager()
|
68
68
|
|
69
69
|
self.task_analysis_prompt = """You are a task analysis expert. Given a task description and available apps, determine:
|
@@ -522,7 +522,7 @@ Be friendly and concise, but list each set of apps clearly. Do not return any ot
|
|
522
522
|
return result
|
523
523
|
|
524
524
|
# Get all available apps from platform manager
|
525
|
-
available_apps =
|
525
|
+
available_apps = self.app_registry.list_apps()
|
526
526
|
|
527
527
|
logger.info(f"Found {len(available_apps)} available apps")
|
528
528
|
|
@@ -563,10 +563,10 @@ if __name__ == "__main__":
|
|
563
563
|
|
564
564
|
# Create platform manager
|
565
565
|
app_registry = AgentrRegistry(api_key=agentr_api_key)
|
566
|
-
want_instructions = input("Do you want to add a system prompt/instructions? (Y/N)")
|
566
|
+
want_instructions = input("Do you want to add a system prompt/instructions? (Y/N): ")
|
567
567
|
instructions = "" if want_instructions.upper() == "N" else input("Enter your instructions/system prompt: ")
|
568
568
|
|
569
|
-
agent = AutoAgent("Auto Agent", instructions, "gpt-4.1", app_registry=app_registry)
|
569
|
+
agent = AutoAgent("Auto Agent", instructions, "azure/gpt-4.1", app_registry=app_registry)
|
570
570
|
|
571
571
|
print("AutoAgent created successfully!")
|
572
572
|
print(f"Agent name: {agent.name}")
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from universal_mcp.agentr.registry import AgentrRegistry
|
2
|
+
from universal_mcp.agents.base import BaseAgent
|
3
|
+
from universal_mcp.tools.manager import ToolManager
|
4
|
+
from universal_mcp.tools.registry import ToolRegistry
|
5
|
+
|
6
|
+
from universal_mcp.agents.autoagent.graph import create_agent
|
7
|
+
|
8
|
+
|
9
|
+
class AutoAgent(BaseAgent):
|
10
|
+
def __init__(
|
11
|
+
self,
|
12
|
+
name: str,
|
13
|
+
instructions: str,
|
14
|
+
model: str,
|
15
|
+
tool_registry: ToolRegistry | None = None,
|
16
|
+
tool_manager: ToolManager | None = None,
|
17
|
+
):
|
18
|
+
super().__init__(name, instructions, model, tool_registry)
|
19
|
+
self.tool_registry = tool_registry or AgentrRegistry()
|
20
|
+
self.tool_manager = tool_manager or ToolManager()
|
21
|
+
self.model = model
|
22
|
+
self.name = name
|
23
|
+
self.instructions = instructions
|
24
|
+
self._graph = self._build_graph()
|
25
|
+
|
26
|
+
def _build_graph(self):
|
27
|
+
builder = create_agent(self.tool_registry, self.tool_manager, self.instructions)
|
28
|
+
return builder.compile()
|
29
|
+
|
30
|
+
@property
|
31
|
+
def graph(self):
|
32
|
+
return self._graph
|
33
|
+
|
34
|
+
|
35
|
+
__all__ = ["AutoAgent"]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from universal_mcp.agentr.registry import AgentrRegistry
|
4
|
+
from universal_mcp.agents.autoagent import AutoAgent
|
5
|
+
|
6
|
+
|
7
|
+
async def main():
|
8
|
+
agent = AutoAgent(
|
9
|
+
name="autoagent",
|
10
|
+
instructions="You are a helpful assistant that can use tools to help the user.",
|
11
|
+
model="azure/gpt-4o",
|
12
|
+
tool_registry=AgentrRegistry(),
|
13
|
+
)
|
14
|
+
result = await agent.run(
|
15
|
+
user_input="Send an email to Manoj from my google mail account, manoj@agentr.dev, with the subject 'Hello from auto agent' and the body 'testing'"
|
16
|
+
)
|
17
|
+
print(result)
|
18
|
+
|
19
|
+
|
20
|
+
if __name__ == "__main__":
|
21
|
+
asyncio.run(main())
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import Annotated
|
3
|
+
|
4
|
+
from universal_mcp.agents.autoagent.prompts import SYSTEM_PROMPT
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass(kw_only=True)
|
8
|
+
class Context:
|
9
|
+
"""The context for the agent."""
|
10
|
+
|
11
|
+
system_prompt: str = field(
|
12
|
+
default=SYSTEM_PROMPT,
|
13
|
+
metadata={
|
14
|
+
"description": "The system prompt to use for the agent's interactions. "
|
15
|
+
"This prompt sets the context and behavior for the agent."
|
16
|
+
},
|
17
|
+
)
|
18
|
+
|
19
|
+
model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
|
20
|
+
default="anthropic/claude-4-sonnet-20250514",
|
21
|
+
metadata={
|
22
|
+
"description": "The name of the language model to use for the agent's main interactions. "
|
23
|
+
"Should be in the form: provider/model-name."
|
24
|
+
},
|
25
|
+
)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import json
|
2
|
+
from datetime import UTC, datetime
|
3
|
+
from typing import cast
|
4
|
+
|
5
|
+
from langchain_core.messages import AIMessage, ToolMessage
|
6
|
+
from langchain_core.tools import tool
|
7
|
+
from langgraph.graph import END, START, StateGraph
|
8
|
+
from langgraph.runtime import Runtime
|
9
|
+
|
10
|
+
from universal_mcp.agents.llm import load_chat_model
|
11
|
+
from universal_mcp.tools.manager import ToolManager
|
12
|
+
from universal_mcp.tools.registry import ToolRegistry
|
13
|
+
from universal_mcp.types import ToolFormat
|
14
|
+
|
15
|
+
from universal_mcp.agents.autoagent.context import Context
|
16
|
+
from universal_mcp.agents.autoagent.prompts import SYSTEM_PROMPT
|
17
|
+
from universal_mcp.agents.autoagent.state import State
|
18
|
+
|
19
|
+
|
20
|
+
def create_agent(tool_registry: ToolRegistry, tool_manager: ToolManager, instructions: str = ""):
|
21
|
+
@tool()
|
22
|
+
def retrieve_tools(query: str) -> list[str]:
|
23
|
+
"""Retrieve tools using a search query. Use multiple times if you require tools for different tasks."""
|
24
|
+
tools = tool_registry.search_tools(query)
|
25
|
+
my_connections = tool_registry.client.list_my_connections()
|
26
|
+
connected_apps = set(connection["app_id"] for connection in my_connections)
|
27
|
+
filtered_tools = [tool for tool in tools if tool["app_id"] in connected_apps]
|
28
|
+
if len(filtered_tools) == 0:
|
29
|
+
return tools
|
30
|
+
return filtered_tools
|
31
|
+
|
32
|
+
@tool()
|
33
|
+
def ask_user(question: str) -> str:
|
34
|
+
"""Ask the user a question. Use this tool to ask the user for any missing information for performing a task, or when you have multiple apps to choose from for performing a task."""
|
35
|
+
full_question = question
|
36
|
+
return f"ASKING_USER: {full_question}"
|
37
|
+
|
38
|
+
def call_model(
|
39
|
+
state: State,
|
40
|
+
runtime: Runtime[Context],
|
41
|
+
):
|
42
|
+
system_prompt = runtime.context.system_prompt if runtime.context.system_prompt else SYSTEM_PROMPT
|
43
|
+
system_prompt = system_prompt.format(system_time=datetime.now(tz=UTC).isoformat())
|
44
|
+
|
45
|
+
messages = [{"role": "system", "content": system_prompt + "\n" + instructions}, *state["messages"]]
|
46
|
+
model = load_chat_model(runtime.context.model)
|
47
|
+
# Load tools from tool registry
|
48
|
+
tool_registry.load_tools(tools=state["selected_tool_ids"], tool_manager=tool_manager)
|
49
|
+
loaded_tools = tool_manager.list_tools(format=ToolFormat.LANGCHAIN)
|
50
|
+
model_with_tools = model.bind_tools([retrieve_tools, ask_user, *loaded_tools], tool_choice="auto")
|
51
|
+
response = cast(AIMessage, model_with_tools.invoke(messages))
|
52
|
+
return {"messages": [response]}
|
53
|
+
|
54
|
+
# Define the conditional edge that determines whether to continue or not
|
55
|
+
def should_continue(state: State):
|
56
|
+
messages = state["messages"]
|
57
|
+
last_message = messages[-1]
|
58
|
+
# If there is no function call, then we finish
|
59
|
+
if not last_message.tool_calls:
|
60
|
+
return END
|
61
|
+
# Otherwise if there is, we continue
|
62
|
+
else:
|
63
|
+
return "tools"
|
64
|
+
|
65
|
+
def tool_router(state: State):
|
66
|
+
last_message = state["messages"][-1]
|
67
|
+
if isinstance(last_message, ToolMessage):
|
68
|
+
return "agent"
|
69
|
+
else:
|
70
|
+
return END
|
71
|
+
|
72
|
+
|
73
|
+
async def tool_node(state: State):
|
74
|
+
outputs = []
|
75
|
+
tool_ids = state["selected_tool_ids"]
|
76
|
+
for tool_call in state["messages"][-1].tool_calls:
|
77
|
+
if tool_call["name"] == retrieve_tools.name:
|
78
|
+
tool_result = retrieve_tools.invoke(tool_call["args"])
|
79
|
+
tool_ids = [tool["id"] for tool in tool_result]
|
80
|
+
outputs.append(
|
81
|
+
ToolMessage(
|
82
|
+
content=json.dumps(tool_result),
|
83
|
+
name=tool_call["name"],
|
84
|
+
tool_call_id=tool_call["id"],
|
85
|
+
)
|
86
|
+
)
|
87
|
+
elif tool_call["name"] == ask_user.name:
|
88
|
+
outputs.append(
|
89
|
+
ToolMessage(
|
90
|
+
content=json.dumps("The user has been asked the question, and the run will wait for the user's response."),
|
91
|
+
name=tool_call["name"],
|
92
|
+
tool_call_id=tool_call["id"],
|
93
|
+
)
|
94
|
+
)
|
95
|
+
ai_message = AIMessage(content=tool_call["args"]["question"])
|
96
|
+
outputs.append(ai_message)
|
97
|
+
else:
|
98
|
+
tool_manager.clear_tools()
|
99
|
+
tool_registry.load_tools([tool_call["name"]], tool_manager=tool_manager)
|
100
|
+
tool_result = await tool_manager.call_tool(tool_call["name"], tool_call["args"])
|
101
|
+
outputs.append(
|
102
|
+
ToolMessage(
|
103
|
+
content=json.dumps(tool_result),
|
104
|
+
name=tool_call["name"],
|
105
|
+
tool_call_id=tool_call["id"],
|
106
|
+
)
|
107
|
+
)
|
108
|
+
return {"messages": outputs, "selected_tool_ids": tool_ids}
|
109
|
+
|
110
|
+
builder = StateGraph(State, context_schema=Context)
|
111
|
+
|
112
|
+
builder.add_node("agent", call_model)
|
113
|
+
builder.add_node("tools", tool_node)
|
114
|
+
|
115
|
+
builder.add_edge(START, "agent")
|
116
|
+
builder.add_conditional_edges("agent", should_continue)
|
117
|
+
builder.add_conditional_edges("tools", tool_router)
|
118
|
+
|
119
|
+
return builder
|
@@ -0,0 +1,5 @@
|
|
1
|
+
"""Default prompts used by the agent."""
|
2
|
+
|
3
|
+
SYSTEM_PROMPT = """You are a helpful AI assistant. When you lack tools for any task you should use the `retrieve_tools` function to unlock relevant tools. Whenever you need to ask the user for any information, or choose between multiple different applications, you can ask the user using the `ask_user` function.
|
4
|
+
|
5
|
+
System time: {system_time}"""
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from typing import Annotated
|
2
|
+
|
3
|
+
from langgraph.prebuilt.chat_agent_executor import AgentState
|
4
|
+
|
5
|
+
|
6
|
+
def _enqueue(left: list, right: list) -> list:
|
7
|
+
"""Treat left as a FIFO queue, append new items from right (preserve order),
|
8
|
+
keep items unique, and cap total size to 20 (drop oldest items)."""
|
9
|
+
max_size = 30
|
10
|
+
preferred_size = 20
|
11
|
+
if len(right) > preferred_size:
|
12
|
+
preferred_size = min(max_size, len(right))
|
13
|
+
queue = list(left or [])
|
14
|
+
|
15
|
+
for item in right[:preferred_size] or []:
|
16
|
+
if item in queue:
|
17
|
+
queue.remove(item)
|
18
|
+
queue.append(item)
|
19
|
+
|
20
|
+
if len(queue) > preferred_size:
|
21
|
+
queue = queue[-preferred_size:]
|
22
|
+
|
23
|
+
return queue
|
24
|
+
|
25
|
+
|
26
|
+
class State(AgentState):
|
27
|
+
selected_tool_ids: Annotated[list[str], _enqueue]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from universal_mcp.agentr.registry import AgentrRegistry
|
2
|
+
from universal_mcp.agents.autoagent import create_agent
|
3
|
+
from universal_mcp.tools import ToolManager
|
4
|
+
|
5
|
+
tool_registry = AgentrRegistry()
|
6
|
+
tool_manager = ToolManager()
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
apps = tool_registry.client.list_all_apps()
|
11
|
+
names = [app["name"] for app in apps]
|
12
|
+
|
13
|
+
instructions = """
|
14
|
+
You are a helpful assistant that can use tools to help the user. If a task requires multiple steps, you should perform separate different searches for different actions.
|
15
|
+
These are the list of applications you can use to help the user:
|
16
|
+
{names}
|
17
|
+
"""
|
18
|
+
graph = create_agent(tool_registry, tool_manager, instructions=instructions)
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from langchain_core.messages import BaseMessage
|
2
|
+
|
3
|
+
|
4
|
+
def get_message_text(msg: BaseMessage) -> str:
|
5
|
+
"""Get the text content of a message."""
|
6
|
+
content = msg.content
|
7
|
+
if isinstance(content, str):
|
8
|
+
return content
|
9
|
+
elif isinstance(content, dict):
|
10
|
+
return content.get("text", "")
|
11
|
+
else:
|
12
|
+
txts = [c if isinstance(c, str) else (c.get("text") or "") for c in content]
|
13
|
+
return "".join(txts).strip()
|
universal_mcp/agents/base.py
CHANGED
@@ -7,7 +7,7 @@ from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
7
7
|
from langgraph.checkpoint.memory import MemorySaver
|
8
8
|
from langgraph.types import Command
|
9
9
|
|
10
|
-
from .llm import
|
10
|
+
from .llm import load_chat_model
|
11
11
|
from .utils import RichCLI
|
12
12
|
|
13
13
|
|
@@ -18,7 +18,7 @@ class BaseAgent:
|
|
18
18
|
self.model = model
|
19
19
|
self.memory = memory or MemorySaver()
|
20
20
|
self._graph = None
|
21
|
-
self.llm =
|
21
|
+
self.llm = load_chat_model(model)
|
22
22
|
self.cli = RichCLI()
|
23
23
|
|
24
24
|
async def _build_graph(self):
|
@@ -47,6 +47,7 @@ class BaseAgent:
|
|
47
47
|
return await self._graph.ainvoke(
|
48
48
|
{"messages": [{"role": "user", "content": user_input}]},
|
49
49
|
config={"configurable": {"thread_id": thread_id}},
|
50
|
+
context={"system_prompt": self.instructions, "model": self.model},
|
50
51
|
)
|
51
52
|
|
52
53
|
async def run_interactive(self, thread_id: str = str(uuid4())):
|
@@ -1,10 +1,10 @@
|
|
1
1
|
from universal_mcp.agentr import Agentr
|
2
2
|
from universal_mcp.agents.codeact import create_codeact
|
3
3
|
from universal_mcp.agents.codeact.sandbox import eval_unsafe
|
4
|
-
from universal_mcp.agents.llm import
|
4
|
+
from universal_mcp.agents.llm import load_chat_model
|
5
5
|
from universal_mcp.tools.adapters import ToolFormat
|
6
6
|
|
7
|
-
model =
|
7
|
+
model = load_chat_model("gpt-4.1")
|
8
8
|
|
9
9
|
agentr = Agentr()
|
10
10
|
agentr.load_tools(["google-mail_send_email"])
|
universal_mcp/agents/hil.py
CHANGED
@@ -7,7 +7,7 @@ from langgraph.graph.message import add_messages
|
|
7
7
|
from langgraph.types import Interrupt, interrupt
|
8
8
|
|
9
9
|
from .base import BaseAgent
|
10
|
-
from .llm import
|
10
|
+
from .llm import load_chat_model
|
11
11
|
|
12
12
|
|
13
13
|
class State(TypedDict):
|
@@ -70,7 +70,7 @@ def handle_interrupt(interrupt: Interrupt) -> str | bool:
|
|
70
70
|
class HilAgent(BaseAgent):
|
71
71
|
def __init__(self, name: str, instructions: str, model: str):
|
72
72
|
super().__init__(name, instructions, model)
|
73
|
-
self.llm =
|
73
|
+
self.llm = load_chat_model(model)
|
74
74
|
self._graph = self._build_graph()
|
75
75
|
|
76
76
|
def chatbot(self, state: State):
|
universal_mcp/agents/llm.py
CHANGED
@@ -1,10 +1,28 @@
|
|
1
|
+
from langchain_anthropic import ChatAnthropic
|
2
|
+
from langchain_core.language_models import BaseChatModel
|
3
|
+
from langchain_google_vertexai.model_garden import ChatAnthropicVertex
|
1
4
|
from langchain_openai import AzureChatOpenAI
|
2
5
|
|
3
6
|
|
4
|
-
def
|
5
|
-
|
7
|
+
def load_chat_model(fully_specified_name: str, tags: list[str] | None = None) -> BaseChatModel:
|
8
|
+
"""Load a chat model from a fully specified name.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
fully_specified_name (str): String in the format 'provider/model'.
|
12
|
+
"""
|
13
|
+
provider, model = fully_specified_name.split("/", maxsplit=1)
|
14
|
+
if provider == "google_anthropic_vertex":
|
15
|
+
return ChatAnthropicVertex(model=model, temperature=0.2, location="asia-east1", tags=tags)
|
16
|
+
elif provider == "anthropic":
|
17
|
+
return ChatAnthropic(
|
18
|
+
model=model, temperature=1, thinking={"type": "enabled", "budget_tokens": 2048}, max_tokens=4096, tags=tags
|
19
|
+
) # pyright: ignore[reportCallIssue]
|
20
|
+
elif provider == "azure":
|
21
|
+
return AzureChatOpenAI(model=model, api_version="2024-12-01-preview", azure_deployment=model, tags=tags)
|
22
|
+
else:
|
23
|
+
raise ValueError(f"Unsupported provider: {provider}")
|
6
24
|
|
7
25
|
|
8
26
|
if __name__ == "__main__":
|
9
|
-
llm =
|
27
|
+
llm = load_chat_model("azure/gpt-4.1")
|
10
28
|
print(llm.invoke("Hello, world!"))
|
universal_mcp/agents/react.py
CHANGED
@@ -60,5 +60,7 @@ if __name__ == "__main__":
|
|
60
60
|
model="gpt-4o",
|
61
61
|
tools=ToolConfig(agentrServers={"google-mail": {"tools": ["send_email"]}}),
|
62
62
|
)
|
63
|
-
result = asyncio.run(
|
63
|
+
result = asyncio.run(
|
64
|
+
agent.run(user_input="Send an email with the subject 'testing react agent' to manoj@agentr.dev")
|
65
|
+
)
|
64
66
|
print(result["messages"][-1].content)
|
universal_mcp/agents/simple.py
CHANGED
@@ -5,8 +5,8 @@ from langgraph.graph import END, START, StateGraph
|
|
5
5
|
from langgraph.graph.message import add_messages
|
6
6
|
from typing_extensions import TypedDict
|
7
7
|
|
8
|
-
from .base import BaseAgent
|
9
|
-
from .llm import
|
8
|
+
from universal_mcp.agents.base import BaseAgent
|
9
|
+
from universal_mcp.agents.llm import load_chat_model
|
10
10
|
|
11
11
|
|
12
12
|
class State(TypedDict):
|
@@ -16,7 +16,7 @@ class State(TypedDict):
|
|
16
16
|
class SimpleAgent(BaseAgent):
|
17
17
|
def __init__(self, name: str, instructions: str, model: str):
|
18
18
|
super().__init__(name, instructions, model)
|
19
|
-
self.llm =
|
19
|
+
self.llm = load_chat_model(model)
|
20
20
|
self._graph = self._build_graph()
|
21
21
|
|
22
22
|
def _build_graph(self):
|
universal_mcp/tools/registry.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
+
from universal_mcp.tools.manager import ToolManager
|
5
|
+
|
4
6
|
|
5
7
|
class ToolRegistry(ABC):
|
6
8
|
"""Abstract base class for platform-specific functionality.
|
@@ -11,7 +13,7 @@ class ToolRegistry(ABC):
|
|
11
13
|
"""
|
12
14
|
|
13
15
|
@abstractmethod
|
14
|
-
|
16
|
+
def list_apps(self) -> list[dict[str, Any]]:
|
15
17
|
"""Get list of available apps from the platform.
|
16
18
|
|
17
19
|
Returns:
|
@@ -20,7 +22,7 @@ class ToolRegistry(ABC):
|
|
20
22
|
pass
|
21
23
|
|
22
24
|
@abstractmethod
|
23
|
-
|
25
|
+
def get_app_details(self, app_id: str) -> dict[str, Any]:
|
24
26
|
"""Get detailed information about a specific app.
|
25
27
|
|
26
28
|
Args:
|
@@ -32,10 +34,18 @@ class ToolRegistry(ABC):
|
|
32
34
|
pass
|
33
35
|
|
34
36
|
@abstractmethod
|
35
|
-
|
37
|
+
def load_tools(self, tools: list[str], tool_manager: ToolManager) -> None:
|
36
38
|
"""Load tools from the platform and register them as tools.
|
37
39
|
|
38
40
|
Args:
|
39
41
|
tools: The list of tools to load
|
40
42
|
"""
|
41
43
|
pass
|
44
|
+
|
45
|
+
@abstractmethod
|
46
|
+
def search_tools(
|
47
|
+
self,
|
48
|
+
query: str,
|
49
|
+
) -> list[str]:
|
50
|
+
"""Retrieve a tool to use, given a search query."""
|
51
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: universal-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.24rc7
|
4
4
|
Summary: Universal MCP acts as a middle ware for your API applications. It can store your credentials, authorize, enable disable apps on the fly and much more.
|
5
5
|
Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
|
6
6
|
License: MIT
|
@@ -9,9 +9,12 @@ Requires-Python: >=3.11
|
|
9
9
|
Requires-Dist: black>=25.1.0
|
10
10
|
Requires-Dist: cookiecutter>=2.6.0
|
11
11
|
Requires-Dist: gql>=4.0.0
|
12
|
+
Requires-Dist: httpx-aiohttp>=0.1.8
|
12
13
|
Requires-Dist: jinja2>=3.1.3
|
13
14
|
Requires-Dist: jsonref>=1.1.0
|
14
15
|
Requires-Dist: keyring>=25.6.0
|
16
|
+
Requires-Dist: langchain-anthropic>=0.3.19
|
17
|
+
Requires-Dist: langchain-google-vertexai>=2.0.28
|
15
18
|
Requires-Dist: langchain-mcp-adapters>=0.1.9
|
16
19
|
Requires-Dist: langchain-openai>=0.3.27
|
17
20
|
Requires-Dist: langgraph-cli[inmem]>=0.3.4
|
@@ -6,26 +6,34 @@ universal_mcp/exceptions.py,sha256=Uen8UFgLHGlSwXgRUyF-nhqTwdiBuL3okgBVRV2AgtA,2
|
|
6
6
|
universal_mcp/logger.py,sha256=VmH_83efpErLEDTJqz55Dp0dioTXfGvMBLZUx5smOLc,2116
|
7
7
|
universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
universal_mcp/types.py,sha256=jeUEkUnwdGWo3T_qSRSF83u0fYpuydaWzdKlCYBlCQA,770
|
9
|
-
universal_mcp/agentr/README.md,sha256=
|
9
|
+
universal_mcp/agentr/README.md,sha256=t15pVgkCwZM5wzgLgrf0Zv6hVL7dPmKXvAeTf8CiXPQ,6641
|
10
10
|
universal_mcp/agentr/__init__.py,sha256=ogOhH_OJwkoUZu_2nQJc7-vEGmYQxEjOE511-6ubrX0,217
|
11
|
-
universal_mcp/agentr/agentr.py,sha256=
|
12
|
-
universal_mcp/agentr/client.py,sha256=
|
11
|
+
universal_mcp/agentr/agentr.py,sha256=kTUiBpDl4ODuXit9VE_ZXW28IKpCqEnJDNtMXi2B3Pc,1245
|
12
|
+
universal_mcp/agentr/client.py,sha256=B4pjIpfD5nDCZ9qldKIqlBV8DISrkTRrajCIQK4r4Hs,6232
|
13
13
|
universal_mcp/agentr/integration.py,sha256=V5GjqocqS02tRoI8MeV9PL6m-BzejwBzgJhOHo4MxAE,4212
|
14
|
-
universal_mcp/agentr/registry.py,sha256=
|
14
|
+
universal_mcp/agentr/registry.py,sha256=BOxy9iuJagKLmH9komaabwXvUglrsWbpRX8WY9xJ7lI,3115
|
15
15
|
universal_mcp/agentr/server.py,sha256=bIPmHMiKKwnUYnxmfZVRh1thcn7Rytm_-bNiXTfANzc,2098
|
16
|
-
universal_mcp/agents/__init__.py,sha256=
|
17
|
-
universal_mcp/agents/auto.py,sha256=
|
18
|
-
universal_mcp/agents/base.py,sha256=
|
16
|
+
universal_mcp/agents/__init__.py,sha256=ZkdQ71fn838LvYdyln6fL1mUMUUCZRZMyqos4aW2_I4,265
|
17
|
+
universal_mcp/agents/auto.py,sha256=UUx3p9riLww2OwRg0pg10mWzWdDNydBrKJ-UdwzAQSk,25411
|
18
|
+
universal_mcp/agents/base.py,sha256=uRb-flv_pdKfDJnHID1c-loYt-EvlAgFmB1_wJQNhUs,4152
|
19
19
|
universal_mcp/agents/cli.py,sha256=7GdRBpu9rhZPiC2vaNQXWI7K-0yCnvdlmE0IFpvy2Gk,539
|
20
|
-
universal_mcp/agents/hil.py,sha256=
|
21
|
-
universal_mcp/agents/llm.py,sha256=
|
22
|
-
universal_mcp/agents/react.py,sha256=
|
23
|
-
universal_mcp/agents/simple.py,sha256=
|
20
|
+
universal_mcp/agents/hil.py,sha256=6xi0hhK5g-rhCrAMcGbjcKMReLWPC8rnFZMBOF3N_cY,3687
|
21
|
+
universal_mcp/agents/llm.py,sha256=0HUI2Srh3RWtGyrjJCKqsroEgc1Rtkta3T8I1axl1mg,1232
|
22
|
+
universal_mcp/agents/react.py,sha256=kAyTS68xzBLWRNgjJrLSP85o1ligz_ziatdlMZAavnA,2385
|
23
|
+
universal_mcp/agents/simple.py,sha256=CXmwJq7jvxAoDJifNK7jKJTMKG4Pvre75x-k2CE-ZEM,1202
|
24
24
|
universal_mcp/agents/tools.py,sha256=7Vdw0VZYxXVAzAYSpRKWHzVl9Ll6NOnVRlc4cTXguUQ,1335
|
25
25
|
universal_mcp/agents/utils.py,sha256=7kwFpD0Rv6JqHG-LlNCVwSu_xRX-N119mUmiBroHJL4,4109
|
26
|
+
universal_mcp/agents/autoagent/__init__.py,sha256=Vfm8brM9TNXCjKbVXV-CAPg_BVnYHOn6RVmkS0EaNV0,1072
|
27
|
+
universal_mcp/agents/autoagent/__main__.py,sha256=FUSETuCDMpp7VSis0UFDnpI32HmQuJYaAXaOX5fQl-4,622
|
28
|
+
universal_mcp/agents/autoagent/context.py,sha256=1ic3sIL14XZeiMjpkwysLImRTQFKXRFSx7rvgVh4plY,802
|
29
|
+
universal_mcp/agents/autoagent/graph.py,sha256=f_TPcMk0t4JgM1gYs4sLFIeCrTGAzecc2rN0MPsmxvs,5116
|
30
|
+
universal_mcp/agents/autoagent/prompts.py,sha256=DwLHwvsISuNrxeua0tKxTQbkU8u9gljCpk3P18VGk4w,386
|
31
|
+
universal_mcp/agents/autoagent/state.py,sha256=TQeGZD99okclkoCh5oz-VYIlEsC9yLQyDpnBnm7QCN8,759
|
32
|
+
universal_mcp/agents/autoagent/studio.py,sha256=FWmZTAH54euF0ePG6xCBNwklBjdmjZ3jAOBoTrwNcqs,656
|
33
|
+
universal_mcp/agents/autoagent/utils.py,sha256=AFq-8scw_WlSZxDnTzxSNrOSiGYsIlqkqtQLDWf_rMU,431
|
26
34
|
universal_mcp/agents/codeact/__init__.py,sha256=5D_I3lI_3tWjZERRoFav_bPe9UDaJ53pDzZYtyixg3E,10097
|
27
35
|
universal_mcp/agents/codeact/sandbox.py,sha256=lGRzhuXTHCB1qauuOI3bH1-fPTsyL6Lf9EmMIz4C2xQ,1039
|
28
|
-
universal_mcp/agents/codeact/test.py,sha256=
|
36
|
+
universal_mcp/agents/codeact/test.py,sha256=AI3qWszpM46hF4wzuQm6A8g_UkhGmcg9KhHtk9u14ro,497
|
29
37
|
universal_mcp/agents/codeact/utils.py,sha256=VuMvLTxBBh3pgaJk8RWj5AK8XZFF-1gnZJ6jFLeM_CI,1690
|
30
38
|
universal_mcp/applications/__init__.py,sha256=HrCnGdAT7w4puw2_VulBfjOLku9D5DuMaOwAuQzu6nI,2067
|
31
39
|
universal_mcp/applications/application.py,sha256=pGF9Rb2D6qzlaSwlcfZ-dNqPtsLkQTqL3jpsRuJ6-qE,23835
|
@@ -44,7 +52,7 @@ universal_mcp/tools/adapters.py,sha256=YJ2oqgc8JgmtsdRRtvO-PO0Q0bKqTJ4Y3J6yxlESo
|
|
44
52
|
universal_mcp/tools/docstring_parser.py,sha256=efEOE-ME7G5Jbbzpn7pN2xNuyu2M5zfZ1Tqu1lRB0Gk,8392
|
45
53
|
universal_mcp/tools/func_metadata.py,sha256=F4jd--hoZWKPBbZihVtluYKUsIdXdq4a0VWRgMl5k-Q,10838
|
46
54
|
universal_mcp/tools/manager.py,sha256=24Rkn5Uvv_AuYAtjeMq986bJ7uzTaGE1290uB9eDtRE,10435
|
47
|
-
universal_mcp/tools/registry.py,sha256=
|
55
|
+
universal_mcp/tools/registry.py,sha256=EA-xJ6GCYGajUVCrRmPIpr9Xekwxnqhmso8ztfsTeE8,1401
|
48
56
|
universal_mcp/tools/tools.py,sha256=Lk-wUO3rfhwdxaRANtC7lQr5fXi7nclf0oHzxNAb79Q,4927
|
49
57
|
universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
|
50
58
|
universal_mcp/utils/common.py,sha256=3aJK3AnBkmYf-dbsFLaEu_dGuXQ0Qi2HuqYTueLVhXQ,10968
|
@@ -65,8 +73,8 @@ universal_mcp/utils/openapi/readme.py,sha256=R2Jp7DUXYNsXPDV6eFTkLiy7MXbSULUj1vH
|
|
65
73
|
universal_mcp/utils/openapi/test_generator.py,sha256=h44gQXEXmrw4pD3-XNHKB7T9c2lDomqrJxVO6oszCqM,12186
|
66
74
|
universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
|
67
75
|
universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
|
68
|
-
universal_mcp-0.1.
|
69
|
-
universal_mcp-0.1.
|
70
|
-
universal_mcp-0.1.
|
71
|
-
universal_mcp-0.1.
|
72
|
-
universal_mcp-0.1.
|
76
|
+
universal_mcp-0.1.24rc7.dist-info/METADATA,sha256=agZ4cb79U2UCRPE6V_NXT5pL-_gAxB2Gz04jsc9rVCs,3143
|
77
|
+
universal_mcp-0.1.24rc7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
78
|
+
universal_mcp-0.1.24rc7.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
|
79
|
+
universal_mcp-0.1.24rc7.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
|
80
|
+
universal_mcp-0.1.24rc7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|